I want to write a Makefile which would run tests. Test are in a directory './tests' and executable files to be tested are in the directory './bin'.
When I run the tests, they don't see the exec files, as the directory ./bin is not in the $PATH.
When I do something like this:
EXPORT PATH=bin:$PATH
make test
everything works. However I need to change the $PATH in the Makefile.
Simple Makefile content:
test all:
PATH=bin:${PATH}
#echo $(PATH)
x
It prints the path correctly, however it doesn't find the file x.
When I do this manually:
$ export PATH=bin:$PATH
$ x
everything is OK then.
How could I change the $PATH in the Makefile?
Did you try export directive of Make itself (assuming that you use GNU Make)?
export PATH := bin:$(PATH)
test all:
x
Also, there is a bug in you example:
test all:
PATH=bin:${PATH}
#echo $(PATH)
x
First, the value being echoed is an expansion of PATH variable performed by Make, not the shell. If it prints the expected value then, I guess, you've set PATH variable somewhere earlier in your Makefile, or in a shell that invoked Make. To prevent such behavior you should escape dollars:
test all:
PATH=bin:$$PATH
#echo $$PATH
x
Second, in any case this won't work because Make executes each line of the recipe in a separate shell. This can be changed by writing the recipe in a single line:
test all:
export PATH=bin:$$PATH; echo $$PATH; x
By design make parser executes lines in a separate shell invocations, that's why changing variable (e.g. PATH) in one line, the change may not be applied for the next lines (see this post).
One way to workaround this problem, is to convert multiple commands into a single line (separated by ;), or use One Shell special target (.ONESHELL, as of GNU Make 3.82).
Alternatively you can provide PATH variable at the time when shell is invoked. For example:
PATH := $(PATH):$(PWD)/bin:/my/other/path
SHELL := env PATH=$(PATH) /bin/bash
Path changes appear to be persistent if you set the SHELL variable in your makefile first:
SHELL := /bin/bash
PATH := bin:$(PATH)
test all:
x
I don't know if this is desired behavior or not.
What I usually do is supply the path to the executable explicitly:
EXE=./bin/
...
test all:
$(EXE)x
I also use this technique to run non-native binaries under an emulator like QEMU if I'm cross compiling:
EXE = qemu-mips ./bin/
If make is using the sh shell, this should work:
test all:
PATH=bin:$PATH x
To set the PATH variable, within the Makefile only, use something like:
PATH := $(PATH):/my/dir
test:
#echo my new PATH = $(PATH)
Related
I am working on a Makefile that gets executed in a Linux environment and Windows environment (through MINGW64). The script has a variable which is pointing to the shell command which should be used:
SHELL:=/usr/bin/env bash
Later on, that variable is being used to run a shell script:
${SHELL} ./utils/tests.sh
On Linux that works just fine but on Windows with MINGW64 (Git Bash) it fails because /usr/bin/env is being replaced with C:/Program Files/Git/usr/local and there is a space character in "Program Files" which breaks the path in the Makefile.
To solve the issue on Windows, I can put ${SHELL} in quotes to make sure that the space character is kept:
"${SHELL}" ./utils/tests.sh
However, this is breaking on Linux environments because it becomes a string now and "/usr/bin/env bash" is not executable.
So I came up with this solution:
ifeq ($(OS),Windows_NT)
"${SHELL}" ./utils/tests.sh
else
${SHELL} ./utils/tests.sh
endif
That's now working in both environments (Linux & MINGW64) but it comes with the downside of having 5 lines of code everywhere where I want to run a script. Ideally, I would like to assign the variable just once. I tried this but it doesn't work either:
ifeq ($(OS),Windows_NT)
SHELL:="/usr/bin/env" bash
else
SHELL:=/usr/bin/env bash
endif
test: clean build
${SHELL} ./utils/tests.sh
Does anyone have a clever solution to my problem?
You could replace:
SHELL:=/usr/bin/env bash
with one of these (or with whatever path directly leads to Bash's executable):
SHELL:=/bin/bash
SHELL:=/usr/bin/bash
SHELL:=bash
which would eliminate the space from the equation.
That being said, you should also be able to run the commands directly, i.e.:
./utils/tests.sh
instead of:
${SHELL} ./utils/tests.sh
which would eliminate the need to set and use variable SHELL altogether.
I found in a makefile the following commands:
$(var):
mkdir -p $(#D)
What is the meaning of this command?
$(VAR) expands to the value of the variable VAR. This is a Make variable (not a shell etc variable). For example, if earlier in your Makefile you define
VAR=ick/poo
then VAR expands to ick/poo, and #D in your recipe expands to the directory part, ick.
As you seem to be confused about the relationship between shell and make, I should perhaps point out that these are two different languages, though in a Makefile, you will encounter both; the recipes - the parts which are indented by a tab - will be passed to a shell for evaluation (though normally the shell will be /bin/sh, not Bash, unless you specifically override the Make variable SHELL to force it).
In the shell, by the way, the superficially similar construct $(cmd) performs a command substitution; that is, the command cmd will be evaluated and its output will be inserted as text. So for example,
echo Running in $(pwd)
will print
Running in /home/you
if executed in the directory /home/you (the command pwd prints out your current working directory). ... Though in a Makefile, the dollar sign will normally be evaluated and consumed by make itself; so to pass a literal dollar sign to the shell, you have to double it.
test:
echo Running in $$(pwd)
As already explained by #tripleee $(var) expands to the variable. Because it is here listed before a colon it means that it is a target in a Makefile.
For $(#D) see 10.5.3 Automatic Variables in the make manual:
The directory part of the file name of the target, with the trailing slash removed. If the value of ‘$#’ is dir/foo.o then ‘$(#D)’ is dir. This value is . if ‘$#’ does not contain a slash.
NOTE: This is NOT a shell script. This is a makefile. Please use "man make" for a description about what "make" does.
I'm on a customized Linux distribution.
I'm trying to print from a Qt project file (using message() directive) an environment variable which is correctly printed in the shell. When qmake runs from the same shell instance, in the printed message is looks like the variable is empty.
I'm using the command message($$(ENVI_VAR)) which according to the Qt website, should get the value of the variable when qmake runs (there is also the $(ENVI_VAR) syntax which is instead evaluated when the Makefile runs)
What am I missing?
Thanks
Edit:
Actually, not only qmake, but every process I run, also a script execution, can't see the environment variables. Only the shell can.
I had only defined the variables in the .bashrc file, but I was missing the export
Usually global variables values are used as custom variables inside the .pro files. I'm putting here reference to Qt doc with specific details how to do this:
"Variables can be used to store the contents of environment variables. These can be evaluated at the time that qmake is run, or included in the generated Makefile for evaluation when the project is built.
To obtain the contents of an environment value when qmakeis run, use the $$(...) operator:
DESTDIR = $$(PWD)
message(The project will be installed in $$DESTDIR)
In the above assignment, the value of the PWD environment variable is read when the project file is processed.
To obtain the contents of an environment value at the time when the generated Makefile is processed, use the $(...) operator:
DESTDIR = $$(PWD)
message(The project will be installed in $$DESTDIR)
DESTDIR = $(PWD)
message(The project will be installed in the value of PWD)
message(when the Makefile is processed.)
In the above assignment, the value of PWD is read immediately when the project file is processed, but $(PWD) is assigned to DESTDIR in the generated Makefile. This makes the build process more flexible as long as the environment variable is set correctly when the Makefile is processed."
In message() need to use different notation for some reason:
message(DESTDIR: ($$DESTDIR))
I had such problem today. Solved only by looking at qmake .pro files examples in Qt mailing list. Another possible problem - clue variables:
# ${VAR} notation allows to append the contents of the variable to another value
# without separating the two with a space
isEmpty(DESTDIR): DESTDIR = $${IDE_BUILD_TREE}/lib/qtcreator/plugins
Without such notation I got empty DESTDIR variable.
I want to use api-easy to test my REST app. I have it in the dependences inside the package.json, so when I run npm install it's installed in ./node_modules
I'm trying to add the api-easy to the path like
this question.
Since I'm using a Makefile I have this:
test:
#PATH="./node_modules/api-easy/node_modules/.bin:$PATH"
#echo $PATH
vows
#node ./test/tests.js
Note: api-easy depends on vows
The PATH var in not being updated, when I do the echo it returns me "ATH"(not the value), and then the command vows in not found.
How can I set properly the PATH in a Makefile?
In a make recipe, each command is executed as a separate process, so setting an environment variable in one command will not affect the others. To do what you want, you need to make sure all the related commands run in a single instance of the shell, where environment variables are passed as you would expect:
test:
#PATH="./node_modules/api-easy/node_modules/.bin:$$PATH"; \
echo $$PATH; \
vows; \
node ./test/tests.js
The trailing backslash tells make to concatenate a line with the one that follows it. Note also that you need to quote $ characters if you want them interpreted by the shell. Hence the $$.
I think something like this should do it:
export PATH="./node_modules/api-easy/node_modules/.bin:$PATH"
test:
vows
#node ./test/tests.js
I am trying to use setenv variable in my makefile but when I execute my make file it gives setenv: command not found.
How can I use it?
Actually I wanted to run a shell script which sets multiple environment variables.
Since the list is very huge I dont have an option except to use the scripts. I cant set them manually like
abcd:= /xx/yy/zz
Please suggest.
P.S. the same command
setenv xxx yyy works very well in shell
it just fails when I use in makefile directly or makefile with a script having this command.
'
Why do not you use export command ?
Running the script to set the environment variable will not work as the shell run a separate process & will not reflect in your current shell. You will need to source the shell script. You can use source or . based on your shell. Following is a sample for your reference where setvar.sh sets a variable & print.sh prints it; in the Makefile (mkfile) setvar.sh is being sourced using .
$ cat setvar.sh
export TEST=ABC
$ cat print.sh
echo $TEST
$ cat mkfile
test:
. ./setvar.sh && ./print.sh
.SILENT:test
$ make -f mkfile
ABC
You can also include I guess for example,
$ cat mkfile2
include setvar.sh
test:
./print.sh
.SILENT:test
$ make -f mkfile2
ABC
Hope this helps!
Look at
make -e
and Communicating Variables to a Sub-make
I think setenv is not a builtin to the sh shell. If you are using GNU Make that is the default shell used. In your situation you probably want to use a different shell, like bash. You do this by setting the SHELL variable in the makefile to what you want like:
SHELL := /usr/bin/bash
For more information checkout this section of the GNU Make manual. It details the different behavior of the SHELL variable and how it is, or isn't inherited from the shell make is invoked from on different platforms.
EDIT: I agree with the implication of the other posters that you are probably not setting enviroment variables the way you think you should be and would not be using the setenv command at. I am just responding to your original question. To learn about variables in make files checkout these other sections in the GNU Make manual.
export MY_VAR := "/package/your_path"