I want to get the flags passed to a make target. Currently I'm able to get the words passed to it but I would need the flags also. This is the current code:
ifeq (docker,$(firstword $(MAKECMDGOALS)))
ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
$(eval $(ARGS):;#:)
endif
.PHONY: docker
docker:
#${DOCKER_FILE} $(ARGS)
I want to run something liek this: make docker up -d
As written, your make invocation would treat up as a target and -d as a make parameter. You can't tell make to interpret the parameters differently. That being said, you can assign up -d to a variable and pass that in:
make docker ARGS="up -d"
.PHONY: docker
docker:
#${DOCKER_FILE} $(ARGS)
And this would do what you want. If you NEED to make the syntax similar to what you are describing for some reason, then you could create a wrapper script that would parse the arguments, and reformat them to a make specific target:
#!/bin/bash
[ "$1" == "docker" ] && make $1 ARGS="${#:2}"
bash> makedocker up -d
I have a rule within a Makefile which tries to render some text with env variables, I always get this variable empty whenever I pass it to the $(shell ) command
the following is the Makefile I am trying to pass the env variables with
and I am using sqoop to fetch data from sql server running a query with defined date in where statement
SHELL := /bin/bash
ifndef DATE
override DATE=$(shell date --date="1 day ago" +%Y%m%d)
endif
test:
sqoop --target-dir /tmp/output/ --fields-terminated-by '\t' --split-by id --query '$(shell cat query.sql | env DATE=$(DATE) python -m mypackage.render)'
I run commands like make test DATE=20190728
when I pass the env argument like | env DATE=20190728 python -m mypackage.render then I am able to see the query with where convert(varchar,created_at,112) = '20190728' but when I use env var I always get an empty string with the env variable like where convert(varchar,created_at,112) = ''
The problem is the make $(shell ...) invocation. You probably want command substitution (a shell feature). And kill a useless cat.
test:
$(MAKE) do_test VAR=test
do_test:
VAR=$(VAR) python -m mypackage.render < file.txt
Note that it is not recommended to set SHELL in a Makefile.
I am trying to set an Environment variable in a Makefile, so it can be used in another program running in the sam shell as make, but after make has run.
Update: This is not possible according to accepted answer with comments.
Steps:
run make test setting env: export TEST_ENV_ONE=OneString
run another program, that can read TEST_ENV_ONE
Tried this:
Not working:
test:
export TEST_ENV_ONE=OneString
$(shell export TEST_ENV_TWO=TwoString)
Afterwards this is empty:
echo $TEST_ENV_ONE
echo $TEST_ENV_TWO
Your export TEST_ENV_ONE=OneString above is running in a dedicated shell. The subsequent commands run in other shell instances. Therefore, they don't inherit the environment variable TEST_ENV_ONE.
You could use a top-level (i.e., not in a target's recipe) export directive in the makefile:
export env_var := MyEnvVariable
.PHONY: all
all:
echo env_var: $$env_var
This way, the variable env_var is exported to the shells that will execute the recipes.
If you run make with the makefile above:
$ make
echo env_var: $env_var
env_var: MyEnvVariable
As you can see from the output, the shell that run echo env_var: $env_var had the variable env_var in its environment.
If you want the environment variables to be exported to the shell from which you invoked make things are a bit difficult because, as explained by ネロク, you cannot directly export environment variables from a child process (make) to its parent process (the shell from which you invoke make). But if you accept to invoke make like this:
eval "$(make)"
then it is indeed possible: just echo export VARIABLE1=VALUE1; export VARIABLE2=VALUE2; ... in your recipe. Warning: you will also have to guarantee that nothing else gets echoed by make on the standard input. But you can use the standard error, instead. Example:
$ cat Makefile
export TEST_ENV_ONE := OneString
all:
#printf 'make variable TEST_ENV_ONE = %s\n' "$(TEST_ENV_ONE)" 1>&2
#printf 'in-recipe shell variable TEST_ENV_ONE = %s\n' "$$TEST_ENV_ONE" 1>&2
#printf 'export TEST_ENV_ONE="%s"\n' '$(TEST_ENV_ONE)'
$ unset TEST_ENV_ONE
$ printenv TEST_ENV_ONE
$ eval "$(make)"
make variable TEST_ENV_ONE = OneString
in-recipe shell variable TEST_ENV_ONE = OneString
$ printenv TEST_ENV_ONE
OneString
Note that make more or less considers environment variables as make variables. From GNU make manual:
Variables in make can come from the environment in which make is run.
Every environment variable that make sees when it starts up is
transformed into a make variable with the same name and value.
However, an explicit assignment in the makefile, or with a command
argument, overrides the environment. (If the ‘-e’ flag is specified,
then values from the environment override assignments in the makefile.
See Summary of Options. But this is not recommended practice.)
So, unless the value of your variable is the result of complex computations by make itself, a much more natural way to obtain the same result would be to define the environment variable in the parent shell and to use it as is in the Makefile:
$ cat Makefile
all:
#printf 'make variable TEST_ENV_ONE = %s\n' "$(TEST_ENV_ONE)"
#printf 'in-recipe shell variable TEST_ENV_ONE = %s\n' "$$TEST_ENV_ONE"
$ export TEST_ENV_ONE=OneString
$ make
make variable TEST_ENV_ONE = OneString
in-recipe shell variable TEST_ENV_ONE = OneString
$ printenv TEST_ENV_ONE
OneString
I calling a autogen.sh script from other Makefile, and this Makefile is called from a shell script, If I set a variable VAR on this more external script I can use-it normally inside this Makefile, really VAR exists and is defined, but when this Makefile runs autogen.sh I cannot use the variable VAR in configure.in file, the only way to get this (or any other variable) is using export command in more external shell.
compile:
pushd $(DIRNAME); \
if test ! -e Makefile; then \
echo $PKG_CONFIG_PATH; \
./autogen.sh $(CONFIGURE_OPTIONS); \
fi; \
make; \
popd
This is the best way to do this ?
Thanks in advance
You could either follow the suggestion from Idelic of passing the variable to autogen.sh explicitly as part of the command:
PKG_CONFIG_PATH="$(PKG_CONFIG_PATH)" ./autogen.sh $(CONFIGURE_OPTIONS)
or, if you have multiple scripts and commands you call from the Makefile and you want to export a bunch of environment variables to any command invoked from this Makefile, you could just add these lines to your Makefile:
export PKG_CONFIG_PATH="YOUR_PKG_CONFIG_PATH"
export FOO="BLAH"
export BAR="BLAH-BLAH"
You can pass the variable explicitly in the autogen.sh invocation:
PKG_CONFIG_PATH="$(PKG_CONFIG_PATH)" ./autogen.sh $(CONFIGURE_OPTIONS)
Is there a better way to source a script, which sets env vars, from within a makefile?
FLAG ?= 0
ifeq ($(FLAG),0)
export FLAG=1
/bin/myshell -c '<source scripts here> ; $(MAKE) $#'
else
...targets...
endif
Makefile default shell is /bin/sh which does not implement source.
Changing shell to /bin/bash makes it possible:
# Makefile
SHELL := /bin/bash
rule:
source env.sh && YourCommand
To answer the question as asked: you can't.
The basic issue is that a child process can not alter the parent's environment. The shell gets around this by not forking a new process when source'ing, but just running those commands in the current incarnation of the shell. That works fine, but make is not /bin/sh (or whatever shell your script is for) and does not understand that language (aside from the bits they have in common).
Chris Dodd and Foo Bah have addressed one possible workaround, so I'll suggest another (assuming you are running GNU make): post-process the shell script into make compatible text and include the result:
shell-variable-setter.make: shell-varaible-setter.sh
postprocess.py #^
# ...
else
include shell-variable-setter.make
endif
messy details left as an exercise.
If your goal is to merely set environment variables for Make, why not keep it in Makefile syntax and use the include command?
include other_makefile
If you have to invoke the shell script, capture the result in a shell command:
JUST_DO_IT=$(shell source_script)
the shell command should run before the targets. However this won't set the environment variables.
If you want to set environment variables in the build, write a separate shell script that sources your environment variables and calls make. Then, in the makefile, have the targets call the new shell script.
For example, if your original makefile has target a, then you want to do something like this:
# mysetenv.sh
#!/bin/bash
. <script to source>
export FLAG=1
make "$#"
# Makefile
ifeq($(FLAG),0)
export FLAG=1
a:
./mysetenv.sh a
else
a:
.. do it
endif
Using GNU Make 3.81 I can source a shell script from make using:
rule:
<tab>source source_script.sh && build_files.sh
build_files.sh "gets" the environment variables exported by source_script.sh.
Note that using:
rule:
<tab>source source_script.sh
<tab>build_files.sh
will not work. Each line is ran in its own subshell.
This works for me. Substitute env.sh with the name of the file you want to source. It works by sourcing the file in bash and outputting the modified environment, after formatting it, to a file called makeenv which is then sourced by the makefile.
IGNORE := $(shell bash -c "source env.sh; env | sed 's/=/:=/' | sed 's/^/export /' > makeenv")
include makeenv
Some constructs are the same in the shell and in GNU Make.
var=1234
text="Some text"
You can alter your shell script to source the defines. They must all be simple name=value types.
Ie,
[script.sh]
. ./vars.sh
[Makefile]
include vars.sh
Then the shell script and the Makefile can share the same 'source' of information. I found this question because I was looking for a manifest of common syntax that can be used in Gnu Make and shell scripts (I don't care which shell).
Edit: Shells and make understand ${var}. This means you can concatenate, etc,
var="One string"
var=${var} "Second string"
I really like Foo Bah's answer where make calls the script, and the script calls back to make. To expand on that answer I did this:
# Makefile
.DEFAULT_GOAL := all
ifndef SOME_DIR
%:
<tab>. ./setenv.sh $(MAKE) $#
else
all:
<tab>...
clean:
<tab>...
endif
--
# setenv.sh
export SOME_DIR=$PWD/path/to/some/dir
if [ -n "$1" ]; then
# The first argument is set, call back into make.
$1 $2
fi
This has the added advantage of using $(MAKE) in case anyone is using a unique make program, and will also handle any rule specified on the command line, without having to duplicate the name of each rule in the case when SOME_DIR is not defined.
If you want to get the variables into the environment, so that they are passed to child processes, then you can use bash's set -a and set +a. The former means, "When I set a variable, set the corresponding environment variable too." So this works for me:
check:
bash -c "set -a && source .env.test && set +a && cargo test"
That will pass everything in .env.test on to cargo test as environment variables.
Note that this will let you pass an environment on to sub-commands, but it won't let you set Makefile variables (which are different things anyway). If you need the latter, you should try one of the other suggestions here.
My solution to this: (assuming you're have bash, the syntax for $# is different for tcsh for instance)
Have a script sourceThenExec.sh, as such:
#!/bin/bash
source whatever.sh
$#
Then, in your makefile, preface your targets with bash sourceThenExec.sh, for instance:
ExampleTarget:
bash sourceThenExec.sh gcc ExampleTarget.C
You can of course put something like STE=bash sourceThenExec.sh at the top of your makefile and shorten this:
ExampleTarget:
$(STE) gcc ExampleTarget.C
All of this works because sourceThenExec.sh opens a subshell, but then the commands are run in the same subshell.
The downside of this method is that the file gets sourced for each target, which may be undesirable.
Depending on your version of Make and enclosing shell, you can implement a nice solution via eval, cat, and chaining calls with &&:
ENVFILE=envfile
source-via-eval:
#echo "FOO: $${FOO}"
#echo "FOO=AMAZING!" > $(ENVFILE)
#eval `cat $(ENVFILE)` && echo "FOO: $${FOO}"
And a quick test:
> make source-via-eval
FOO:
FOO: AMAZING!
An elegant solution found here:
ifneq (,$(wildcard ./.env))
include .env
export
endif
If you need only a few known variables exporting in makefile can be an option, here is an example of what I am using.
$ grep ID /etc/os-release
ID=ubuntu
ID_LIKE=debian
$ cat Makefile
default: help rule/setup/lsb
source?=.
help:
-${MAKE} --version | head -n1
rule/setup/%:
echo ID=${#F}
rule/setup/lsb: /etc/os-release
${source} $< && export ID && ${MAKE} rule/setup/$${ID}
$ make
make --version | head -n1
GNU Make 3.81
. /etc/os-release && export ID && make rule/setup/${ID}
make[1]: Entering directory `/tmp'
echo ID=ubuntu
ID=ubuntu
--
http://rzr.online.fr/q/gnumake
Assuming GNU make, can be done using a submake. Assuming that the shell script that exports the variables is include.sh in the current directory, move your Makefile to realmake.mk. Create a new Makefile:
all:
#. ./include.sh; \
$(MAKE) -f realmake.mk $(MAKECMDGOALS)
$(MAKECMDGOALS):
+#. ./include.sh; \
$(MAKE) -f realmake.mk $(MAKECMDGOALS)
Pay attention to the ./ preceding include.sh.
Another possible way would be to create a sh script, for example run.sh, source the required scripts and call make inside the script.
#!/bin/sh
source script1
source script2 and so on
make
target: output_source
bash ShellScript_name.sh
try this it will work, the script is inside the current directory.