Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

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.

Comments on make: How to compile all files in a directory.

Parent

make: How to compile all files in a directory.

+5
−0

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 $@ $<
History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.
Why should this post be closed?

1 comment thread

Simple oversight? (3 comments)
Post
+1
−0

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.

History
Why does this post require attention from curators or moderators?
You might want to add some details to your flag.

1 comment thread

It worked. (1 comment)
It worked.
Vanity Slug ❤️‭ wrote about 1 month ago

"Each rule in the Makefile represents a single target." - this was very helpful to my understanding. I have seen people in tutorials use % like a wildcard and that sent me on a wild goose chase in the wrong direction. Your original code was giving me "make: Nothing to be done for 'all'." output because it was looking for .c files in current directory. I suggested an edit that worked for me. Thank you!