2011-09-21 210 views
40

有没有更好的方法来从makefile中设置env变量的脚本?如何在Makefile中编写脚本?

FLAG ?= 0 
ifeq ($(FLAG),0) 
export FLAG=1 
/bin/myshell -c '<source scripts here> ; $(MAKE) [email protected]' 
else 
...targets... 
endif 
+1

听起来像你真正想要的是编写一个脚本,其源代码设置envvars然后运行make,而不是让源脚本本身... –

回答

28

要回答这个问题的问:你不能

基本问题是子进程不能改变父进程的环境。 shell通过解决了这个问题,而不是在分割一个新进程source'ing,但只是在shell的当前版本中运行这些命令。这工作正常,但make不是/bin/sh(或脚本的任何shell),并不理解该语言(除了他们有共同的位)。

多德和Foo呸已经解决了一个可能的解决方法,所以我会建议其他(假设你正在运行GNU做):后期处理的shell脚本到兼容make文字和结果包括:

shell-variable-setter.make: shell-varaible-setter.sh 
    postprocess.py @^ 

# ... 
else 
include shell-variable-setter.make 
endif 

凌乱的细节留作练习。

17

如果你的目标仅仅设置环境变量作,为什么不把它放在Makefile的语法和使用include命令?

include other_makefile 

如果你必须调用shell脚本,拍摄效果在shell命令:

JUST_DO_IT=$(shell source_script) 

shell命令应该在目标之前运行。但是,这不会设置环境变量。

如果您想要在构建中设置环境变量,请编写一个单独的shell脚本来源环境变量和调用make。然后,在makefile中,让目标调用新的shell脚本。

例如,如果你原来的makefile有目标a,那么你想要做这样的事情:

# mysetenv.sh 
#!/bin/bash 
. <script to source> 
export FLAG=1 
make "[email protected]" 

# Makefile 
ifeq($(FLAG),0) 
export FLAG=1 
a: 
    ./mysetenv.sh a 
else 
a: 
    .. do it 
endif 
+2

我无法控制脚本。必须来源。 – brooksbp

+0

@brooksbp编辑。 –

+0

如果未导出环境变量,它会捕获环境变量吗?从乍看起来,它似乎调用一个新的shell,运行命令,然后退出并返回到旧的环境..? – brooksbp

4

shellGNU Make中的一些构造是相同的。

var=1234 
text="Some text" 

您可以更改您的shell脚本以获取定义。它们都必须是简单的name=value类型。

即,

[script.sh]

. ./vars.sh 

[生成文件]

include vars.sh 

然后,外壳脚本和生成文件可以共享相同的信息 '源'。我发现这个问题是因为我正在寻找一个可以在Gnu Make和shell脚本中使用的公共语法清单(我不关心哪个shell)。

编辑:贝壳和明白$ {var}。这意味着你可以连接等 var =“一个字符串” var = $ {var}“第二个字符串”

+0

Err,像上面这样的shell脚本不会在接受的答案中创建**凌乱的细节**。它最终就像一个[tag:kbuild]文件。 –

9

这适用于我。将env.sh替换为您想要的文件的名称。它通过在bash中获取文件并在格式化后输出修改后的环境,将其转换为名为makeenv的文件,然后由该文件生成该文件。

IGNORE := $(shell bash -c "source env.sh; env | sed 's/=/:=/' | sed 's/^/export /' > makeenv")       
include makeenv 
+1

'IFDEF _intel64onosx'
'GCC = /选择/英特尔/ bin中/ icc'
'CFLAGS = -m64 -g -std = c99' 'IGNORE:= $(壳的bash -c“源极/选择/英特尔/bin/iccvars.sh intel64; env | sed's/= /:= /'| sed's/^/export /'> makeenv“)' 'include makeenv' – revher

+0

设置正确的编译器环境很不错位)。谢谢。 – revher

11

使用GNU使3.81我可以化妆使用源shell脚本:

rule: 
<tab>source source_script.sh && build_files.sh 

build_files.sh “变” 由source_script.sh导出的环境变量。

注意,使用:

rule: 
<tab>source source_script.sh 
<tab>build_files.sh 

将无法​​正常工作。每行都在其自己的子shell中运行。

+0

不错的黑客,谢谢! – Timmmm

+0

请注意,使用GNU Make 4.1(也可能是4.0)将'source'和命令保留在同一行是必要的。使用3.81,我可以使用不同的线。为便于携带,一条线路适用于这些版本。但是,如果线路太长,调用外部脚本更具可读性! –

+0

你确定吗?正如我在答案中提到的那样,在3.81中有多行不适用于我。 Make在其自己的子shell中运行每行,因此多行不应在任何版本上运行。也许你有一个不寻常的操作系统或构建? – Samuel

0

如果你只需要一些已知的变量导出在生成文件可以是一个选项,这里是我正在使用的一个例子。

$ 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

1

我对这个解决方案:(假设你有bash,为[email protected]语法是不同的tcsh例如)

有一个脚本sourceThenExec.sh,因为这样的:

#!/bin/bash 
source whatever.sh 
[email protected] 

然后,在你的makefile中,前言你的目标TS与bash sourceThenExec.sh,例如:

ExampleTarget: 
    bash sourceThenExec.sh gcc ExampleTarget.C 

你当然可以把像STE=bash sourceThenExec.sh在你的makefile的顶部和缩短这个:

ExampleTarget: 
    $(STE) gcc ExampleTarget.C 

所有这一切工作,因为sourceThenExec.sh打开一个子shell,但那么这些命令在相同的子shell中运行。

这种方法的缺点是文件来源于每个目标,这可能是不受欢迎的。

3

我真的很喜欢Foo Bah的答案,在那里调用脚本,脚本回调。为了扩大对这个问题的答案我这样做:

# Makefile 
.DEFAULT_GOAL := all 

ifndef SOME_DIR 

%: 
<tab>. ./setenv.sh $(MAKE) [email protected] 

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 

这在任何情况下使用$(MAKE)的优势是采用了独特的化妆程序,也将处理在命令行中指定的任何规则,而不必在SOME_DIR未定义的情况下复制每个规则的名称。

+0

如果你有一个老版本的make,并且.DEFAULT_GOAL不被支持,那么ifndef的第一个分支将会失败,并显示“make:*** No targets。Stop。”。您可以通过向隐式规则添加“全部”来解决此问题,如下所示:“all%:”。请注意,这将导致使用较新版本的make的“***混合隐式和正常规则:不赞成使用的语法”警告。 – Samuel

+0

要使'%'目标_.SILENT_,用'@'启动命令 – ITL

1

另一种可能的方法是创建一个sh脚本,例如run.sh,获取所需脚本并在脚本中调用make。

#!/bin/sh 
source script1 
source script2 and so on 

make 
0

如果你想要得到的变量进入环境,让他们传递给子进程,那么你可以使用bash的set -aset +a。前者的意思是,“当我设置一个变量时,也设置相应的环境变量。”因此,这对我的作品:

check: 
    bash -c "set -a && source .env.test && set +a && cargo test" 

这将上cargo test.env.test通过一切为环境变量。

请注意,这会让你将一个环境传递给子命令,但它不会让你设置Makefile变量(这是不同的东西)。如果你需要后者,你应该尝试其中一个建议。

8

Makefile默认shell是/bin/sh,它不执行source

更改外壳/bin/bash能够:

# Makefile 

SHELL := /bin/bash 

rule: 
    source env.sh && YourCommand 
0
target: output_source 
    bash ShellScript_name.sh 

试试这个它会工作,脚本是当前目录内。