Welcome to Software Development on Codidact!
Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.
make: How to compile all files in a directory.
I am learning how to write makefile to compile a c program. I have a directory structure like this:
.
├── include
│ └── library.h
├── lib
│ └── library.c
├── makefile
└── obj
My makefile has this content:
DIR_INC=include
DIR_LIB=lib
DIR_OBJ=obj
$(DIR_OBJ)/library.o: $(DIR_LIB)/library.c $(DIR_INC)/library.h
gcc -c -o $@ $<
And it works: library.o
gets generated inside obj
folder.
Now I want to make it generic so that ALL files inside lib
folder get compiled to object files into obj
folder, so I replace library
with %
(because based on my limited/wrong understanding that's how it should work), like so:
DIR_INC=include
DIR_LIB=lib
DIR_OBJ=obj
$(DIR_OBJ)/%.o: $(DIR_LIB)/%.c $(DIR_INC)/%.h
gcc -c -o $@ $<
but I get this error:
make: *** No targets. Stop.
I find this Q/A: https://stackoverflow.com/questions/33530341/no-targets-stop. Based on that I add all:
target, so my makefile looks like this now:
DIR_INC=include
DIR_LIB=lib
DIR_OBJ=obj
all: $(DIR_OBJ)/%.o
$(DIR_OBJ)/%.o: $(DIR_LIB)/%.c $(DIR_INC)/%.h
gcc -c -o $@ $<
And I get this error:
make: *** No rule to make target 'obj/%.o', needed by 'all'. Stop.
Why am I getting these errors and how do I fix them? My goal is to compile all *.c
files inside lib
directory into *.o
object files into obj
directory.
Update 1 (after receiving an answer from mr Tsjolder )
I also tried making use of wildcard but was not successful with it. (I did not mention it earlier because I thought this was not the way to go). For the record, here is the latest makefile I had:
DIR_INC=include
DIR_LIB=lib
DIR_OBJ=obj
FILES_LIB=$(wildcard $(DIR_LIB)/*.c)
FILES_OBJ=$(patsubst $(DIR_LIB)/%.c,$(DIR_OBJ)/%.o,$(FILES_LIB))
all: $(FILES_OBJ)
$(DIR_OBJ)/%.o: $(FILES_LIB)/%.c $(DIR_INC)/%.h
gcc -c -o $@ $<
1 answer
The following users marked this post as Works for me:
User | Comment | Date |
---|---|---|
Vanity Slug ❤️ | (no comment) | Oct 11, 2024 at 11:52 |
Each rule in the Makefile represents a single target. This means that your first makefile has a single target: obj/library.o
. I guess that if there is only one target, make
assumes that this is the target you want to build and does not require targets to be provided by the command line. As a result, your Makefile works "as expected".
In the second Makefile your rule has a wildcard and thus represents infinitely many targets. Now make
is unable to guess what you actually want to build, hence the error. However, you can still use this makefile by providing the targets you want to build on the command line: make obj/library.o
should provide the exact same results as your initial attempt. If you are only interested in re-building (i.e. all files already exist), you could even use make obj/*.o
to re-build all files with this Makefile.
Your third Makefile introduces the standard target all
, which is intended to solve the problem with your second makefile: tell make
which targets to build when no command line arguments are given. The targets provided here are interpreted as literal strings. As a result, the makefile will look for the literal target %.o
, which does not exist and fail the build.
Now, if you would look closer at the proposed solution in Stackoverflow, you would notice that the proposed answer does not use any wildcards, but specifies all targets that need to be build. It can be helpful to be explicit about which targets you wish to build (as proposed in the stackoverflow answer), however it is often too convenient to simply use wildcards.
Luckily it is possible to use wildcards, but not in the way you might have hoped. Instead of directly specifying a wildcard, you have to create a list of all targets that you wish to build. Because these targets typically won't exist the first time you build the project, you have to somehow generate this list. One way to do that, is by following the documentation, which would lead to the following Makefile:
DIR_INC=include
DIR_LIB=lib
DIR_OBJ=obj
SOURCES := $(wildcard $(DIR_LIB)/*.c)
OBJECTS := $(patsubst $(DIR_LIB)/%.c,$(DIR_OBJ)/%.o,$(SOURCES))
all: $(OBJECTS)
$(DIR_OBJ)/%.o: $(DIR_LIB)/%.c $(DIR_INC)/%.h
gcc -c -o $@ $<
I hope that clears things up for you.
1 comment thread