2009-11-24 201 views
227

例如,我在我的Makefile是这样的:如何在makefile中写入'cd'命令?

all: 
    cd some_directory 

但是,当我输入make我看到的只是“CD some_directory”,像在echo命令。

+3

目前还不清楚你想做什么,但是,根据我对'make'的经验,我从来不想改变这个目录。也许你应该尝试另一种解决方案? –

+0

相信你的目录很重要,这是一个常见的新手错误。对于大多数情况来说,它不是; 'cd dir; cmd文件几乎总是可以用'cmd dir/file'来表示。 – tripleee

+8

这是一个常见的新手错误,相信你目前的工作目录是无足轻重的。许多程序,特别是shell脚本,都是以“。”为特定的值写入的。 的确,大多数工具都是这样设计的,你不需要为它改变你的密码。但这并非总是如此,我认为将其称为“错误”以相信您的目录可能很重要是个好主意。 –

回答

408

它实际上正在执行命令,将目录更改为some_directory,但是,这是在子进程shell中执行的,并且不影响make和您正在使用的shell。

如果您要在some_directory范围内执行更多任务,则需要添加一个分号并附加其他命令。请注意,您不能使用换行符,因为它们被make解释为规则的结尾,因此为清晰起见使用的任何换行符都需要使用反斜杠进行转义。

例如:

all: 
     cd some_dir; echo "I'm in some_dir"; \ 
      gcc -Wall -o myTest myTest.c 

还要注意分号是每一个命令之间必要的,即使你加一个反斜杠和一个换行符。这是由于整个字符串被shell解析为一行。正如评论中指出的那样,您应该使用'& &'来加入命令,这意味着只有在前面的命令成功时才会执行它们。

all: 
     cd some_dir && echo "I'm in some_dir" && \ 
      gcc -Wall -o myTest myTest.c 

做破坏性的工作,如清理时,你会否则将会破坏错了东西,应cd失败不管出于什么原因,这一点尤为重要。

虽然常用的方法是在子目录中调用make,但您可能需要查看该子目录。有此一命令行选项,这样你就不用调用cd自己,所以你的规则是这样的

all: 
     $(MAKE) -C some_dir all 

这将改变成some_dir,并在那里与目标“的所有”执行Makefile。作为最佳实践,使用$(MAKE)而不是直接调用make,因为它会小心地调用正确的make实例(例如,如果您为构建环境使用特殊的make版本),并提供稍微不同的行为运行时使用某些开关,如-t

对于记录,使总是回声它执行的命令(除非明确禁止),即使它没有输出,这就是你所看到的。

+6

那么,不*总是*。为了抑制回声,只需在该行的开头添加@。 – Beta

+1

@Beta:好的,并且破折号前缀也会忽略错误状态。也许我有点失落了,我想指出这样一个事实,即无论命令是什么,命令都会回应命令。在这种情况下,这是一个没有输出的命令,这让不熟悉make的人看起来更加陌生。 – falstro

+33

两个nits:1.这些命令应该真的被'&&'连接,因为用';'如果目录不存在并且'cd'失败,shell将继续运行当前目录中的其余命令,这可能会导致诸如编译时发现神秘的“文件未找到”消息,调用make时出现无限循环,或者像'clean :: cd dir; rm -rf *'。 2.调用子make时,调用'$(MAKE)'而不是'make',以便[选项将正确传递](http://www.gnu.org/software/make/manual/make.html #MAKE-变量)。 – andrewdotn

5

一旦到达那里,你想要它做什么?每个命令都在子shell中执行,因此子shell会更改目录,但最终结果是下一个命令仍在当前目录中。

用GNU做,你可以这样做:

BIN=/bin 
foo: 
    $(shell cd $(BIN); ls) 
+3

为什么'$(shell ...)'时出现cd $(BIN); ls'或'cd $(BIN)&& ls'(就像@andrewdotn指出的)就足够了。 – vapace

20

这里有一个可爱的把戏对付目录而作。而不是使用多行字符串,或“cd;”每个命令,定义了一个简单的chdir函数像这样:

CHDIR_SHELL := $(SHELL) 
define chdir 
    $(eval _D=$(firstword $(1) $(@D))) 
    $(info $(MAKE): cd $(_D)) $(eval SHELL = cd $(_D); $(CHDIR_SHELL)) 
endef 

然后,所有你需要做的就是把它在你的规则,像这样:

all: 
      $(call chdir,some_dir) 
      echo "I'm now always in some_dir" 
      gcc -Wall -o myTest myTest.c 

你甚至可以做到以下几点:

some_dir/myTest: 
      $(call chdir) 
      echo "I'm now always in some_dir" 
      gcc -Wall -o myTest myTest.c 
+32

“可爱”?更像是有足够的绳子在脚下射击自己。 – tripleee

+0

这个当前目​​录是为那些只在该规则中的命令设置的,还是为所有后来执行的规则设置的?此外,将在Windows下这项工作的一些变化? – user117529

+4

这当然会破坏并行执行('-jn'),这是_make_的全部重点。 – bobbogo

36

GNU make 3.82开始,您可以使用.ONESHELL特殊目标在外壳的单实例运行所有食谱线:

  • 新的特殊目标:.ONESHELL指示作出调用单个实例的外壳 并提供其与整个配方,不管它有多少 线包含的内容。 [...]

例子:

.ONESHELL: 
all: 
    cd ~/some_dir 
    pwd # this line is correctly run in ~/some_dir if cd succeeded 
+4

请注意,'pwd'本身以及'''pwd'''(带反引号),但'$(shell pwd)'和'$(PWD)'在执行'cd之前仍然会返回目录'命令,所以你不能直接使用它们。 – anol

+2

是的,因为在通过make执行命令之前完成了变量和函数的扩展,而'pwd'和'''pwd \''是由shell本身执行的。 – Chnossos

+0

是的,但没有人可以安装3.82,因为它引入了破解兼容性变化:( – CpILL

1

像这样:

target: 
    $(shell cd ....); \ 
    # ... commands execution in this directory 
    # ... no need to go back (using "cd -" or so) 
    # ... next target will be automatically in prev dir 

祝你好运!