This code doesn't work:
$ cat Makefile
dev:
AWS_PROFILE=foobar-$# echo $$AWS_PROFILE
demo:
AWS_PROFILE=foobar-$# echo $$AWS_PROFILE
Based on my target, I want the AWS_PROFILE correctly setup for the commands in the target. How does one achieve this with GNU Make 4.2.1?
Add a semi-colon before the echo command:
dev:
AWS_PROFILE=foobar-$#; echo $$AWS_PROFILE
Each line of a recipe is sent to a different shell. So, if you want to propagate shell variables you must define and use them on the same line, in the same shell list. You could also use && or || to join your individual shell commands.
Note that, for better readability, you can use the line continuation:
dev:
AWS_PROFILE=foobar-$#; \
echo $$AWS_PROFILE
Related
i have a tricky conundrum for you all!
i would like to set environment variables with a makefile. i know, that the called process cannot change the calling environment's variables, but there has to be some workaround.
in the makefile:
target:
export VAR=test
later, in the terminal:
echo $VAR
should print "test".
how can this be achieved with some kind of workaround?
thank you very much for any help!
As Andreas says, there is no "workaround" for this. It's a fundamental feature of a POSIX operating system. The only way for the parent process (e.g. a shell) to have its environment modified is by doing something different than simply run make. If you're willing to do that, then you have options.
For example, if your makefile does this:
target:
#echo 'export VAR=test'
then in your shell you can do this:
$ eval $(make target)
and now that variable will be set. Of course, this will fail miserably if your makefile prints ANYTHING except valid shell syntax so you can only do a very limited set of things.
Alternatively you can have the makefile write stuff to a file then source the file, like this:
target:
echo 'export VAR=test' > target
then:
$ make target
$ . target
Make does updates to files, so what you need to do is "export" the variable to a file, and then later in terminal call something using the content of the file. Possible convoluted solution:
-- Makefile --
target:
echo "test" > VAR
-- Terminal --
cat VAR; # Prints var content, like echo does
cat VAR | xargs echo; # Sends each token (?) as argument to echo, printing it.
# Here be dragons.
I have a function in a makefile that returns a bucket name in google cloud storage
define get_composer_bucket
gcloud beta composer environments describe --location=europe-west2 ${COMPOSER_SANDBOX_NAME} \
| grep -hnr "dagGcsPrefix"
endef
I want to use the output of this in a makefile command. How can i assign the output of this to a variable? I have tried the below, which returns an empty variable. Am i doing something wrong?
run:
BUCKET=$(call get_composer_bucket)
echo ${BUCKET}
There are many SO answers that will describe to you the difference between make variables and shell variables; knowing this difference is crucial to writing makefiles.
There are also many SO answers that will explain that each command in a recipe is run in a different shell, and that variables set in one shell can't be passed to another shell, so setting a variable in one recipe command line can't be seen in another recipe command line (you can use \ to combine multiple physical lines into a single logical line, but you probably need to add ; as well to satisfy the shell).
And, there are many answers discussing how a makefile is not a shell script (although it can CONTAIN shell scripts) so you can't just write a shell script anywhere in a makefile and have it evaluated.
The simplest thing to do for you is to set a make variable using the make shell function to run the shell script:
BUCKET := $(shell gcloud beta composer environments describe --location=europe-west2 ${COMPOSER_SANDBOX_NAME} | grep -hnr "dagGcsPrefix")
run:
echo $(BUCKET)
Of course, this is different in various ways than what you tried to do above. So maybe it doesn't meet your requirements, but you haven't actually stated what those are.
Please see below code which will help you to extract the name
define get_composer_bucket
`gcloud beta composer environments describe --location=europe-west2 ${COMPOSER_SANDBOX_NAME} \
| grep -hnr "dagGcsPrefix"`
endef
BUCKET:= $(call get_composer_bucket)
run:
#echo "BUCKET=> $(BUCKET)"
I am writing a makefile, and my shell is zsh. All is operating well until I hit an if statement. I've tried using the traditional ifeq makefile statement, but that doesn't appear to work when using zsh. So, I've tried just using zsh if statements, like the following:
if [[ "$(BUILD_ENV)" == "DEV" ]]; then
#echo dev environment
else
#echo not dev environment
fi
Every time I run it, I get the following error:
...
if [[ "DEV" == "DEV" ]]; then
zsh:1: parse error near `then'
make: *** [build] Error 1
I've tried removing the semicolon (;), putting then on a new line (like in bash syntax), but to no avail.
How can I properly perform an if else statement in a makefile, using zsh as the shell?
It's irrelevant what shell you use (consider the insanity if make always used the shell of the user who invoked it... no makefile could ever be portable between two people!)
make always uses /bin/sh. If you want to use some different shell you must explicitly set it in your makefile by adding:
SHELL := /bin/zsh
However, I don't recommend this because not every system will have zsh installed.
You should simply write your recipes to use portable POSIX syntax rather than using zsh extensions. In this case, that means using [..] instead of [[...]] and using = for equality instead of ==.
Also, remember that each logical line of a makefile is invoked in a separate shell so if you want to write an if-statement you have to use backslashes to ensure all the physical lines are combined into a single logical line:
mytarget:
#if [ "$(BUILD_ENV)" = "DEV" ]; then \
echo dev environment; \
else \
echo not dev environment; \
fi
This won't work for me. I want to make some substitution and assign it to a variable in Makefile. An example is as follows but I prefer to do it with Perl since other substitutions can be more complex than this.
eval.%:
# make eval.exp-1.ans
# $* --> exp-1.ans
folder=`echo $* | sed -e 's/\..*//g'`
# OR
folder=`echo $* | perl -ne 'm/(.*)\.ans/; print $$1'
# I want that folder will be exp-1
echo $$folder ${folder}
Why this does not work? How can I do this kind of things in Makefile?
Your question is not clear. Are you trying to set a variable in your makefile, so that other recipes, etc. can see it? Or are you just trying to set a variable in one part of your recipe that can be used in other parts of the same recipe?
Make runs recipes in shells that it invokes. It's a fundamental feature of UNIX that no child process can modify the memory/environment/working directory/etc. of its parent process. So no variable that you assign in a recipe (subshell) of a makefile can ever set a make environment variable. There are ways to do this, but not from within a recipe. However that doesn't appear (from your example) to be what you want to do.
The next thing to note is that make runs each logical line of the recipe in a different shell. So shell variables set in one logical line will be lost when that shell exits, and the next logical line cannot see that value. The solution is to ensure that all the lines of your recipe that need access to the variable are on the same logical line, so they'll be sent to the same shell script, like this:
eval.%:
folder=`echo $* | perl -ne 'm/(.*)\.ans/; print $$1' || exit 1 ; \
echo $$folder
Have you tried the $(VARIABLE:suffix=replacement) syntax? In your case, $(*:.ans=). That will work for any suffix, even if it doesn't start with a dot.
So, to compile my executable, I need to have the library locations set up correctly. The problem is, the setup comes from a bunch of scripts that do the env variable exporting, and what needs to be set up may change (beyond my control) so I need to use those scripts instead of copying their functionality. To compile in regular command line, I need to do something like:
setup library1
setup library2
source some_other_setup_script.bash
g++ blah.c
# setup is a executable on my system that run some scripts
How would I write a makefile that accomplishes that? As far as I tried, the env variable exporting does not carry over (i.e. "export VAR=remember; echo $VAR" won't work)
You can also add environment variables properly with the machinery of GNU make, like so:
export TEST:="Something Good!"
test:
echo $$TEST
This (I think) has different semantics from:
TEST2:="Something not quite so useful?"
test2:
echo ${TEST2}
Which (again, I think) does the substitution within make before passing along to the shell. Note that the export command doesn't work within a target block, just unindented as an immediately executed command.
If variable exporting is not working the way it does on your command line, that suggests that Make is choosing a shell different from the one you're using, with different syntax for handling variables (export VAR=remember; echo $VAR works fine for me). Make uses /bin/sh by default, but you can override this with the SHELL variable, which Make does not import from the environment. I suggest setting SHELL (in the Makefile) to whatever you're using in your environment and trying the export VAR=remember experiment again.
Ultimately you will need to define the variable and execute the compiler in a shell list or even a script, rather than in separate make commands. There are a couple of refinements you could add, however. You could tell make about the script:
maintarget: script.sh blah.c
source script.sh; g++ blah.c
script.sh:
setup include script here
Another thing would be to just execute all that stuff in the same shell
maintarget: blah.c
run this; run that; run the other thing; g++ blah.c
I believe all make versions will run a ; list in the same shell, but you can always force a subshell with (list) or by calling specifically a shell script as a compiler command wrapper.
Don't forget to have the appropriate targets depend on your scripts themselves. BTW, some make versions (pmake aka bsd make) can execute a command when defining a make variable, and all versions of make then exports those. But I don't think gmake can do that.
You could write another shell script that executes all those commands, then prints out variable assignments that make can use. Run the script, pipe its output to a file, then include that file from your Makefile. For example:
Makefile:
all:
echo $(FOO)
test.mk: test.sh
./$< > $#
include test.mk
test.sh
echo "FOO=1"
Running "make" in the directory containing this Makefile produces:
make: Entering directory `/home/luser/build/mktest'
Makefile:7: test.mk: No such file or directory
./test.sh > test.mk
make: Leaving directory `/home/luser/build/mktest'
make: Entering directory `/home/luser/build/mktest'
echo 1
1
make: Leaving directory `/home/luser/build/mktest'
make creates test.mk by running the shell script, then includes it. test.mk contains the output of test.sh, and is parsed as a Makefile. See http://www.gnu.org/software/make/manual/make.html#Include for more details.
We use a variant of this in Mozilla's client.mk to let you define options in a "mozconfig" file:
http://mxr.mozilla.org/mozilla-central/source/client.mk#138
Restatement: How do I get a shell variable into a make file?
Something like:
MYVAR := $(shell echo $(MYVAR)) <any_makefile_additions_here>
So, this defines MYVAR inside a MAKEFILE when an environment variable named MYVAR is also set.
It might be of interest, that, in order to override an option that is already defined in a makefile, make supports (I am referring to GNU Make 3.82, but other version probably too) the option -e.
Example:
Makefile:
CC=gcc
...
Run make:
CC=gcc-4.7
make -e
will use gcc-4.7 instead of gcc.