2011-09-21 212 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 

試試這個它會工作,腳本是當前目錄內。