Makefile target depend on file from environment variable - makefile

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: $^

Related

How can I pass a argument to a makefile dependency?

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).

Makefile target name compared to string

In a makefile I'm trying to compare the target name with a string, and depending on this set a variable with a string or another.
This example illustrates what I'm trying to do:
ifeq ($#,"Target_A")
THE_PATH="Path_a"
THE_TARGET=$#
else
THE_PATH="Path_b"
THE_TARGET=$#
endif
Target_A:
#echo $(THE_PATH)
#echo $(THE_TARGET)
Target_B:
#echo $(THE_PATH)
#echo $(THE_TARGET)
This is the output when I call make passing Target_A and when I call it passing Target_B:
$ make Target_A
Path_b
Target_A
$ make Target_B
Path_b
Target_B
The fact that I always get "Path_b" indicates the ifeq always evaluates to false, but you can see that $# contained the right string.
Why doesn't this work?
You probably want target-specific variables:
Target_A: THE_PATH="Path_a"
Target_A:
#echo $(THE_PATH)
Since contents of a (regular) variable are expanded each time it's used, THE_TARGET=$# can be made global.
Target-specific variables are only accesible in a target they belong to, and its dependencies.
Normally this is enough, but if you need to have global variables, you can use the same code you have in the question, with the condition changed to this:
ifneq ($(filter Target_A,$(MAKECMDGOALS)),)
$# (which you tried to use) only works inside of a recipe, and expands to a target name that the recipe builds.
$(MAKECMDGOALS) is a global variable that contains all targets specified (as command-line parameters) when invoking make.
This option will only work if the target you're looking for was specified as a command-line parameter.

Makefile target global variable

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.

make: trigger without rebuilding dependency

Basically I have the usual Makefile construct:
target: dependency1 dependency2 dependency3
runtargetscript.sh
However in this case, the target only needs one of the dependencies and some dependencies may not be buildable. (so I cannot just build all dependencies)
Is it possible to tell make to trigger "target" when one of the dependencies changed/was created (i.e. normal behaviour) but NOT to try to rebuild any missing dependencies?
With GNU make you can use shell escapes to build the dependencies dynamically, adding them only if they already exist:
if_exist = $(shell if [ -e $(1) ]; then echo $(1); fi)
target: $(call if_exist,dependency1) $(call if_exist,dependency2) $(call if_exist,dependency3)
runtargetscript.sh
This will run the script if target does not exist, or if it is older than any of the dependencies that do exist at the time the makefile was read, but will not attempt to build them if they do not exist at that time.
Note the important caveat there -- if the file(s) do not exist, but some other unrelated rule runs an action that creates them, it won't rebuild target, unless you rerun make target again.
Assuming your rule body does not do anything special depending on which dependencies are newer than the target you can use the -W flag to make to instruct it to consider certain targets as always new (and thus not in need of building).
So for the given example assuming you can (and want) to build dependency2 but not dependency1 or dependency3 you would run:
make -W dependency1 -W dependency3 target
Edit: As pointed out in the comments this does not work correctly when dependency2 is not newer than target as target will still be built.
In that case I believe the only solution (given the comments below) is to use something like:
DEPENDENCY_BIN := $(or $(wildcard /path/to/mysql),$(wildcard /path/to/sqlite3),/path/that/does/not/exist)
target: $(DEPENDENCY_BIN) dependency2

How does "make" app know default target to build if no target is specified?

Most Linux apps are compiled with:
make
make install clean
As I understand it, the make command takes names of build targets as arguments. So for example install is usually a target that copies some files to standard locations, and clean is a target that removes temporary files.
But what target will make build if no arguments are specified (e.g. the first command in my example)?
By default, it begins by processing the first target that does not begin with a . aka the default goal; to do that, it may have to process other targets - specifically, ones the first target depends on.
The GNU Make Manual covers all this stuff, and is a surprisingly easy and informative read.
To save others a few seconds, and to save them from having to read the manual, here's the short answer. Add this to the top of your make file:
.DEFAULT_GOAL := mytarget
mytarget will now be the target that is run if "make" is executed and no target is specified.
If you have an older version of make (<= 3.80), this won't work. If this is the case, then you can do what anon mentions, simply add this to the top of your make file:
.PHONY: default
default: mytarget ;
References:
https://www.gnu.org/software/make/manual/html_node/How-Make-Works.html
GNU Make also allows you to specify the default make target using a special variable called .DEFAULT_GOAL. You can even unset this variable in the middle of the Makefile, causing the next target in the file to become the default target.
Ref: The Gnu Make manual - Special Variables
bmake's equivalent of GNU Make's .DEFAULT_GOAL is .MAIN:
$ cat Makefile
.MAIN: foo
all:
#echo all
foo:
#echo foo
$ bmake
foo
See the bmake(1) manual page.

Resources