Linking inside of pattern rules - makefile

I have a generic build system that I import as a Git submodule into my projects, each of which has a wrapper makefile that includes the build system's makefile. Suppose I have something like the following in the build system's makefile:
$(BIN_DIR): | $(BIN_DIR)
$(CC) $(LDFLAGS) -o $# $<
And prerequisites like the following in the projects' makefiles:
$(BIN_DIR)/foo: $(FOO_OBJ_FILES:%=$(OBJ_DIR)/%)
While this technique works well for libraries, it obviously doesn't work well for executables, because each needs to be linked with a different set of libraries. So my question is how to achieve that without relying of GNU Make-specific features? With GNU Make, I could do something like this:
$(BIN_DIR): | $(BIN_DIR)
$(CC) $(LDFLAGS) -o $# $< $((nodir $(basename $#))_LINK:%=-l %)
foo_LINK = mylib1 mylib2
$(BIN_DIR)/foo: $(FOO_OBJ_FILES:%=$(OBJ_DIR)/%)
One portable solution that I've thought of but would rather not use because it's a huge hack, is to have a .link file associated with each executable, containing one line like this: -l mylib1 -lmylib2 and then do something like this:
$(BIN_DIR): | $(BIN_DIR)
$(CC) $(LDFLAGS) -o $# $< `cat $#.link`
So, all these things aside, I need to somehow magically pass the correct set of libraries to the pattern rule or to have a variable expand to the correct set of libraries when the pattern rule gets executed.
Any ideas? Thanks.
PS: I know this seems like more trouble than it's worth but I've simplified things for the purpose of this question. In reality, it saves me from dealing with some pepetition, not just saving me one line.

Okay, I've come up with an answer:
$(BIN_DIR): | $(BIN_DIR)
$(CC) $(LDFLAGS) -o $# $< $($(F)_LINK:%=-l %)
foo_LINK = mylib1 mylib2
Unfortunately, while there is a proposal for adding nested/computed variables into POSIX make for Issue 8 of the specification, it seems to have been in limbo for the past 10 years or so.

Related

Makefile Syntax unclear

This is my first Makefile, and I am can't figure out some of the syntax used. The questions are marked below.
$(BUILD_DIR)/%.o: %.c $(BUILD_DIR)
$(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $#
What is the usage of "$(BUILD_DIR)" in the dependency?
What is the meaning of "$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $#" in the role?
As with most computer languages the syntax of make cannot be clear if you don't know it. If you are using GNU make the GNU make manual is your friend. In the following explanations I will assume that BUILD_DIR = build and that one of the source files is bar/foo.c.
$(BUILD_DIR) in the list of prerequisites (dependencies) tells make that the build directory (in which object files are supposed to go) must exist before the recipe is executed; logical. There must be another rule somewhere to create the directory if it does not exist yet. Something like:
$(BUILD_DIR):
mkdir -p $#
But unless you forgot to copy an important character, this dependency is terribly sub-optimal. As the last modification time of a directory changes each time its content changes (files or sub-directories added or removed), it will force the re-compilation of all source files every time the directory changes, which is not what you want. A better dependency would be order-only:
$(BUILD_DIR)/%.o: %.c | $(BUILD_DIR)
that tells make to consider only the existence of $(BUILD_DIR), not its last modification time, when deciding to re-build or not.
$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $# is just a combination of make automatic variables and functions.
$< and $# expand as the first prerequisite (bar/foo.c) and the target (build/bar/foo.o) respectively.
$(<:.c=.lst) replaces .c by .lst in $<: bar/foo.lst.
$(notdir $(<:.c=.lst)) removes the directory part: foo.lst.
All in all, for a bar/foo.c source file, and with BUILD_DIR = build, the pattern rule would be equivalent to:
build/bar/foo.o: bar/foo.c | build
$(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=build/foo.lst bar/foo.c -o build/bar/foo.o
Note that there are two different situations to consider:
All your source files are in the same directory as the Makefile (no bar/foo.c, just foo.c). Then you can simplify your recipe:
$(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(<:.c=.lst) $< -o $#
because the $(notdir...) is useless.
Your source files can be in sub-directories (bar/foo.c). Then you need the $(notdir...) in your recipe. But be warned that if you have two source files with the same base name (bar/foo.c and baz/foo.c) you will have a name conflict for $(BUILD_DIR)/foo.lst and your Makefile will not work as expected. Moreover, the order-only prerequisite of the rule should be equivalent to build/bar (or build/baz), not just build. And there should be a rule to create it if needed. If it is your case I suggest to change your pattern rule for:
$(BUILD_DIR)/%.o: %.c
mkdir -p $(dir $#)
$(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $#
There are other solutions (secondary expansion...) but there are a bit too complicated for this already too long answer.

Variable dependent target in makefile

I have a small project for testing and I want to link different implementations of a solution to test.
So, I create makefile that looks like this
CC=g++
FLAGS=-Wall -O2
CFLAGS=-c $(FLAGS)
I_PATH=implementation1.cpp
all: instance
instance: instance.cpp .implementation.o
$(CC) $(FLAGS) instance.cpp .implementation.o -o $#
.implementation.o: $(I_PATH)
$(CC) $(CFLAGS) $(I_PATH) -o $#
clean:
-rm .implementation*
-rm instance
Here, I_PATH is a path to solution implementation. And I want to test different solution passing different implementation via command line arguments: make I_PATH=implementation2.cpp.
But, because of all my implementations compiled to the same object file .implementation.o, make can't understand that something changes and doesn't rebuild project.
Of course, I can call make clean before run make for a specific implementation. But this increase build time (I can run tests for one implementation many times) and not very comfortable.
I can fix this makefile to something like this:
CC=g++
FLAGS=-Wall -O2
CFLAGS=-c $(FLAGS)
I_PATH=implementation1.cpp
C_PATH := $(shell echo -n $(I_PATH) | md5sum | awk '{print ".implementation_" substr($$1, 0, 10) ".o";}')
all: force instance
force_relink:
touch -c $(C_PATH)
instance: instance.cpp $(C_PATH)
$(CC) $(FLAGS) instance.cpp $(C_PATH) -o $#
$(C_PATH): $(I_PATH)
$(CC) $(CFLAGS) $< -o $#
clean:
-rm .implementation*
-rm instance
Here I create I_PATH dependent object file(take hash of path to implementation) and in addition force re-linking instance.cpp with object file every time make runs.
But maybe there is some mechanism in make to fix this behavior? Or I can achieve the same goal with different approaches?
Wouldn't it make more sense to give each compiled .o file a distinct name, and simply link to the compiled .o file you want?
instance: instance.cpp $(IMPL_O)
$(CC) $(FLAGS) $^ -o $# # propably no need to override default rule
Use make IMPL_O=implementation2.o to generate the example in your question.
This way, the name of each file truly reveals its identity, and you don't have to keep track of anything explicitly.
(Obviously, you could refactor the .o extension into the Makefile itself so you can just say IMPL=implementation2 or whatever.)

Recovering from an abruptly ended make

When I compile large programs (like gcc or clang for example) there is a chance that my computer will overheat, and be forced to shut down.
I would like to resume the make process from where I left off. The problem seems to be that there are half completed/written .o files that are floating around that cause the rest of the built to break (this is especially bad when I specify -j 8)
Is there an easy way to get around this problem (whithout doing a make clean or make distclean or the like)?
Using GNU Make 3.81
Along the same lines as Beta's comment, but more reliable and less confusing IMO, would be to change your compile rule so that you compile to a temporary file, then at the end rename it to the real file. So where you might have something like:
%.o : %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -o $# -c $<
instead you would use something like:
%.o : %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -o $#.tmp -c $< \
&& mv -f $#.tmp $#
You may or may not want to add a "rm -f $#" as well.
As to whether you can make this change "programmatically" or not, it all dependes on your makefile and how it's structured. If it's a well-formed makefile then you can just make these changes in a few places in a few implicit rules, as above.

separate builds in separate directories

I'm sure this is a totally normal thing to do, but I can't figure out how to get make to do this.
I have a compiler that generates make dependencies of the usual form:
M/A.o : M/A.hs
M/B.o : M/A.o
So I write a rule to compile %.hs into %.o, add a rule to link the binary, include the dependencies file, and all is well. But I want to have several binary targets with different flags. E.g. I want build/test built with -DTESTING and build/profile built with -prof. So I need to keep the .o files in a separate tree, where they will be compiled with special flags.
The straightforward way I can think of would be to have dependencies that look something like this:
build/test/M/A.o : M/A.hs
build/test/M/B.o : build/test/M/A.o
build/profile/M/A.o : M/A.hs
... etc.
And then rules so that %.hs to build/test/%.o compiles with -DTESTING, etc. I think this would work, but it's clumsy, means preprocessing the deps file to add all that build/whatever/ prefix stuff, and would multiply its size by however many kinds of builds.
VPATH appears to be designed for this sort of thing and my idea was that I could set the VPATH and compiler flags depending on the target, and it almost works, but:
%.o: %.hs
#mkdir -p build/M
cp $< build/$#
VPATH = build
main: M/A.o M/B.o
cat $^ >$#
M/A.o : M/A.hs
M/B.o : M/B.hs
The first time the main target wants to run 'cat M/A.o M/B.o >main' which seems contrary to the gnu make documentation that says $^ should include the include the VPATH directory in which the dependency was found. Curiously, if I remove 'main' and make again, this time it uses the correct path. This is GNU make, 3.81.
What's going on here? Is there a better way to build with different flags? VPATH seems like a clumsy tool, surely there is a better way?
Make is working correctly. It tries cat M/A.o M/B.o >main the first time because it can't find the prerequisites it needs, but it knows a rule for M/A.o' andM/B.o(<em>not</em>build/M/A.o' and build/M/B.o) and expects that that is what the rule will produce. If you remove main and try again, it will find build/M/A.o' andbuild/M/B.o` via VPATH.
Let's modify this makefile in stages. First we change the VPATH so that it can find the .hs files (Make is good at using things there to build things here, not vise-versa, and that's what VPATH is good for), and change the rules slightly:
build/%.o: %.hs
cp $< $#
VPATH = M
main: build/A.o build/B.o
cat $^ > $#
Now for the different object directories.
build/test/%.o build/project/%.o: %.hs
cp $< $#
VPATH = M
test: build/test/A.o build/test/B.o
cat $^ > $#
project: build/project/A.o build/project/B.o
cat $^ > $#
Then we simplify those last two rules, so that it's easy to add more object files and binary targets:
OBJECTS = A.o B.o
test: $(addprefix build/test/,$(OBJECTS))
project: $(addprefix build/project/,$(OBJECTS))
test project:
cat $^ > $#
Now for the different compiler flags:
build/test/%.o: FLAGS += test_flags
build/project/%.o: FLAGS += proj_flags
build/test/%.o build/project/%.o: %.hs
#echo building $# from $^ using flags $(FLAGS)
cp $< $#
Finally the dependencies. This is a little tricky. Suppose you want the dependency B.o : A.hs to apply to however many object you have. This is one approach:
OBJECT_PATHS = build/test/ build/project/
# The following is from the included file generated by the compiler
$(addsuffix B.o,$(OBJECT_PATHS)) : A.hs
To generate lines like that, I'd pipe the raw lines (e.g. B.o: A.hs) through sed 's/\(.*\):\(.*\)/\1:\2/', and note that if you want to put this in a makefile command, don't forget to double the $ signs to preserve them for the shell.
I know that's a lot to absorb. Take it one step at a time and let us know how it works out.
If you haven't solved your problem by now or are experiencing further problems, best give the autotools (automake and autoconf) a chance. They'll quickly build you a Makefile that supports more configurable and flexible out-of-tree builds.

How to add different rules for specific files?

I have a certain problem with my Makefile.
With this command, I can compile all my *.c files to *.o which works well:
$(OBJ) : %.o : %.c $(LDSCRIPT) Makefile
$(CC) $(ARM9_INCLUDES) -c $(CFLAGS) $< -o $#
But now I'm wondering, what if I want to run -O3 optimization on just ONE particular file, and have -O0 on the rest?
Is there any command to add a different rule for a specific file?
What I'm doing right now is compiling each C file with its own rules, which is very annoying because I have around 30 files which makes the Makefile huge, and every time I change something in one file it compiles EVERYTHING again.
particular_file.o : CFLAGS+=-O3
(assuming GNU make) see target-specific variable values in GNU Make manual
(and the immediately following pattern-specific variable values, maybe).
Also note, that commands are used from the most specific rule for given file, so you can have in case target-specific variable value is not sufficient:
particular_file.o : particular_file.c
completely_special_compiler -o $# $<
%.o : %.c
$(CC) $(CFLAGS) -o $# $<
It's possible to make the solution a bit more extensible.
Suppose you need to compile one set of files in one way and the other set of files in another way, rather than having only one exception, and you could identify patterns in those two sets of files, e.g. one set starts with "a", and the other set starts with "b", you can do something like this:
a%.o : a%.c
completely_special_compiler -o $# $<
b%.o : b%.c
$(CC) $(CFLAGS) -o $# $<
For more explanation, see Static Patterns.

Resources