Passing variable upward from makefile targets - makefile

I have several targets in the Makefile. Each target add some values to the makefile variable. For example, CFLAGS.
CFLAGS := 1
a: CFLAGS += 2
b: CFLAGS += 3
I need to write target "с" which will "see" values assigned in "a" and "b". And which, may be, add some more values.
I tried to do it like this:
c: CFLAGS += 4
c: a b
echo ${CFLAGS}
Here I planned to see CFLAGS equal to "1 2 3 4" or "1 4 2 3"
But in fact CGLAGS will be equal to:
a: 1 4 2
b: 1 4 3
c: 1 4
Sample Makefile:
CFLAGS := 1
.PHONY: all a b c
all: c
a: CFLAGS += 2
a:
echo TargetA ${CFLAGS}
b: CFLAGS += 3
b:
echo TargetB ${CFLAGS}
c: CFLAGS += 4
c: a b
echo TargetC ${CFLAGS}
Any way to do this?
PS. Some reasoning for ones who want to.
I have some programs that needs libA. Target "a" that have inclides/libs for libA and is used for these programs.
I have some programs that needs libB.
And I have some programs that needs all: libA, libB and libC.
I want to "reuse" targets "a" and "b" to avoid making assignment duplication.

As mentioned in the other comment, you have the prerequisite relationship backwards to get the behavior you want.
Rather than writing a lot of call and eval functions, I would prefer to use computed variable names here. You can do something like this:
CFLAGS = 1
CFLAGS_a = 2
CFLAGS_b = 3
CFLAGS_c = 4
all: c
c: a b
a b c:
#echo '$(CFLAGS) $(CFLAGS_$#) $(foreach P,$<,$(CFLAGS_$P))'
to print the default CFLAGS, then the CFLAGS for the target, then the CFLAGS for each prerequisite. Of course you'll have to modify this for your specific purposes.
For more information on different types of "metaprogramming" in GNU make, you can read this set of blog posts. Start with the first one (which is at the end of that list, unfortunately).

Problem
From GNU Make's manual section on target-specific variables:
when you define a target-specific variable that variable value is also in effect for all prerequisites of this target, and all their prerequisites, etc.
In your makefile, a and b are prerequisites of c, so setting CFLAGS as a c's target-specific variable does have effect also in a and b. Not the other way around, i.e.: setting CFLAGS as an a or b's target-specific variable has no effect in c at all, since it is not a prerequisite of any of them.
Possible alternative
If your motivation for achieving such a thing, is that a target may need an arbitrary combination of any of libA, libB or libC. Then, I would suggest you to define the variables libA, libB, libC, libAB, libAC, libBC and libABC that represent the corresponding library combinations and then include one of those variables to the corresponding target as prerequisite.
The following makefile may serve as a starting point and clarifies what I want to express as well:
.PHONY: all
libsCommon := 1
libsA := $(libsCommon) 2
libsB := $(libsCommon) 3
libsC := $(libsCommon) 4
# $(call include-to,varSrc,varDst)
# for avoiding repetition of libraries
define include-to
$(eval $2 += $(filter-out $($2),$($1)))
endef
# libsAB
$(call include-to,libsA,libsAB)
$(call include-to,libsB,libsAB)
# libsAC
$(call include-to,libsA,libsAC)
$(call include-to,libsC,libsAC)
# libsBC
$(call include-to,libsB,libsBC)
$(call include-to,libsC,libsBC)
# libsABC (all)
$(call include-to,libsA,libsABC)
$(call include-to,libsB,libsABC)
$(call include-to,libsC,libsAll)
all:
#echo "libsA: $(libsA)"
#echo "libsB: $(libsB)"
#echo "libsC: $(libsC)"
#echo
#echo "libsAB: $(libsAB)"
#echo "libsAC: $(libsAC)"
#echo "libsBC: $(libsBC)"
#echo
#echo "libsABC: $(libsABC)"
Running make with this makefile produces:
libsA: 1 2
libsB: 1 3
libsC: 1 4
libsAB: 1 2 3
libsAC: 1 2 4
libsBC: 1 3 4
libsABC: 1 2 3 4
This approach, however, won't scale well when you have many libraries or different possible combinations of them.
In any case, I would strongly recommend that if target a does need libA, then libA should be made a prerequisite of a.

Related

Can GNU Make use pattern matching to look up variables?

I'm trying to get Make to build some data analysis, where there are file lists controlled by one overall parameter.
To write it explicitly would be something like:
A_EXTS = a b c d e
B_EXTS = f g h i j
C_EXTS = k l m n o
A.dat : $(foreach EXT, ${A_EXTS}, prefix1_${EXT}.dat prefix2_${EXT}.dat)
python analyse.py $^ > $#
B.dat : $(foreach EXT, ${B_EXTS}, prefix1_${EXT}.dat prefix2_${EXT}.dat)
python analyse.py $^ > $#
C.dat : $(foreach EXT, ${C_EXTS}, prefix1_${EXT}.dat prefix2_${EXT}.dat)
python analyse.py $^ > $#
Obviously the only difference between the three rules is the A vs B vs C.
I thought to try something like
%.dat : $(foreach EXT, ${%_EXTS}, prefix1_${EXT}.dat prefix2_${EXT}.dat)
python analyse.py $^ > $#
…but that doesn't work; e.g. make B.dat runs the rule for B.dat but ignores the dependencies; $^ is set to the empty string.
The files starting prefix2_ are generated by another recipe, so I can't just specify them within the recipe, they need to be marked as dependencies here.
Is this possible to express these dependencies without repeating the same rule?
Well, you can't do it quite like you want to here, but it's not related to looking up variable names: it's because of expansion order.
Variables in targets and prerequisites are expanded when the makefile is parsed, but make doesn't expand the patterns in pattern rules until much later. That means when make expands the ${%_EXTS} variable as it parses the makefile, it has no idea what the value of % will be later when it's actually trying to build things.
You can use secondary expansion to delay expansion of variables until make's second pass where it is actually finding target names. I pulled the logic out into a separate variable and used call to make it a bit more readable:
.SECONDEXPANSION:
EXPANDDEPS = $(foreach EXT,${$1_EXTS},prefix1_${EXT}.dat prefix2_${EXT}.dat)
%.dat : $$(call EXPANDDEPS,$$*)
python analyse.py $^ > $#

Extract macro parameters from target name in 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.

Gnu make: is it possible to delay include directive to secondary expansion?

I need to delay inclusion of dependency fragments until second expansion time because the make file I'm editing is itself an include file and I will not have a list of source files to generate the includes until secondary expansion.
.SECONDEXPANSION:
AUTO_DEPENDENCY_FILES = $(patsubst %.cc, depend/%.d, $(CC_SRC_FILES))
# the following does the work because the include argument is not a rule
# prerequisite therefore no secondary expansion occurs
include $$(AUTO_DEPENDENCY_FILES)
depend:
-mkdir depend
all: autodepend
autodepend: depend autodepend_include
autodepend_include: $$(AUTO_DEPENDENCY_FILES)
#echo \"$^\"
$$(AUTO_DEPENDENCY_FILES): depend
depend/%.d: | %.cc
# generate .d files that do not exist
$(COMPILE.cc) -E $*.cc > /dev/null
%.o: %.cc
# update .d files that exist
$(COMPILE.cc) -o $# $<
Note the COMPILE.cc is a very long string that includes -MP -MMD -MFdepend/$*.d flags for auto dependency generation.
I don't know that there's a clean solution to this problem but with a bit of a hack you can get you what you want.
Given a main Makefile of:
$(info main one)
include depend.mk
$(info main two)
CC_SRC_FILES := $(addsuffix .c,a b c d e f)
$(info main three)
and a depend.mk of:
$(info depend one)
AUTO_DEPENDENCY_FILES = $(patsubst %.c,%.d,$(CC_SRC_FILES))
$(info AUTO_DEPENDENCY_FILES := $(AUTO_DEPENDENCY_FILES))
$(info MAKE_RESTARTS := $(MAKE_RESTARTS))
$(info CC_SRC_FILES := $(CC_SRC_FILES))
$(info depend two)
you get the following output when you run make:
main one
depend one
AUTO_DEPENDENCY_FILES :=
MAKE_RESTARTS :=
CC_SRC_FILES :=
depend two
main two
main three
make: `all' is up to date.
Which isn't surprising given the order of assignment and inclusion of files, etc.
Here's where the horrible hack comes in.
When make encounters an include directive that references a file that doesn't exist make sticks the file in a list of "missing include files" and continues parsing the makefile.
When it gets to the end of the makefile(s) it then tries to treat each entry in that list as a potential goal target1 and attempts to make the file.
Once the makefiles have been built make restarts itself and tries again.
You can use this to capture the value of CC_SRC_FILES in an built makefile that your makefile includes and have it be visible when you need it.
If we make depend.mk look like this:
$(info depend one)
include hack.mk
AUTO_DEPENDENCY_FILES = $(patsubst %.c,%.d,$(CC_SRC_FILES))
$(info AUTO_DEPENDENCY_FILES := $(AUTO_DEPENDENCY_FILES))
$(info MAKE_RESTARTS := $(MAKE_RESTARTS))
$(info CC_SRC_FILES := $(CC_SRC_FILES))
$(info depend two)
hack.mk: $(if $(MAKE_RESTARTS),,force)
#echo creating hack.mk
#echo 'CC_SRC_FILES := $(CC_SRC_FILES)' > '$#'
force: ;
Then our output from make becomes:
main one
depend one
depend.mk:3: hack.mk: No such file or directory
AUTO_DEPENDENCY_FILES :=
MAKE_RESTARTS :=
CC_SRC_FILES :=
depend two
main two
main three
creating hack.mk
main one
depend one
AUTO_DEPENDENCY_FILES := a.d b.d c.d d.d e.d f.d
MAKE_RESTARTS := 1
CC_SRC_FILES := a.c b.c c.c d.c e.c f.c
depend two
main two
main three
make: `all' is up to date.
Which gives us the value where we want it.
This isn't pretty but it does work.

Makefile to compile files that begin with a pattern

My makefile has this line:
OBJS = Test.o Test1.o Test2.o Test3.o ...
Is there a way to make this automatic by matching patterns?
If you are using gmake - you can use the shell command to ls the source files, then patsubst command to convert the list of source files to the list of object files.
SRC=$(shell ls *.cc)
OBJS=$(patsubst %.cc,%.o, $(SRC))
all:
#echo $(OBJS)
I doubt that what you're asking for is what you really want, but here goes:
NUMS := 1 2 3 4 5
OBJS := Test.o $(patsubst %,Test%.o,$(NUMS))
(There is a way to generate a sequence like 1 2 3 4 5 without spelling it out, but it's really ugly and not worth the effort.)

Make 3 passes for a target

I'll explain myself, here is my scenario:
Compile my target
Do a first optimization based on the first compilation
Do a second optimization based on the first optimization
Do a third optimization based on the second optimization
So far I tried the following:
.SUFFIXES:
.SECONDARY:
OBJECTS := $(addsuffix .obj,$(SOURCES))
override OBJECTS := $(OBJECTS:$(SRC)/%.obj=$(OBJ)/%.obj)
OC1 := $(patsubst %.obj, %.oc1, $(filter %c.obj,$(OBJECTS)))
O1 := $(L166_CMD:%.lnp=%.o1)
all: $(TARGET) $(O1)
$(TARGET): $(OBJECTS)
#echo Linking $(TARGET)...
$(OBJ)/%.c.obj: $(SRC)/%.c
#echo Compiling $(<F) ...
# c.oc1 is a intermediate file
%.c.oc1: %.c.obj
#echo 1 - Optimize $<...
#touch $#
$(O1): $(OC1)
#touch $#
echo Linking O1
Result is, I modify a C file, the target will regenerate only the modified C file but the O1 pass will optimize all C files again like it was not done before (but it was).
How can I modify this behavior?
The reason is your last target, $(O1): $(OC1). That is each O1 depends on every OC1.
What is the actual value of $(O1)? Is it supposed to be a list or a single target?
I would try to replace this rule by a pattern (if it possible).

Resources