使用“make -f”自动化的奇怪行为

gol*_*cks 7 make gnu-make

我有一组目录,其中一些包含 makefile,一些 makefile 有clean目标。在父目录中,我有一个简单的脚本:

#!/bin/bash

for f in *; do
        if [[ -d $f && -f $f/makefile ]]; then
                echo "Making clean in $f..."
                make -f $f/makefile clean
        fi
done
Run Code Online (Sandbox Code Playgroud)

当它使用没有(定义的)“干净”目标的 makefile 访问目录时,这会做一件奇怪的事情。例如,给定两个目录,onetwo,包含;

一个/makefile

clean:
        -rm *.x
Run Code Online (Sandbox Code Playgroud)

两个/makefile

clean:
Run Code Online (Sandbox Code Playgroud)

在第二种情况下,“clean”没有指令,所以如果你运行“make clean”,two你会得到:

make: Nothing to be done for `clean'.
Run Code Online (Sandbox Code Playgroud)

对比如果没有“干净”:

make: *** No rule to make target `clean'.  Stop.
Run Code Online (Sandbox Code Playgroud)

但是,对于我将要描述的问题,无论目标存在但未定义或不存在,结果都是相同的。clean.sh从父目录运行;

Making clean in one...
rm *.x
rm: cannot remove `*.x': No such file or directory
make: [clean] Error 1 (ignored)
Run Code Online (Sandbox Code Playgroud)

所以one不需要清洗。没什么大不了的,这正如预期的那样。但是之后:

Making clean in two...
cat clean.sh >clean 
chmod a+x clean
Run Code Online (Sandbox Code Playgroud)

为什么cat clean.sh>clean等等?请注意,我确实创建了一个完全如上所示的最小示例——周围没有其他文件或目录(只有 clean.sh、目录一和目录二,以及非常小的 makefile)。但是在 clean.sh 运行后,make已复制clean.sh > clean并使其可执行。如果我然后再次运行 clean.sh:

Making clean in one...
make: `clean' is up to date.
Making clean in two...
make: `clean' is up to date.
Press any key to continue...
Run Code Online (Sandbox Code Playgroud)

更奇怪的是,因为现在它根本不使用指定的生成文件——它使用了一些“最新”的神秘目标。

我注意到一个可能相关的现象:如果像这样删除 clean.sh 中的一个测试子句:

#       if [[ -d $f && -f $f/makefile ]]; then
        if [[ -d $f ]]; then
Run Code Online (Sandbox Code Playgroud)

并创建一个three没有makefile的目录,部分输出包括:

Making clean in three...
make: three/makefile: No such file or directory
make: *** No rule to make target `three/makefile'.  Stop.
Run Code Online (Sandbox Code Playgroud)

我理解没有这样的文件或目录,但是为什么make要继续寻找具有该名称的目标?手册页看起来很简单:

-f 文件, --file=file, --makefile=FILE

  Use file as a makefile.
Run Code Online (Sandbox Code Playgroud)

les*_*ana 9

这种行为不是错误。这是一个特点。准确地说是一个功能和一个可能的用户错误。

有问题的特征是 Make 的隐含规则之一。在您的情况下,“构建”*.sh文件的隐式规则。用户错误,您的错误,在调用子目录中的 makefile 之前没有更改工作目录。


TL; DR:要解决此问题,您可以执行以下一项或多项操作:

  1. 修复 shell 脚本以更改工作目录:

    #!/bin/bash
    
    for f in *; do
            if [[ -d $f && -f $f/makefile ]]; then
                    echo "Making clean in $f..."
                    (cd $f; make clean)
            fi
    done
    
    Run Code Online (Sandbox Code Playgroud)
  2. 明确空规则:

    clean: ;
    
    Run Code Online (Sandbox Code Playgroud)
  3. 使clean目标变得虚假:

    .PHONY: clean
    
    Run Code Online (Sandbox Code Playgroud)

详细解释:

Make 有一堆隐含的规则。这允许人们在不编写 makefile 的情况下对简单项目调用 make。

试试这个进行演示:

  1. 创建一个空目录并进入该目录
  2. 创建一个名为clean.sh.
  3. make clean

输出:

$ make clean
cat clean.sh >clean 
chmod a+x clean
Run Code Online (Sandbox Code Playgroud)

砰!这就是隐含规则的力量。有关更多信息,请参阅有关隐式规则make 手册


我将尝试回答剩下的悬而未决的问题:

为什么它不调用第一个 makefile 的隐式规则?因为您用显式clean规则覆盖了隐式规则。

为什么clean第二个 makefile中的规则没有覆盖隐式规则?因为它没有配方。没有配方的规则不会覆盖隐式规则,而只是附加先决条件。有关更多信息,请参阅有关多个规则make 手册。另请参阅有关具有显式空配方的规则制作手册

为什么在调用子目录中的 makefile 之前不更改工作目录会出错?因为 make 不会改变工作目录。Make 将在继承的工作目录中工作。好吧,从技术上讲,这不一定是错误,但大多数情况下是错误的。您希望子目录中的 makefile 在子目录中工作吗?或者您希望它们在父目录中工作?

为什么 makeclean在第二次调用时忽略第一个 makefile 中的显式规则clean.sh因为现在目标文件clean已经存在。由于规则clean没有先决条件,因此无需重建目标。请参阅有关虚假目标制作手册准确描述了此问题。

为什么three/makefile在第三次调用中搜索目标?因为 make 总是在做任何其他事情之前尝试重新制作 makefile。如果 makefile 被明确请求使用-f但它不存在,则尤其如此。有关更多信息,请参阅有关重新制作 makefilemake 手册