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.
Related
Newbie question for Makefiles... why doesn't this work?
TARGET=$#
$(TARGET): * **/*
#echo "TARGET=$(TARGET)"
Where this does?
TARGET=my_target
$(TARGET): * **/*
#echo "TARGET=$(TARGET)"
When started with make my_target?
Result of the former is, "no rule to make target `my_target'."
In addition to the question "why this doesn't work," is there a workaround? I'd like to be able to specify an arbitrary target from the command line. I suppose I could react to an env var, but that makes the CLI clunky, e.g., make target=my_target build or similar.
I've searched, but I'm not getting the right hits to solve this. GNU make 3.81. Thanks!
The automatic variable $# is defined in the context of a pattern rule; outside of any rule it has no value.
If you want Make to do the same thing to whatever target you name, you can use a match-anything rule:
%:
#echo TARGET=$#
Is it possible for gcc make to test if a define exists in one of the source files ie.
ifeq (DEFINED_BLAH_BLAH,3)
#echo is equal to 3
else
#echo is not equal to 3
endif
I've looked at this and to expend on the suggestion from the comment, you could do the following. Not elegant, probably not the best available solution, but it works:
echo -ne "#if 1==MYVAL\n0\n#else\n1\n#endif\n" | cpp -P -imacros test.h
Or to call it through gcc or g++:
echo -ne "#if 1==MYVAL\n0\n#else\n1\n#endif\n" | \
gcc -Xpreprocessor -P -E -imacros test.h -
These would return shell style 0 (true for MYVAL defined in test.h and being 1) or 1 on stdout which you could test for in make / shell.
You may also want to strip all blank lines appending | grep -v '^$'.
To elaborate a bit more on the above. I create (echo) as simple file that I run through the preprocessor which check for equality on given macro and results in 0 or 1 being in the output. -P for cpp, because it's not really a C file and we do not need any of the extra bits in the output. -imacros means retain the macros defined in that file, but discard any output generated by processing it.
It should also be noted, if you'd have any conditional definitions needed to consider any defines passed to the compiler, you would need to pass them to this cpp run as well.
Also note whether you test.h being your header file you know should include the macro defintion. Or main.c being a source file including that (and other) headers doesn't really matter / yields the same result (whatver the value was when cpp was done with the file read and (pre)processed for -imacros.
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
I have a lot of custom CMake commands, so I end up with a lot of repetition of this pattern in build scripts, e.g.
set(PREREQ ${CMAKE_CURRENT_SOURCE_DIR}/foo.txt ${CMAKE_CURRENT_SOURCE_DIR}/bar.txt)
add_custom_command(
OUTPUT baz.txt
COMMAND cat ${PREREQ} > baz.txt
DEPENDS ${PREREQ}
)
add_custom_target(a ALL DEPENDS baz.txt)
Are there equivalents of GNU Make automatic variables in CMake ($#, $<, etc) so I can avoid specifying inputs/outputs twice (dependencies, output, and command)?
How else can I DRY it up?
How about using custom functions? For your example script this could look like this:
function (add_custom_command_with_target _targetName _output)
add_custom_command(
OUTPUT ${_output}
COMMAND cat ${ARGN} > ${_output}
DEPENDS ${ARGN}
)
add_custom_target(${_targetName} ALL DEPENDS ${_output})
endfunction()
The function can be invoked in the following way:
add_custom_command_with_target(a baz.txt ${CMAKE_CURRENT_SOURCE_DIR}/foo.txt ${CMAKE_CURRENT_SOURCE_DIR}/bar.txt)
In the function body you can use the predefined variable ARGN, which holds the list of arguments past the last expected argument. This is the closest thing you can get to GNU Make's predefined 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.