How do I check values of variables from an included makefile? - makefile

../Makefile
BAR=sometext
Makefile
include ../Makefile
FOO=othertext
ifneq ($(BAR),)
FOOBAR=$(FOO) $(BAR)
else
FOOBAR=$(FOO)
endif
all:
#echo $(FOOBAR)
This will print "othertext" regardless of whether I have BAR defined in ../Makefile. Why is that? If i have BAR defined locally, it will print "othertext sometext" which is what I want. But I'm stuck using an include in my situation.
I've tried various scenarios using $(value $(BAR)) $(strip $(BAR)), etc but I can't seem to get this to work how I want.

You could do this other way around. In Makefile:
FOO=othertext
BAR=$(FOO)
include ../Makefile
all:
#echo $(BAR)
and ../Makefile
BAR+=sometext

Related

Makefile: target-specific variables in target dependencies declaration

I'm trying to write a makefile that uses target-specific variables, but while the variables are set correctly in each target and prerequisite body, the prerequisites list itself isn't updated with the variable, thus causing the wrong prerequisite to be checked and called.
How can I update target-specific variables in the prerequisites too?
In the example below, both make foo and make bar should print "world", but make foo prints "hello".
X=hello
hello:
echo "hello"
world:
echo "world"
foo:X=world
foo:$(X)
bar:X=world
bar:
make $(X)
The goal I'm trying to achieve is that different targets will build similar prerequisites - the same files, in different folders - by passing the folders as a target-specific variable. The issue is that as in the example below, if one target is called first (foo, in the example), calling the second will not do anything.
DIR=fooDir
FILE=$(DIR)/filename
$(FILE):
touch $(FILE)
echo $(FILE)
foo: $(FILE)
bar:DIR=barDir
bar: $(FILE)
6.11 Target-specific Variable Values:
As with automatic variables, these values are only available within the context of a target’s recipe (and in other target-specific assignments).
But not in the prerequisite list. In other words, in foo:$(X) X is not target specific.
One way to achieve the desired results is:
same_files := filename another_filename
# ${1} is the target name.
# ${2} is the directory.
define similar_prerequisites
${1} : $(addprefix ${2}/,${same_files})
$(addprefix ${2}/,${same_files}) : | ${2}
touch $$#
${2} :
mkdir $$#
endef
all : foo bar
foo bar :
#echo "$# prerequisites are $^"
$(eval $(call similar_prerequisites,foo,fooDir))
$(eval $(call similar_prerequisites,bar,barDir))
Output:
$ make
mkdir fooDir
touch fooDir/filename
touch fooDir/another_filename
foo prerequisites are fooDir/filename fooDir/another_filename
mkdir barDir
touch barDir/filename
touch barDir/another_filename
bar prerequisites are barDir/filename barDir/another_filename
I needed something similar. Does this exemple suit your needs ?
# Basic rules can be ignored
# Use % to not take much space
.PHONY: foo bar
foo bar:
#printf "%s depends on %s\n" "$#" "$^"
fooDir/ barDir/:
#printf "%s\n" "Building $#"
#mkdir "$#"
fooDir/%: fooDir
barDir/%: barDir
%/filename: %/
#printf "%s\n" "Building $#"
#touch "$#"
%/filename2: %/
#printf "%s\n" "Building $#"
#touch "$#"
%/filename3: %/
#printf "%s\n" "Building $#"
#touch "$#"
fooFile = fooDir/filename2
barFile = barDir/filename2
DIR = $(*)Dir
FILE = $(DIR)/filename3
.SECONDEXPANSION:
# Here the fun begin
# See full details on https://stackoverflow.com/a/73679964/7227940
foo bar: %: $$(*)Dir/filename $$($$(*)File) $$(FILE)
output:
$ make foo; make bar
Building fooDir/
Building fooDir/filename
Building fooDir/filename2
Building fooDir/filename3
foo depends on fooDir/filename fooDir/filename2 fooDir/filename3
Building barDir/
Building barDir/filename
Building barDir/filename2
Building barDir/filename3
bar depends on barDir/filename barDir/filename2 barDir/filename3
The tricks is in .SECONDEXPANSION: all target defined after (that's why I put it at the end) will get a second expansion. The $(*) aka the stem of matched rules will only become available in the second expansion. This allow us to build name that is linked to the target.
The pattern targets: %: deps allow to match only on listed target and not catch all target.
I prefer the version 1 or 2 because the third version you might be tempted to use in place before .SECONDEXPANSION: and thus you will get weird error messages. If you really want to use 3 then put this special target at the top. Also 3 might have wrong value (you might want MAKECMDGOALS or something like that instead of *) if used with multiple wildcard rule. Just remember if you want to use 3 then put $$ either in DIR in FILE definition or in the use site.

Conditional statements using Make

I'm having a problem with conditional statements in make. Existing questions on SO only work on the highest level in the Makefile.
Here's what I have:
.PHONY: all
all: bar/*.o
bar/%.o: foo/%.cc
ifeq (,$(wildcard $(#D)))
#echo $(wildcard $(#D))
endif
There are two empty files under foo, a.cc and b.cc. The folder bar exists.
Here's the output:
PS C:\Users\cbrown2\Src> mingw32-make
bar
bar
It enters the ifeq statement, even though $(wildcard $(#D)) gives bar.
What gives?
You cannot use conditionals this way. When make evaluates your conditional automatic variables don't have a value yet. So make will see:
bar/%.o: foo/%.cc
#echo $(wildcard $(#D))
because $(#D) expands as the empty string. And when make will pass the recipe to the shell it will first expand $(#D) which, this time, has a value: bar.
One side note that has nothing to do with your problem: when bar does not contain all object files yet,
all: bar/*.o
will not do what you probably want. What you probably want is:
SRC := $(wildcard foo/*.cc)
OBJ := $(patsubst foo/%.cc,bar/%.o,$(SRC))
all: $(OBJ)

Makefile: defining rules and prerequisites in recipes

I have a setup where the files I want to process with make are dependent on the output of another program. Building the program and all its prerequisites
is also a complicated task so I would like to use make for this as well. Now my problem is, that it doesn't seem that one can generate rules and prerequisites in Makefile recipes. Consider the following code:
bar:
echo target1 target2 target3 > bar
foo: bar
$(eval BAR := $(shell cat bar))
define FUN
$(1):
touch a$(1)
endef
ifdef BAR
$(foreach i,$BAR,$(eval $(call FUN,$(i))))
endif
blub: foo $(BAR)
I replaced a big set of complicated recipes that lead to the generation of the list of files I want to have in the end by the bar recipe. In reality, producing the content of bar is very complicated and should be done by a set of Makefile recipes and cannot just be done by (as the above suggests):
BAR:=$(shell echo target1 target2 target3)
I would like to put the foreach loop into the recipe for foo but that fails with prerequisites cannot be defined in recipes which makes sense and is also explained in function define in makefile
But it seems that when I do make blub that at the time when foo eval's BAR to a different value, the prerequisites for blub are not re-evaluated.
So I think ultimately I'm looking for two things:
how do I generate recipes dynamically at runtime, based on (and dependent on) what another recipe (bar in this case) outputs?
how do I update the prerequisites of a target (blub in this case) dynamically at runtime, based on (and dependent on) what another recipe (bar in this case) outputs?
Thank you!
EDIT: SOLUTION
With the help of #user657267 the following seems to solve my problem:
.PHONY: all
all: blub
-include bar.make
.PHONY: blub
blub: $(BAR)
echo $^
bar.make: Makefile
printf 'BAR=target1 target2 target3\n' > $#
printf 'target1 target2 target3:\n' >>$#
printf '\ttouch $$#' >> $#
.PHONY: clean
clean:
rm -f target1 target2 target3 bar.make
Sounds like you should be using make's self-remaking features
-include bar.make
blub: $(BAR)
#echo $^
bar.make:
#echo BAR := target1 target2 target3 > $#
#echo target1 target2 target3: ; touch $$# >> $#
Obviously the recipes for bar.make are contrived, in the real world they'd probably invoke some kind of script that outputs a valid makefile.

How do I force a target to be rebuilt if a variable is set?

Assume I have a build-target foo:
foo:foo.c
$(CC) $(CFLAGS) $(ARGS) -c foo.c -o foo
Now, ARGS is something that I pass on the command line:
$ make ARGS:=-DX=1 foo
So, I need to bypass make's cleverness, because the foo target does not only depend on which files have changed, but also on the value of ARGS.
Is there something in make to do this? My hack (see answer) doesn't seem to be the most elegant but it works. Anything better?
Here is a general solution to your specific problem.
You want to be able to depend on a variable as a prerequisite. That is, you can make it a prerequisite to any target in your makefile, and when the value of the variable changes, you rebuild those targets.
Here is a function that does that, you use this function to declare a variable to be dependable, and then you can use it as a prerequisite.
Note that if the variable is not used on the command line, it will still mean that variable still has a value, namely, the empty string.
define DEPENDABLE_VAR
.PHONY: phony
$1: phony
#if [[ `cat $1 2>&1` != '$($1)' ]]; then \
echo -n $($1) > $1 ; \
fi
endef
#declare ARGS to be dependable
$(eval $(call DEPENDABLE_VAR,ARGS))
foo:foo.c ARGS
$(CC) $(CFLAGS) $(ARGS) -c foo.c -o foo
In fact, we could omit the need for "declaration", and just write a similar function that will make all variables dependable by default. But I don't like that. I prefer that the users that modify makefiles I write, declare their intentions explicitly. It is good for them :)
My solution was to create a dummy phony target:
.PHONY:dummy
dummy:
#:
and have foo depend on dummy if ARGS is nonempty:
foo:foo.c $(patsubst %,dummy,$(ARGS))
Note on Mark's excellent answer
The bare necessities of Mark's answer are actually very simple. It really boils down to just:
.PHONY: phony
ARGS: phony
#if [[ `cat ARGS 2>&1` != '$(ARGS)' ]]; then echo -n $(ARGS) >ARGS; fi
The rest of his code is just to let you reproduce the recipe for other variable names without repeating yourself. Useful in practice, but the above version will help you see what's going on more easily.
In fact, my answer can even be made general (like Mark's) for any variable name, but in a less complicated way as follows:
.PHONY: phony
.ARG~%: phony
#if [[ `cat .ARG~$* 2>&1` != '$($*)' ]]; then echo -n $($*) >.ARG~$*; fi
Then you simply add .ARG~MYVARNAME as a dependency to any target to make that target depend on variable MYVARNAME.
Note that the dot in front of .ARG~% simply causes it to create a dependency-tracking file that is 'hidden' in linux.
So in your case, you would do:
foo: foo.c .ARG~ARGS
$(CC) $(CFLAGS) $(ARGS) -c foo.c -o foo
I don't understand how the other solutions are supposed to work. If the ARGS target is .PHONY or depends on a .PHONY, then it will always be run, right?
Here is my solution using the $(file) function in newer versions of gmake:
.PHONY: FORCE
define DEPENDABLE_VAR
$(1):
echo -n $($(1)) > $(1)
ifneq ("$(file <$(1))","$($(1))")
$(1): FORCE
endif
endef
#declare ARGS to be dependable
$(eval $(call DEPENDABLE_VAR,ARGS))
foo: foo.c ARGS
touch foo
And the result:
~/stuff/makevars> make foo ARGS=1
echo -n 1 > ARGS
touch foo
~/stuff/makevars> make foo ARGS=1
make: 'foo' is up to date.
~/stuff/makevars> make foo ARGS=2
echo -n 2 > ARGS
touch foo
~/stuff/makevars> make foo ARGS=2
make: 'foo' is up to date.

How to specify --no-print-directory within the Makefile itself

I'd like to run my makefile without the -w flag turned on by the recursive make calls.
The flag to do that is --no-print-directory on the make command line.
Is it possible to specify that flag within the makefile itself?
I plan to make this flag dependent on a VERBOSE mode, perhaps something like
$(if $(VERBOSE),,MAKEFLAGS += no-print-directory))
Thanks,
Dan
Yes, just appending --no-print-directory to MAKEFLAGS should be enough, but you have to do that with conditional directives, not with conditional functions:
ifndef VERBOSE
MAKEFLAGS += --no-print-directory
endif
You can include the .SILENT: special target in the calling makefile. For example, here's your toplevel makefile:
all:
$(MAKE) -f sub.mk foo
.SILENT:
and the submake makefile, sub.mk:
foo:
#echo done
Note that .SILENT is considered obsolete, so it may not be around forever, and also note that including that in your makefile also has the effect of suppressing command echo, just as if you had put # before every command in the makefile.

Resources