Extract macro parameters from target name in Makefile - makefile

I have a bunch of lines like this in my makefile, in different permutations. I want to automate them with a general rule, so if I type $ make foo-WHATEVER, make knows how to build it from foo.c and relevant -D flags.
foo-PARAMA.o: foo.c
foo-PARAMA.o: CPPFLAGS += -DPARAMA
foo-PARAMA-PARAMB.o: foo.c
foo-PARAMA-PARAMB.o: CPPFLAGS += -DPARAMA -DPARAMB
foo-PARAMA-PARAMB-PARAMC.o: foo.c
foo-PARAMA-PARAMB-PARAMC.o: CPPFLAGS += -DPARAMA -DPARAMB -DPARAMC

Well, you can try something like this:
foo-%.o : foo.c
$(CC) $(CPPFLAGS) $(addprefix -D,$(subst -, ,$*)) $(CFLAGS) -o $# -c $<
but my suspicion is that you're really going to want to do this for "any source file" not just foo.c. That's much harder because you can't have multiple patterns in a single target or prerequisite.
For that you'll have to know the list of source files up-front and use eval:
SRCS = foo.c bar.c baz.c biz.c
define make-pattern
$1-%.o : $1.c
$$(CC) $$(CPPFLAGS) $$(addprefix -D,$$(subst -, ,$$*)) $$(CFLAGS) -o $$# -c $$<
endif
$(foreach S,$(SRCS),$(eval $(call make-pattern,$S)))

It looks like you want to do configuration management inside make. The problem is that make itself is not really equipped with such functionality. My guess is that at the time of its invention the extreme diversity of platforms which one day will run some unixoid build system simply wasn't foreseeable - so the main use case was to easily define a rather homogenous build with some small deviations.
Now, its 2019 and make is still around and supposedly with a larger user base than ever before. One possible remedy which has the advantage that it doesn't require any other tool is to use gmtt which is a GNUmake library for exactly this configuration purpose. Below I sketch a hypothetical config scenario:
include gmtt/gmtt.mk
PLATFORM := $(shell uname)
# table of platforms with 2 columns. Don't forget that the leading element of every table must be an integer denoting the number of columns!
define AVAILABLE-PLATFORMS :=
2
CYGWIN?NT-4.? toolX
CYGWIN?NT-10.? toolY
Linux* toolZ
FreeBSD toolXYZ
endef
define ADDITIONAL-FLAGS :=
2
toolX -Dfoo
toolY -Dbar
toolZ -Dbaz
toolXYZ -Dfoo
toolXYZ -Dbar
toolXYZ -Dbaz
endef
# select column 2 from the table line(s) which glob-match the current platform:
USED-TOOL := $(call select,2,$(AVAILABLE-PLATFORMS),$$(call glob-match,$(PLATFORM),$$1))
# now select the right flags for the tool
CFLAGS_OPT := $(call select,2,$(ADDITIONAL-FLAGS),$$(call str-eq,$(USED-TOOL),$$1))
$(info $(USED-TOOL) Options: $(CFLAGS_OPT))
Admittedly, this is a lemma on Greenspun's tenth rule but the other options (composing configuration information with a handful of external tools) aren't so attractive either.

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.

GNU make: several targets in one pattern rule

With explicit targets I can combine several rules like
foo.o bar.o: $(SOURCES)
cc $< -o $#
This is equivalent of
foo.o: $(SOURCES)
cc $< -o $#
bar.o: $(SOURCES)
cc $< -o $#
But I want to use pattern rules.
I have several troff documents (man, README) and I want to generate .html and .ascii files.
Naive approach is
GROFF := groff
DOCS := man README
DOC_FILES = $(foreach doc,$(DOCS),$(doc).html $(doc).ascii)
CALL_GROFF = $(GROFF) -T$(subst $*.,,$#) -mman $< > $#
%.html %.ascii: %.doc
$(CALL_GROFF)
.DEFAULT: all
all: $(DOC_FILES)
.PHONY: clean
clean:
rm $(DOC_FILES)
But it doesn't work, because make believes that all files are created with one command (much like & in modern make: https://www.gnu.org/software/make/manual/html_node/Multiple-Targets.html)
Obviously I can do
GROFF := groff
DOCS := man README
DOC_FILES = $(foreach doc,$(DOCS),$(doc).html $(doc).ascii)
CALL_GROFF = $(GROFF) -T$(subst $*.,,$#) -mman $< > $#
%.ascii: %.doc
$(CALL_GROFF)
%.html: %.doc
$(CALL_GROFF)
.DEFAULT: all
all: $(DOC_FILES)
.PHONY: clean
clean:
rm $(DOC_FILES)
But it is a kind of copy-paste.
Could it be solved with GNU make?
This is exactly how this works; it's a long-standing feature. From the documentation:
Pattern rules may have more than one target; however, every target must contain a % character. Pattern rules are always treated as grouped targets (see Multiple Targets in a Rule) regardless of whether they use the : or &: separator.
As example states, it was meant to deal with programs that generate more than one output in one invocation, like bison. You can either update your recipe to generate both files in one shot, or keep the rules separated as you do now.

Makefile Dynamic Rules w/ No GNU-make Pattern

I have a set of .cpp files that I want to compile. These .cpp files are in a hierarchical directory structure. I want the corresponding .o files to all end up in one build folder.
Here's how I get GNU make to enumerate the files:
SRCS = \
$(wildcard $(CODE)/**/*.cpp) \
$(wildcard $(CODE)/AlgebraLibraries/**/*.cpp) \
$(wildcard $(CODE)/Calculator/Environments/**/*.cpp)
BARE_SRCS = $(notdir $(SRCS))
BARE_OBJS = $(BARE_SRCS:.cpp=.o)
OBJS = $(addprefix $(BUILD)/, $(BARE_OBJS))
Having done this, I have no idea how to create the rules that will create the .o files from the .cpp files. Intuitively, what I want to do is the following pseudocode:
for i=0, N do # <-- a for-loop!
$(OBJS)[i]: $(SRCS)[i] # <-- the rule!
$(CPP) -c $(SRCS)[i] -o $(OBJS)[i] # <-- the recipe
end
Of course, this is not valid GNU make code, but I trust you understand what it is here that I'm trying to do. The following will not work.
%.o: %.cpp
$(CPP) -c $< -o $#
This doesn't work, because GNU make is matching up the % signs, assuming that the .o files live along-side the .cpp files.
The alternative to all of this, which I know will work, but will be extremely tedious, is to enumerate all of the rules by-hand as explicit rules. There has to be a better way!
I've been researching GNU make's ability to generate rules, but there appears to be no way to do it without the built-in logic. It would be really nice if I could utilize some flow-control statements to generate the rules that I want to make. Is this asking too much of GNU-make?
In any case, is there a way to do what it is I'm trying to do with GNU make? If so, how?
This looks like a job for... several advanced Make tricks:
all: $(OBJS)
define ruletemp
$(patsubst %.cpp, $(BUILD)/%.o, $(notdir $(1))): $(1)
$$(CPP) -c $$< -o $$#
endef
$(foreach src,$(SRCS),$(eval $(call ruletemp, $(src))))
If $(BUILD) is constant, you can always just do:
$(BUILD)/%.o: %.cpp
$(CPP) -c $< -o $#

How to write different implicit rules for different file names for GNU Make

I have a directory in which I keep adding different C++ source files, and generic Makefile to compile them. This is the content of the Makefile:
.PHONY: all clean
CXXFLAGS = -pipe -Wall -Wextra -Weffc++ -pedantic -ggdb
SRCS = $(wildcard *.cxx)
OBJS = $(patsubst %.cxx,%.out,$(SRCS))
all: $(OBJS)
clean:
rm -fv $(OBJS)
%.out: %.cxx
$(CXX) $(CXXFLAGS) $^ -o $#
NOTE: As is obvious from above, I am using *.out for executable file extensions (and not for object file).
Also, there are some files which are compiled together:
g++ file_main.cxx file.cxx -o file_main.out
To compile such files, until now I have been adding explicit rules in the Makefile:
file_main.out: file_main.cxx file.cxx
file.out: file_main.out
#echo "Skipping $#"
But now my Makefile has a lot of explicit rules, and I would like to replace them with a simpler implicit rule.
Any idea how to do it?
First, this method of compiling several source files directly into an executable is not a terribly good idea. The more common compile-then-link approach will save a lot of unnecessary compilation.
That said, the way to replace many explicit rules with a simpler rule depends on what the explicit rules have in common. You already have a pattern rule:
%.out: %.cxx
$(CXX) $(CXXFLAGS) $^ -o $#
and if all you want to do is add another source file to a particular target, you don't have to do this:
g++ file_main.cxx file.cxx -o file_main.out
you can get the effect just by adding a prerequisite (in a line by itself):
file_main.out: file.cxx
If you have several targets with that pattern, you can use a pattern rule:
file_main.out another_main.out a_third_main.out: %_main.out : %.cxx
If you have many such targets, you can use a variable:
MAIN_THINGS = file another a_third a_fourth and_yet_another
MAIN_TARGETS = $(addsuffix _main.out, $(MAIN_THINGS))
$(MAIN_TARGETS): %_main.out : %.cxx
And you can add other patterns for other target sets, even overlapping sets. Does that cover your situation?
It seems that you are putting the source code for multiple different programs in the same folder, and this is really the source of your problems. If you separate the source code for your libraries and programs into separate folders (or, better yet, separate projects), then you can skirt this issue by depending on all source files in the given folder. When you have everything intermixed, it is necessary to be explicit.
That said, if your dependencies have consistent, predictable names, then it is possible to eliminate this redundancy by using the eval function. For example, based on the example above:
#
# I'm going to use standard file extensions here,
# slightly deviating from your conventions. I am also
# assuming that there is a variable named PROGNAMES,
# which gives a list of all the programs to be built.
#
define ADD_EXECUTABLE
$(1): $(1).o $(1)_main.o
$(LINK.cc) $(1).o $(1)_main.o -o $(1)
endef
$(foreach progname,$(PROGNAMES),$(eval $(call ADD_EXECUTABLE,$(progname))))
Also, just a few suggestions... you should append to CXXFLAGS rather than overwrite it and you would be better off using standard file extensions (".cpp" for C++ source files, ".o" for object files, no extension for executables). See my Makefile tutorial for tips on making things easier with Make (no pun intended).

No rule to make target consoleio.c

In a recent issue, I've found that DJGPP can only accept the DOS command line character limit. To work around this limitation, I've decided to try to write a makefile to allow me to pass longer strings. In the process of hacking together a makefile and testing it, I've come across a strange error. The makefile is as follows:
AS := nasm
CC := gcc
LD := ld
TARGET := $(shell basename $(CURDIR))
BUILD := build
SOURCES := source
CFLAGS := -Wall -O -fstrength-reduce -fomit-frame-pointer -finline-functions \
-nostdinc -fno-builtin -I./include
ASFLAGS := -f aout
export OUTPUT := $(CURDIR)/$(TARGET)
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
SOBJS := $(SFILES:.s=.o)
COBJS := $(CFILES:.c=.o)
OBJS := $(SOBJS) $(COBJS)
build : $(TARGET).img
$(TARGET).img : $(TARGET).bin
concat.py
$(TARGET).bin : $(OBJS)
$(LD) -T link.ld -o $# $^
$(SOBJS) : %.o : %.asm
$(AS) $(ASFLAGS) $< -o $#
$(COBJS) : %.o : %.c
$(CC) -c $< $(CFLAGS) -o $#
When attempting to run it, I receive this error:
make: *** No rule to make target `consoleio.c', needed by `consoleio.o'. Stop.
What I don't understand is why it's trying to find a rule for .c files. From what I understand, if the file is there, it should just use it. How do I make make not need a rule for .c files?
What you are trying to do will not work without VPATH, and since you are still learning makefiles, I would avoid using VPATH.
The rule is looking for "consoleio.c", which if I understood your makefile correctly does not exist; what exists is "source/consoleio.c". You probably should change it to something like "$(SOURCES)/%.c" instead of "%c".
I didn't check your syntax for that rule, however. If it's incorrect, the builtin "%.o: %.c" rule will be used instead, which would have the same problem.
The way you are doing is not the usual way I've seen, however. The usual way is to:
Create an implicit rule "%.o: %.c" (or in your case "%.o: $(SOURCES)/%.c")
Explicit list the dependencies for each file: "foo.o: foo.c bar.h baz.h" (with no command, the implicit rule has the command)
Let's try a non-comment answer...
Possibility A:
Your macro for SFILES is looking for files ending in '.s'.
Your rule for compiling SOBJS is looking for files ending in '.asm'.
Possibility B:
Your rule for SOBJS and COBJS is in a notation I don't recognize.
According to the GNU Make manual, you can write implicit rules as:
%.o : %.c ; command
You seem to have a list of targets $(SOBJS) that depends on '%.o : %.asm'.
I'm not sure how make will interpret that.
Personally, I would not trust wild-cards in build rules. I'd much rather spend the time listing exactly which source files are needed to build the code. I don't often run into this problem as a result.
#CesarB seems to have nailed the issue, I'll just add a couple of observations.
I'd strongly recommend against using wildcards in build rules. The build rules should clearly define exactly what is being built, and not depend on what files happen to be in the directory.
I'd also recommend against using VPATH unless you are (1) building in a separate build directory, or (2) have your source files spread out over a large number of directories. If all your sources are in a single directory, using VPATH is only going to confuse.
The := assignment form is usually only used when the variable evaluation is known to take long time, such as when using a $(shell ...). Otherwise, "=" is preferrable.
Using "export" to propagate OUTDIR to concat.py (which I presume it is, since concat.py doesn't take any parameters) is a code smell. If possible, pass it as a parameter instead.

Resources