Shell 如何在Makefile中生成脚本?

Shell 如何在Makefile中生成脚本?,shell,makefile,Shell,Makefile,是否有更好的方法从makefile中获取设置环境变量的脚本 FLAG ?= 0 ifeq ($(FLAG),0) export FLAG=1 /bin/myshell -c '<source scripts here> ; $(MAKE) $@' else ...targets... endif 标志?=0 ifeq($(标志),0) 导出标志=1 /bin/myshell-c'$(MAKE)$@' 其他的 ……目标。。。 恩迪夫 如果您的目标只是为Make设置环境变量,为什么不将

是否有更好的方法从makefile中获取设置环境变量的脚本

FLAG ?= 0
ifeq ($(FLAG),0)
export FLAG=1
/bin/myshell -c '<source scripts here> ; $(MAKE) $@'
else
...targets...
endif
标志?=0
ifeq($(标志),0)
导出标志=1
/bin/myshell-c'$(MAKE)$@'
其他的
……目标。。。
恩迪夫

如果您的目标只是为Make设置环境变量,为什么不将其保留为Makefile语法并使用
include
命令

include other_makefile
如果必须调用shell脚本,请在
shell
命令中捕获结果:

JUST_DO_IT=$(shell source_script)
shell命令应在目标之前运行。但是,这不会设置环境变量

如果要在生成中设置环境变量,请编写一个单独的shell脚本,以获取环境变量和调用。然后,在makefile中,让目标调用新的shell脚本

例如,如果原始makefile具有target
a
,则需要执行以下操作:

# 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
#mysetenv.sh
#!/bin/bash
. 
导出标志=1
使“$@”
#生成文件
ifeq($(标志),0)
导出标志=1
a:
./mysetenv.sh a
其他的
a:
.. 做吧
恩迪夫

回答问题:你不能

基本问题是子进程不能改变父进程的环境。shell通过在
source
时不分叉新进程来解决这个问题,而只是在shell的当前版本中运行这些命令。这很好,但是
make
不是
/bin/sh
(或者你的脚本所用的任何shell),并且不理解那种语言(除了它们的共同点)

Chris Dodd和Foo Bah已经解决了一个可能的解决方案,因此我将建议另一个解决方案(假设您正在运行GNU make):将shell脚本后处理为make兼容文本,并包括结果:

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

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

凌乱的细节留作练习。

shell
gnumake
中,有些构造是相同的

var=1234
text="Some text"
您可以修改shell脚本来定义源代码。它们都必须是简单的
name=value
类型

[script.sh]

. ./vars.sh
[制作文件]

include vars.sh
然后,shell脚本和Makefile可以共享相同的信息“源”。我发现这个问题是因为我在寻找可以在GNUmake和shell脚本中使用的公共语法清单(我不关心哪个shell)

编辑:shell并使其理解${var}。这意味着您可以连接,等等, var=“一个字符串”
var=${var}“第二个字符串”

这对我很有用。用要源文件的名称替换
env.sh
。它的工作原理是在bash中对文件进行源化,并在格式化后将修改后的环境输出到名为
makeenv
的文件中,然后由makefile进行源化

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

使用GNU Make 3.81,我可以使用以下方法从Make获取shell脚本:

rule:
<tab>source source_script.sh && build_files.sh
规则:
source\u script.sh和build\u files.sh
build_files.sh“获取”由source_script.sh导出的环境变量

请注意,使用:

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

这是行不通的。每一行都在它自己的子shell中运行。

如果您只需要在makefile中导出几个已知变量,那么可以选择,下面是我使用的示例

$ 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
$grep ID/etc/os发行版
ID=ubuntu
ID_LIKE=debian
$cat生成文件
默认值:帮助规则/设置/lsb
来源?=。
帮助:
-${MAKE}--version|head-n1
规则/设置/%:
echo ID=${@F}
规则/设置/lsb:/etc/os发布
${source}$<&&export-ID&&${MAKE}规则/setup/$${ID}
$make
make——版本|头部-n1
GNU Make 3.81
. /etc/os发行版和导出ID和制定规则/setup/${ID}
make[1]:输入目录“/tmp”
echo ID=ubuntu
ID=ubuntu
--

我的解决方案:(假设您有
bash
,例如
$@
的语法对于
tcsh
是不同的)

有一个脚本
sourceThenExec.sh
,例如:

#!/bin/bash
source whatever.sh
$@
然后,在makefile中,用
bash sourceThenExec.sh
为目标添加序言,例如:

ExampleTarget:
    bash sourceThenExec.sh gcc ExampleTarget.C
当然,您可以将类似于
STE=bash sourceThenExec.sh的内容放在makefile的顶部,并将其缩短:

ExampleTarget:
    $(STE) gcc ExampleTarget.C
所有这些都是有效的,因为
sourceThenExec.sh
打开了一个子shell,但随后命令在同一个子shell中运行


这种方法的缺点是为每个目标获取文件源,这可能是不可取的。

我非常喜欢Foo-Bah的回答,make调用脚本,脚本调用make。为了进一步说明这个答案,我做了以下工作:

# Makefile
.DEFAULT_GOAL := all 

ifndef SOME_DIR

%:
<tab>. ./setenv.sh $(MAKE) $@

else

all:
<tab>...

clean:
<tab>...

endif

如果有人正在使用唯一的MAKE程序,则使用$(MAKE)还有一个额外的优点,并且还将处理命令行上指定的任何规则,而不必在未定义某个目录的情况下复制每个规则的名称。

另一种可能的方法是创建sh脚本,例如
run.sh
,获取所需脚本的源代码,并在脚本内部调用make

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

make 

如果希望将变量放入环境中,以便将它们传递给子进程,那么可以使用bash的
set-a
set+a
。前者的意思是,“当我设置一个变量时,也要设置相应的环境变量。”所以这对我来说是可行的:

check:
    bash -c "set -a && source .env.test && set +a && cargo test"
这将把
.env.test
中的所有内容作为环境变量传递给
货物测试


请注意,这将允许您将环境传递给子命令,但不允许您设置Makefile变量(无论如何,它们是不同的)。如果您需要后者,您应该在这里尝试其他建议。

Makefile默认shell是
/bin/sh
,它不实现
源代码

将shell更改为
/bin/bash
可以:

# Makefile

SHELL := /bin/bash

rule:
    source env.sh && YourCommand

试试这个,它会工作的,脚本在当前目录中。

根据Make和封闭shell的版本,您可以通过
eval
cat
,以及使用链接调用来实现一个很好的解决方案
# Makefile

SHELL := /bin/bash

rule:
    source env.sh && YourCommand
target: output_source
    bash ShellScript_name.sh
ENVFILE=envfile

source-via-eval:
  @echo "FOO: $${FOO}"
  @echo "FOO=AMAZING!" > $(ENVFILE)
  @eval `cat $(ENVFILE)` && echo "FOO: $${FOO}"
> make source-via-eval
FOO:
FOO: AMAZING!
ifneq (,$(wildcard ./.env))
    include .env
    export
endif