Defer target until variable set - makefile

I have a project where you can build for multiple platforms, architectures, etc. I need to be able to set various variables based on the target before compiling any source files. But what's happening is the non-platform specific files are getting compiled immediately and then the platform specific ones didn't get compiled at all.
Below calling "make foobar_mac", foo.o gets created but bar.mac.o does not and therefore during the final link bar.mac.o is missing.
OBJS = foo.c bar.$(PLATFORM).c
%.o : %.c
$(COMPILE.c) $(OUTPUT_OPTION) $<
foobar_mac: PLATFORM := mac
foobar_mac: $(OBJS)
foobar_win: PLATFORM := win
foobar_win: $(OBJS)

There are various ways to get what you want. One way is to use secondary expansion to delay the expansion until after the target-specific variable is in scope.
Like this:
.SECONDEXPANSION:
OBJS = foo.c bar.$$(PLATFORM).c
%.o : %.c
$(COMPILE.c) $(OUTPUT_OPTION) $<
foobar_mac: PLATFORM := mac
foobar_mac: $(OBJS)
foobar_win: PLATFORM := win
foobar_win: $(OBJS)
Note how we added .SECONDEXPANSION, then we escaped the $(PLATFORM) reference in OBJS as $$(PLATFORM) so it would not be expanded when GNU make parses the makefile.
There are various other ways to do this as well.

Related

How can I configure my makefile for debug and release builds to more complex projects with multiple executables?

In the post How can I configure my makefile for debug and release builds?, I find the answer https://stackoverflow.com/a/20830354/5922947 perfect!
But it seems work with only one final target.
I've tried unsuccessfully to extend this makefile to my projects where there are several executables as targets.
What changes should I make to this example so that I can have multiple final targets and with
make
or
make all
I can produce all targets (in debug or release modes), or
make test
for a specific target?
EDIT:
One of the attempts I made but which did not produce any results, but serves to better illustrate what is intended:
(...)
OBJS = $(SRCS:.c=.o)
EXE=$($(TARGET)_EXE)
sample1_EXE=sample1
sample2_EXE=sample2
sample1: TARGET=sample1
sample1: debug
sample2: TARGET=sample2
sample2: debug
all: prep debug sample1 sample2
The problem has two parts: specifying the mode, and coordinating the build.
(This will be tricky, so we will take it in stages.)
First the mode. Setting the value of mode in the command, and using debug as the default, is easy. We put this in the makefile:
mode = debug
And a command like make sailfish mode=release will override it.
Now to use the mode:
mode = debug
ifeq ($(mode),debug)
BUILDDIR := debug
CFLAGS += -g -O0 -DDEBUG
else
ifeq ($(mode),release)
BUILDDIR := release
CFLAGS += -O3 -DNDEBUG
else
$(error unknown mode: $(mode))
endif
endif
Note that I added the error statement to catch mistakes like make mode=test and make mode=releas. Such errors are much easier to catch and correct if they cause Make to abort on the spot. (Also note that the leading whitespace is not necessary, it has no effect on execution, but it makes the makefile easier to read.)
Now for the build. Suppose we execute make sailfish, so that the mode is debug. First notice that although we give sailfish as the target, we are not actually building sailfish, we are building debug/sailfish. This is important, so we make sailfish a PHONY target that requires the file we actually want:
.PHONY: sailfish
sailfish: $(BUILDDIR)/sailfish
The relevant objects are sailfish.o and seaThing.o. (You must compose this list yourself, it is almost impossible for Make to deduce it.) We could put the objects in the makefile as a distinct variable:
sailfish_OBJS := sailfish.o seaThing.o
but it will make things simpler later if we make this a target-specific variable:
$(BUILDDIR)/sailfish: $(addprefix $(BUILDDIR)/,sailfish.o seaThing.o)
$(CC) $(CFLAGS) -o $# $^
We still need a rule to build the object files, and we notice that the two pattern rules in the linked answer collapse into one:
$(BUILDDIR)/%.o: %.c
$(CC) -c $(CFLAGS) -o $# $<
This is enough to build debug/sailfish and release/sailfish. To add the executable catamaran, we add:
.PHONY: catamaran
catamaran: $(BUILDDIR)/catamaran
$(BUILDDIR)/catamaran: $(addprefix $(BUILDDIR)/,catamaran.o boatThing.o seaThing.o)
$(CC) $(CFLAGS) -o $# $^
But we notice some redundancy here, so we combine the PHONY declarations:
.PHONY: sailfish catamaran
Then we notice that if we put the names of the executables in a variable:
EXECUTABLES := sailfish catamaran
we can use it in the PHONY declaration:
.PHONY: $(EXECUTABLES)
and we can also combine the two PHONY rules into a static pattern rule:
$(EXECUTABLES): %: $(BUILDDIR)/%
and we can separate the prerequisite lines from the recipes and use one recipe for both:
$(BUILDDIR)/sailfish: $(addprefix $(BUILDDIR)/,sailfish.o seaThing.o)
$(BUILDDIR)/catamaran: $(addprefix $(BUILDDIR)/,catamaran.o boatThing.o seaThing.o)
$(addprefix $(BUILDDIR)/,$(EXECUTABLES)):
$(CC) $(CFLAGS) -o $# $^
Now, to add another executable like seagull, we must only add seagull to the EXECUTABLES := ... line, and write another line:
$(BUILDDIR)/seagull: $(addprefix $(BUILDDIR)/,seagull.o birdThing.o seaThing.o)
A few more refinements are possible, but this should be enough for now.

gmake wildcard function not generating target

I'm using gmake on Windows with MinGW. This is a snippet of the makefile:
SRCS = ..\path\to\srcs\*.c*
SRCS := $(wildcard $(SRCS))
OBJS := $(addsuffix .o,$(basename $(SRCS)))
$(OBJS) : $(SRCS)
$(CC) $(FLGAS) $(INCLUDES) "$<"
I'm getting the following:
gmake: *** No targets. Stop.
If I define SRCS, for instance, as ..\path\to\srcs\a.c, it works.
Am I using wildcard in the correct manner?
I'm using GNU Make 3.81.
make doesn't cope well with backslashes; you need to double each, or (better) switch to forward slashes instead.
Your recipe overrides the built-in rules for creating object files from C files with a broken one, though.
Your recipe claims that all $(OBJS) will be produced from a single compilation which has all the $(SRCS) as dependencies, but only reads the first one ($< pro $^). It's probably better to just say what you want and let make take it from there.
.PHONY: all
all: $(OBJS)
%.o: ../path/to/srcs/%.c

Assignment of variable in GNU Makefile

I am new in GNU make. I have a problem understanding Makefile program inside NETAL software (it can be downloaded from here).
Inside folder NETAL, there is a file Makefile which includes other files such as make.inc and make.headers.
I understand most of the rules in Makefile inside folder NETAL, except for these:
Line 16 and 22. $(LINK.o) $^ $(LDLIBS) $(OUTPUT_OPTION)
What does $(LINK.o) and $(OUTPUT_OPTION) refer to? I cannot find a definition/assignment of these variable anywhere (neither in make.inc and make.headers).
Line 26 and 30. $(COMPILE.c) $< $(OUTPUT_OPTION)
Same problem with variable $(COMPILE.c). I can't find its assignment anywhere, and what it refers to. Also I don't understand the meaning of $< (and why "<" has to be preceded by a dollar sign "$"). I know dollar sign is for variable, just like in PHP.
I have knocked myself out reading the manual (RTFM, I know): GNU make manual
And this is the result of my reading: In Chapter 4.3 Types of Prerequisites, I found an example which similar with what I am looking for:
OBJDIR := objdir
OBJS := $(addprefix $(OBJDIR)/,foo.o bar.o baz.o)
$(OBJDIR)/%.o : %.c
$(COMPILE.c) $(OUTPUT_OPTION) $<
all: $(OBJS)
$(OBJS): | $(OBJDIR)
$(OBJDIR):
mkdir $(OBJDIR)
Unfortunately, there is no explanation about what is "$(COMPILE.c)", "$(OUTPUT_OPTION)", and "$<" .
Can someone explain it to me in a way that is easier for me to understand?
Or give me a link/keyword so that I can Google further.
These are prefedined make variables. From the GNU make manual:
Every rule that produces an object file uses the variable OUTPUT_OPTION. make defines this variable either to contain ‘-o $#’, or to be empty, depending on a compile-time option.
and
make follows the convention that the rule to compile a .x source file uses the variable COMPILE.x. Similarly, the rule to produce an executable from a .x file uses LINK.x; and the rule to preprocess a .x file uses PREPROCESS.x.

Why isn't make detecting changes in header dependencies

I'm not sure what I'm doing wrong here. I'm trying to get make to figure out what dependencies my project has for, not only source files, but non-system included header files. I've many resources from this very site related to this subject.
Such as: Makefile header dependencies and Makefile, header dependencies
However, when I do
touch MyHeader.h
as a test to see if this works, my make process fails to rebuild the source files that include this header. So, here's what I have in my makefile (of relevance that is)
CPP=g++
CPPFLAGS=-Iadditional/includes -MMD
CXXFLAGS=-std=c++0x -c
# not every source file in this directory needs to be included in this build
# this is because of shared code with Windows
SOURCESFILTER = File1.cpp File2.cpp
OBJ_DIR=obj
SOURCES = $(filter-out $(SOURCEFILTER),$(wildcard *.cpp))
OBJECTS = $(addprefix $(OBJ_DIR)/,$(SOURCES:.cpp=.o))
DEPENDENCIES = $(OBJECTS:.o=.d)
.PHONY: archive
archive : $(OBJECTS)
ar mylib.a obj/*.o
-include $(DEPENDENCIES)
$(OBJ_DIR)/%.o: $(SOURCES) $(DEPENDENCIES)
$(CPP) $(CPPFLAGS) $(CXXFLAGS) $< -o $#
I've verified that the above process does indeed generate the expected *.d files. I assume that I'm including them correctly. However, as mentioned, as a test I do:
touch MyHeader.h
which is in the same directory as the sources, and rerun the make, none of the source files which include this header are remade. What am I missing?
Andy
First, you cannot include prerequisites in a suffix rule. Even if you could, you certainly would not want to include $(SOURCES) or $(DEPENDENCIES), because that would cause every object to rebuild whenever any source or dependency file changed.
Second, you cannot create the target file in a different directory from where make expects it to be. Make will put the place where it wants to find the target in the variable $#, and you must write the output into that location exactly. If you ever see a rule that modifies the target, such as above where you use obj/$#, that won't work.
Most likely GCC is writing the files as obj/foo.d, but your include is trying to include foo.d but that doesn't exist... but since you used -include make doesn't complain.
I recommend you first write the object files into the local directory and get that working with dependencies. Once that works, then read up on how to write targets to a different directory and/or ask again.
ETA:
Try something like this:
CXX := g++
CPPFLAGS := -Iadditional/includes -MMD
CXXFLAGS := -std=c++0x
# not every source file in this directory needs to be included in this build
# this is because of shared code with Windows
SOURCESFILTER := File1.cpp File2.cpp
OBJ_DIR := obj
SOURCES := $(filter-out $(SOURCEFILTER),$(wildcard *.cpp))
OBJECTS := $(addprefix $(OBJ_DIR)/,$(SOURCES:.cpp=.o))
DEPENDENCIES := $(OBJECTS:.o=.d)
.PHONY: archive
archive: mylib.a
mylib.a: $(OBJECTS)
$(AR) $# $^
-include $(DEPENDENCIES)
$(OBJ_DIR)/%.o: %.cpp
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $# $<

makfile variable expansion

I have a few libs that use each other. Whenever I build any of them I need to define preprocessor define to make sure that proper visibility modifiers are used (e.g. dllimport/dllexport in windows speak).
All the libs use the same makefile, that is, they share rules, CFLAGS etc. All these libs only differ by list of input files, the rest of makefiles are shared by the libs.
The shared makefile has a variable that contains list of all the libs, like this:
MODULE_LIBS = liba123 libb456 libc999
Then, I need these preprocessor defines to be enabled for each of these libs:
For liba123: -Da123_EXPORTS
For libb456: -Db456_EXPORTS
For libc999: -Dc999_EXPORTS
Each of these libs live in their respective subfolders that are names like the libs themselves (e.g. liba123, libb456 etc).
So, I wrote this makefile trick to enabled these EXPORTS defines based on the path of the file being compiled:
%.o: %.c
$(CC) $(CPPFLAGS) $(CFLAGS) -D$(filter $(MODULE_LIBS),$(subst lib, ,$(subst /, ,$#)))_EXPORTS -c -o $# $<
I have to add that thing ('-D$(filter $(MODULE_LIBS),$(subst lib, ,$(subst /, ,$#)))_EXPORTS') all over the place, because I have many similar rules. There is nothing can be done with all these different rules, but there is one thing that they have in common: the $(CPPFLAGS).
Here comes the question. Can I add that "-D$(filter $(MODULE_LIBS),$(subst lib, ,$(subst /, ,$#)))_EXPORTS" to CPPFLAGS in such a way that all these makefile variables would only expand in place where it's used?
I think I'd do it this way:
liba123/%.o : EXPORTS=a123_EXPORTS
libb456/%.o : EXPORTS=b456_EXPORTS
libc999/%.o : EXPORTS=c999_EXPORTS
%.o: %.c
$(CC) $(CPPFLAGS) $(CFLAGS) -D$(EXPORTS) -c -o $# $<
But you can put them in CPPFLAGS if you prefer:
liba123/%.o : CPPFLAGS+=-Da123_EXPORTS
libb456/%.o : CPPFLAGS+=-Db456_EXPORTS
libc999/%.o : CPPFLAGS+=-Dc999_EXPORTS

Resources