Reputation: 1986
Is there a better way to source a script, which sets environment variables, from within a makefile?
FLAG ?= 0
ifeq ($(FLAG),0)
export FLAG=1
/bin/myshell -c '<source scripts here> ; $(MAKE) $@'
else
...targets...
endif
Upvotes: 112
Views: 146555
Reputation: 1623
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.
Upvotes: 2
Reputation: 11
Here is my solution
This is my dir structure
- root_dir
| - Makefile
| - scripts
| - | - bash
| - | - helpers.sh
The file you want to source is the helpers.sh
. (Let's say, you've defined customFunc
in helpers.sh
)
Now, Following is the contents of scripts/bash
#!/bin/bash
# Use this file as the SHELL variable in makefiles to get access to the helper functions
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd );
source $SCRIPT_DIR/helpers.sh;
# makefile passes "-c" as the first argument, so we shift it off
shift && eval "$@";
Every command will have the helpers.sh
sourced in the following makefile.
SHELL := ./scripts/bash
install:
customFunc "do" "this";
Upvotes: 1
Reputation: 1662
As mentioned above, you can use .ONESHELL
.ONESHELL
source
doesn't workSHELL=/bin/bash
SOURCE_FILE='.to-source'
${SOURCE_FILE}:
echo "export SOME_VAR=some-value" > $@
use-sourced: ${SOURCE_FILE}
source ${SOURCE_FILE}
env|grep SOME_VAR
make use-sourced
gives the output
echo "export SOME_VAR=some-value" > '.to-source'
source '.to-source'
env|grep SOME_VAR
make: *** [Makefile:9: use-sourced] Error 1
.ONESHELL
, it works as if in a shell scriptSHELL=/bin/bash
SOURCE_FILE='.to-source'
${SOURCE_FILE}:
echo "export SOME_VAR=some-value" > $@
.ONESHELL:
use-sourced: ${SOURCE_FILE}
source ${SOURCE_FILE}
env|grep SOME_VAR
make use-sourced
works
echo "export SOME_VAR=some-value" > '.to-source'
source '.to-source'
env|grep SOME_VAR
SOME_VAR=some-value
be sure to read the linked documentation - converting to .ONESHELL
can lead to unexpected behavior as now 'state' is shared
Upvotes: 1
Reputation: 4821
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
.
Upvotes: 1
Reputation: 420
An elegant solution found here:
ifneq (,$(wildcard ./.env))
include .env
export
endif
Upvotes: 1
Reputation: 1001
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!
Upvotes: 1
Reputation: 1388
target: output_source
bash ShellScript_name.sh
try this it will work, the script is inside the current directory.
Upvotes: -4
Reputation: 5085
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
Upvotes: 170
Reputation: 24551
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.
Upvotes: 3
Reputation: 869
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
Upvotes: -3
Reputation: 8915
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.
Upvotes: 4
Reputation: 3176
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
Upvotes: 0
Reputation: 101181
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.
Upvotes: 67
Reputation: 8915
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.
Upvotes: 22
Reputation: 8016
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
Upvotes: 12
Reputation: 22430
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"
Upvotes: 8
Reputation: 26271
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
Upvotes: 28