I would like to have something like that:
PrintTarget:
#echo Building $(TARGET)
SetRelTarget: TARGET = Release
SetRelTarget:
#echo Target is set.
BuildRel: SetRelTarget PrintTarget
But TARGET variable set in SetRelTargetAs is not a global.
My question is:
Is it possible to modify global variables inside the rule and use this modified value outside this rule?
Thank you.
Set TARGET to release in BuildRel, then it should apply to all its prerequisites (although it's not global),
PrintTarget:
#echo Building $(TARGET)
#SetRelTarget: TARGET = Release
SetRelTarget:
#echo Target is set to $(TARGET).
BuildRel: TARGET = Release
BuildRel: SetRelTarget PrintTarget
Since there is nothing variable in the code you can simply do this:
TARGET = Release
PrintTarget:
#echo Building $(TARGET)
BuildRel: PrintTarget
Demo:
$ make BuildRel
Building Release
$ make TARGET=foo BuildRel
Building foo
If your use case is more complex please include more details.
Related
I have the following Makefile where the all make target depends on a separate setup make target that also takes an argument. However when I make all the setup target is not invoked with the argument
setup:
...command
clean:
...command
all: setup myarg=value clean myarg=value
#echo "setup & clean"
I think what you're asking is if a prerequisite can inherit a target-specific variable. In which case, yes it can -- Note, in your example you tried to intersperse the target specific variables and the prerequisites, which you can't do. But beware -- this has sharp sticks attached. Consider the following makefile:
all:
setup:
#echo "building $#: myarg=$(myarg)"
all: myarg:=value
all: setup
#echo "building $#: myarg=$(myarg)"
blah: setup
#echo "building $#: myarg=$(myarg)"
If I do make all, I get:
tmp> make all
building setup: myarg=value
building all: myarg=value
Which is what you want. But if I do make blah, then setup is run as a prerequisite of blah, and does not have the value set as you might expect. It will not be rebuilt for main, even though the variable is different:
tmp> make blah all
building setup: myarg=
building blah: myarg=
building all: myarg=value
See the make manual for more details
The command line of the make program is not free-form. You can't just pass it a bunch of stuff and have that "stuff" passed through make to appear somehow inside your recipes. make can only take arguments that it's defined to take: all arguments (not options) are either targets or variable assignments. See the documentation or the man page.
It is not possible in general to pass arbitrary values on the make command line. However, as I said, make does allow variables to be set on its command line.
If you run make setup myarg=value then this will set the make variable myarg to have the value value, and ask make to build the setup target.
So, if you write your makefile:
setup:
...command $(myarg)
referencing the make variable myarg, then it will "work" (I guess, you haven't made clear exactly what you want to run using myarg).
I have inherited a large branched project? that requires a volatile set of .a archives $(LIB_FILES) to be included into link target, located in some directories $(LIB_DIRS). I can write an expression like this:
LIBDEP = $(foreach ldir, $(LIB_DIRS), \
$(filter $(addprefix %/, $(LIB_FILES)), $(wildcard $(ldir)/* )))
The problem is that they might not exist at moment of make's invocation and would be built by invoking $(MAKE) inside of another target's rule, which is a prerequisite to the link step.
The problem is actual list of files that should be created varies on external factors determined at their build steps, that I can't hard-code it properly, without turning makefile into a spaghetti mess and said variable is not re-evaluated at the moment of link command invocation.
I have suspicion that $(eval ) function can be used somehow, but manual is not very forthcoming as well as I didn't found examples of its use in this way.
Toolchain: GCC and binutils, make 3.81
Another solution is to create an explicit dependency of your make script on the output of the step which currently creates the variable $(LIB_FILES). This is what the manual is dealing with in the chapter How makefiles are remade and it aims at the technique which make is best at, namely deriving dependencies from the existence and timestamp of files (instead of variables). The following hopefully depicts your situation with the process of deducing a new set of libraries simulated by the two variables $(LIBS_THIS_TIME) and $(LIB_CONFIG_SET).
LIBS_THIS_TIME = foo.a:baz.a:bar.a
LIB_CONFIG_SET = $(subst :,_,$(LIBS_THIS_TIME))
include libdeps.d
linkstep:
#echo I am linking $^ now
touch $#
libdeps.d: $(LIB_CONFIG_SET)
-rm libdeps.d
$(foreach lib,$(subst :, ,$(LIBS_THIS_TIME)),echo linkstep: $(lib) >> libdeps.d;)
$(LIB_CONFIG_SET):
touch $#
If make finds that libdeps.d is not up to date to your current library configuration it is remade before make executes any other rule, although it is not the first target in the makefile. This way, if your build process creates a new or different set of libraries, libdeps.d would be remade first and only then make would carry on with the other targets in your top makefile, now with the correct dependecy information.
It sometimes happens that you need to invoke make several times in succession. One possibility to do this is to use conditionals:
ifeq ($(STEP),)
all:
<do-first-step>
$(MAKE) STEP=2 $#
else ifeq ($(STEP),2)
all:
<do-second-step>
$(MAKE) STEP=3 $#
else ifeq ($(STEP),3)
all:
<do-third-step>
endif
In each step you can generate new files and have them existing for the next step.
Here is a Makefile that I currently use to make targets with different configurations, i.e., I am building different software packages with the same target, either all at once or individually.
.PHONY: build test %.build %.test build-all test-all
%.build %.test: PACKAGE = $*
%.build:
#echo build $(PACKAGE)
%.test:
#echo test $(PACKAGE)
build-all: a.build b.build
test-all: a.test b.test
build: $(PACKAGE).build
test: $(PACKAGE).test
I can now build all packages with make build-all or individual packages with, e.g., make build PACKAGE=a. However I would like to switch the body of the %.build and build, etc. targets as in the following:
.PHONY: build test %.build %.test build-all test-all
build:
#echo build $(PACKAGE)
test:
#echo test $(PACKAGE)
build-all: a.build b.build
test-all: a.test b.test
%.build %.test: PACKAGE = $*
$(PACKAGE).%: $*
This way, the pattern matching logic is fully separated from the "main" targets build and test that should contain the actual build commands; making the important parts of the Makefile more readable. However, the last line does not work as intended, i.e., running make a.build and thus make build-all should trigger the target build with PACKAGE=a. The variable assignment in second-last line works, the target matching in the last line does not.
Question: Is there a way to express a matching target like $(PACKAGE).%: $* or to match separate parts of a target like %.%: $2?
As MadScientist explained, the problem cannot be solved easily in GNU make. For completeness, I would like to add and explain my final and more comprehensive solution:
.PHONY: all build test clean %.build %.test build-all test-all
PACKAGES = a b c e f g
PACKAGE = a
all: clean build-all test-all
%.build %.test: PACKAGE = $*
%.build:
#echo build $(PACKAGE)
%.test:
#echo test $(PACKAGE)
clean:
#echo remove build dir
build-all: $(addsuffix .build, $(PACKAGES))
test-all: $(addsuffix .test, $(PACKAGES))
build: $(PACKAGE).build
test: $(PACKAGE).test
This solution avoids eval and foreach and is based on my initial working solution, where the dynamic %.build and %.test targets contain the actual build commands. I added the PACKAGES variable to facilitate easy addition of new packages, a default PACKAGE to prevent execution of misconfigured build commands, and the common targets all and clean as complements.
From the command line, you just call make all, clean, build PACKAGE=x, build-all, etc., i.e., only the static targets, which will then trigger the build commands in the dynamic targets. The static targets and the two variables are also visible in the Bash/Zsh auto-completion.
I think this is the most flexible and yet readable way to build multiple dynamic targets.
First you probably want:
$(PACKAGE).% : %
not using $*, which is an automatic variable and so it has no value except inside the recipe; you can't use it like that in the prerequisite lists.
Second, you can't do this in GNU make. A pattern rule with no recipe doesn't just a create prerequisite relationship, like an explicit rule would do; instead it deletes the pattern rule. Since you didn't have a pattern rule for $(PACKAGE).% yet, this is basically a no-op. Also, target-specific variables are only available inside the recipe, so trying to use $(PACKAGE) in the target definition and expecting it to take the value from some previously set target-specific variable cannot work.
You could do something like this, but it's not fully dynamic (you still need the list of packages and types):
PACKAGES = a b
TYPES = build test
$(foreach T,$(TYPES),$(eval $(addsuffix .$T,$(PACKAGES)): $T))
Is there a way to use the native Makefile if-else conditional and also have it respects target-specific variables re-assignments?
Example Makefile:
#!/usr/bin/make
CONFIG = Debug
.PHONY: test printme
test: override CONFIG=Release
test: printme
#echo "Done."
printme:
ifeq "$(CONFIG)" "Debug"
#echo "should be DEBUG -> $(CONFIG)"
else
#echo "should be RELEASE -> $(CONFIG)"
endif
Running make test prints the following output:
should be DEBUG -> Release
Done.
The output I'm looking for is should be RELEASE -> Release how can I achieve that? Do I need to use shell conditionals instead?
This behavior seems logical to me: At the time of parsing the Makefile, CONFIG is defined as Debug. The ifeq conditional uses the value of CONFIG it knows at that time. Therefore it chooses the ifeq branch that outputs "Should be DEBUG".
The target-specific variable is only defined as Release with the target test. Its prerequisite printme also knows the target-specific variable and ouputs Release.
May I suggest that you set variable to make on the command line for the purpose you want. It's not many more characters to write when invoking make but brings all you seem to be willing.
make CONFIG=Release
if I run make like this:
make VAR=dir
is there a way to add the location pointed by the VAR variable as a target dependency? actually, I need to define a file inside that directory as a dependency.
I'd go with:
target: $(VAR)/file.txt
echo yes
but if the variable was not defined, the target will understand /file.txt, which is not what I want. I also thought about creating a phony target to check for the variable, with test, but then the phony target would be executed every time and, consequently, target also would.
any solution to that?
You haven't said what behavior you want if the variable is not defined, but this is probably what you want:
ifdef VAR
target: $(VAR)/file.txt
endif
target:
echo yes
#echo and here are the dependencies: $^