Why is my Makefile's target specific variable getting set for all targets? - variables

I have a Makefile similar to the following:
target1: DEFAULT_VALUE ?= $(shell bash -c 'read -p "Enter DEFAULT_VALUE to set: " value && echo $$value')
target2:
echo "Hello"
target1:
echo "World"
I expect that the code to set DEFAULT_VALUE will only execute if I run make target1, however I find that it runs even if I run make target2
Does anyone know why this happens?

Your "similar" makefile is not similar enough. Your example above works fine for me: if I run make target2 then the shell command is not executed. If I run make target1, then it is.
Please check your example before posting it here and provide one that really fails.
My suspicion is that in your real environment, whatever is represented by target2 is a prerequisite of whatever is represented by target1, which means that target2 will inherit all of target1's target-specific variable assignments.

With the above Makefile, the shell command will never run, for any target. That's because that style of variable is a recursively expanding variable, so it will be expanded (and the shell command run) every time the variable is used.
If you change the last action in the Makefile to
target1:
echo $(DEFAULT_VALUE)
echo $(DEFAULT_VALUE)
then it will run TWICE when you make target1, echoing potentially two different things
If you want the shell command to run only once, you need to use := to set it. But if you do that, it will be run when the Makefile is read (before its even considering which targets to build), so it will run regardless of which target you eventually specify.
If you want something that will only run when a given target is built, you need to put it in the actions for that target. The easiest way to do that is with a recursive make call
target1:
read -p "Enter DEFAULT_VALUE to set: " value && \
$(MAKE) real_target1 DEFAULT_VALUE=$$value

Related

How to run post build commands in meson?

How can I do in meson to run a command after building a target?
Eg. I have an executable:
executable('target.elf', 'source1.c', 'source2.c')
And after target.elf built I want to execute a command (eg. chmod -x target.elf) on it.
I tried custom_target(), but that requires an output. I don't have new output, I just have target.elf. I tried run_command() but I didn't know how to execute it after the building.
executable now has an argument install_mode (added 0.47.0) to specify the file mode in symbolic format and optionally the owner/uid and group/gid for the installed files.
I just noticed that yasushi-shoji has provided this answer already.
The following code should do.
project('tutorial', 'c')
exec = executable('target.elf', 'main.c', build_by_default : false)
custom_target('final binary',
depends : exec,
input : exec,
output : 'fake',
command : ['chmod', '+x', '#INPUT#'],
build_by_default : true)
Note that because I want to always run the fake target, I'm using custom_target(). However, the command chmod + x demo doesn't generate the file fake specified in custom_target(), successive ninja command will always run the target.
If you don't want this behaviour, there are two ways:
You can write a script which chmod the target.elf and then copies it to target, thus effectively creates the target file. Make sure to change the output file in the meson.build if you do so.
If you don't mind typing ninja chmod instead of ninja, you can use run_target().
# optional
run_target('chmod',
command : ['chmod', '+x', exec])
Another alternative is to use install_mode for executable().
Also note that you should always use find_program() instead of plain chmod. This example doesn't use it for simplicity.

In CMake, have the Makefile not print anything if no actions were actually taken

I use CMake in my project. Often I run a make before firing a test script, so that if the code has changed I don't test an outdated binary. If I see lines about compiling or linking etc I know something has changed. But if nothing has changed, I still see:
[100%] Built target foo
And I would really rather not see that at all. I mean, I don't want a silent build altogether - just in case no actions are necessary. How can I do that?
PS - Here's the result of make -rRd | grep 'Must remake':
Must remake target 'cmake_check_build_system'.
Must remake target 'all'.
Must remake target 'CMakeFiles/tester.dir/all'.
Must remake target 'CMakeFiles/tester.dir/depend'.
Must remake target 'CMakeFiles/tester.dir/build'.
Must remake target 'all'.
Must remake target 'default_target'.
but nothing is actually done.
I use the following
make2() {
# Exclude any output that contains 'Built target'
make $# | grep -v 'Built target';
echo '-'
}
Then
make2 my_target_generated_by_cmake

Variables expansion in Make

Let us assume I have the following Makefile
N := 1
.PHONY : target_$(N)
target_$(N) :
#echo $(N)
N := 2
.PHONY : target_$(N)
target_$(N) :
#echo $(N)
The result is:
$ make target_1
2
$ make target_2
2
Is there any way of achieving what I want? My idea was to copy-paste a recipe using a variable as parameter. The variable has to be expanded in the target name and in the recipe for that target, perhaps it could appear in the dependencies too, but not for now. The problem is that recipe expansion seems to occur at the time of recipe execution, but I was expecting, and needing, it to occur at the same time that target expansion.
Another option is to use target-specific variables:
N := 1
.PHONY : target_$(N)
target_$(N) :
#echo $(N)
target_$(N) : N := $(N)
Instead of all the ugly nested function invocations, I'd say KEEP IT SIMPLE and use a command line variable as a parameter for just a single target.
target:
#echo ${N}
#if test ${N} -eq 1; then DO THIS; else DO THAT; fi
and call your Makefile with
make target N=1
That way you have all the power of the shell's control constructs. And your Makefile remains fully portable to non-GNU make.
I can achieve the result you want with automatic variables and a pattern rule:
target_%:
#echo $*
Output:
$ make target_1
1
$ make target_2
2
To make the value available in the prerequisites list you need the .SECONDEXPANSION special target:
.SECONDEXPANSION:
target_%: prerequisite_$$*
#echo $<
prerequisite_%:
#echo "do something here to build prerequisite nb.$*"
Output:
$ make target_1
do something here to build prerequisite nb.1
prerequisite_1
The expansion of make variables occurs when necessary. In target lines, such as target_$(N):, that occurs as the line is read. In command (recipe) lines, it occurs when the action is executed. Thus, you can change the value of N as the file is read, but the final value is the one that will be used when the command is executed, hence the value 2 appears twice. (Doing that will lead to confusion; don't use it in production makefiles.)
AFAIK, there isn't a way to change that behaviour. You can't freeze the macro set at the point where the command line is read. Some of the macros may not have been defined until after they were used in a command line, and you need to be able to change the values of the macros used by the default rules, etc.
I have found that the best way to do what I wanted was to stripe the parameter from the target names themselves:
TARGETS := target_1 target_2
.PHONY : $(TARGETS)
$(TARGETS) :
$(eval $#_N := $(subst target_,,$#))
#echo $($#_N)
If the target names become too complicated, one may programatically create them from the parameter set to start with.

make targets depend on variables

I want (GNU) make to rebuild when variables change. How can I achieve this?
For example,
$ make project
[...]
$ make project
make: `project' is up to date.
...like it should, but then I'd prefer
$ make project IMPORTANTVARIABLE=foobar
make: `project' is up to date.
to rebuild some or all of project.
Make wasn't designed to refer to variable content but Reinier's approach shows us the workaround. Unfortunately, using variable value as a file name is both insecure and error-prone. Hopefully, Unix tools can help us to properly encode the value. So
IMPORTANTVARIABLE = a trouble
# GUARD is a function which calculates md5 sum for its
# argument variable name. Note, that both cut and md5sum are
# members of coreutils package so they should be available on
# nearly all systems.
GUARD = $(1)_GUARD_$(shell echo $($(1)) | md5sum | cut -d ' ' -f 1)
foo: bar $(call GUARD,IMPORTANTVARIABLE)
#echo "Rebuilding foo with $(IMPORTANTVARIABLE)"
#touch $#
$(call GUARD,IMPORTANTVARIABLE):
rm -rf IMPORTANTVARIABLE*
touch $#
Here you virtually depend your target on a special file named $(NAME)_GUARD_$(VALUEMD5) which is safe to refer to and has (almost) 1-to-1 correspondence with variable's value. Note that call and shell are GNU Make extensions.
You could use empty files to record the last value of your variable by using something like this:
someTarget: IMPORTANTVARIABLE.$(IMPORTANTVARIABLE)
#echo Remaking $# because IMPORTANTVARIABLE has changed
touch $#
IMPORTANTVARIABLE.$(IMPORTANTVARIABLE):
#rm -f IMPORTANTVARIABLE.*
touch $#
After your make run, there will be an empty file in your directory whose name starts with IMPORTANTVARIABLE. and has the value of your variable appended. This basically contains the information about what the last value of the variable IMPORTANTVARIABLE was.
You can add more variables with this approach and make it more sophisticated using pattern rules -- but this example gives you the gist of it.
You probably want to use ifdef or ifeq depending on what the final goal is. See the manual here for examples.
I might be late with an answer, but here is another way of doing such a dependency with Make conditional syntax (works on GNU Make 4.1, GNU bash, Bash on Ubuntu on Windows version 4.3.48(1)-release (x86_64-pc-linux-gnu)):
1 ifneq ($(shell cat config.sig 2>/dev/null),prefix $(CONFIG))
2 .PHONY: config.sig
3 config.sig:
4 #(echo 'prefix $(CONFIG)' >config.sig &)
5 endif
In the above sample we track the $(CONFIG) variable, writing it's value down to a signature file, by means of the self-titled target which is generated under condition when the signature file's record value is different with that of $(CONFIG) variable. Please, note the prefix on lines 1 and 4: it is needed to distinct the case, when signature file doesn't exist yet.
Of course, consumer targets specify config.sig as a prerequisite.

How to get fully expanded variables out of configure?

I created a configure.ac file like this:
AC_INIT()
set
the purpose of this is to print every available environment variable the configure script creates using set, so I do this:
user#host:~$ autoconf
user#host:~$ ./configure
which prints a bunch of variables like
build=
cache_file=/dev/null
IFS='
'
LANG=C
LANGUAGE=C
datarootdir='${prefix}/share'
mandir='${datarootdir}/man'
no_create=
So far so good.
The problem is:
I want to expand the variables like ${prefix}/share - but piping
everything to a file example.sh and executing it using bash doesn't work, because bash complains about modifying read-only variables like UID and expansion itself doesn't seem to work either.
I tried using a makefile for this where expansion works, but it complains about newlines in strings, like in the above output the line IFS=' causes an error message Makefile:24: *** missing separator. Stop.
Does anyone have an idea how to get a fully expanded version of configure's output?
The Autoconf manual (I cannot recall or find exactly where) recommends to "manually" do such a kind of variable substitution from within a Makefile.in (or .am if you happen to use Automake):
Makefile.in
[...]
foo.sh: foo.sh.in
$(SED) \
-e 's|[#]prefix#|$(prefix)|g' \
-e 's|[#]exec_prefix#|$(exec_prefix)|g' \
-e 's|[#]bindir#|$(bindir)|g' \
-e 's|[#]datarootdir#|$(datarootdir)|g' \
< "$<" > "$#"
[...]
foo.sh.in
#!/bin/sh
datarootdir=#datarootdir#
du "$datarootdir"