执行Makefile中的另一个目标

209

我有一个类似于这样结构的Makefile:

all : 
    compile executable

clean :
    rm -f *.o $(EXEC)

我意识到在运行"make all"之前,我总是会在终端中先运行"make clean",然后再运行"clear"。我喜欢在尝试查找糟糕的C++编译错误之前清理终端。因此,我尝试添加第三个目标:

fresh :
    rm -f *.o $(EXEC)
    clear
    make all

这个方法可行,但它会运行第二个make实例(我认为)。是否有一种正确的方法,在不运行第二个make实例的情况下获得相同的功能?


10
我不确定这是否应该是一个单独的问题,但为什么调用嵌套的make命令是不好的呢? - Marek Židek
3个回答

281

实际上你是正确的:它会运行另一个make实例。一种可能的解决方案是:

.PHONY : clearscr fresh clean all

all :
    compile executable

clean :
    rm -f *.o $(EXEC)

fresh : clean clearscr all

clearscr:
    clear
通过调用make fresh命令,首先会运行clean目标,然后是clearscreen目标,它会运行clear命令,最后是执行任务的all目标。 编辑于8月4日 如果使用make的-j选项进行并行构建,会发生什么情况?可以通过定义顺序依赖来解决这个问题。从make手册第4.2节中可以找到如下内容:
偶尔会出现这样一种情况,即您希望对要调用的规则强制执行特定的顺序,但不希望在执行这些规则之一时强制更新目标。 在这种情况下,您需要定义顺序依赖性。 顺序依赖可以通过在前提条件列表中放置一个管道符号(|)来指定:位于管道符号左侧的任何前提条件都是正常的;位于管道符号右侧的任何前提条件都是顺序依赖:targets : normal-prerequisites | order-only-prerequisites。 当然,正常的前提条件部分可能为空。 此外,您仍然可以为同一目标声明多行先决条件:它们将被适当地附加。 请注意,如果您将同一文件声明为常规前提条件和仅顺序前提条件,则常规前提条件具有优先权(因为它们是仅顺序前提条件行为的严格超集)。
因此,makefile 变成了:
.PHONY : clearscr fresh clean all

all :
    compile executable

clean :
    rm -f *.o $(EXEC)

fresh : | clean clearscr all

clearscr:
    clear

12月5日更新:

运行多个makefile实例并不是什么大问题,因为任务内部的每个命令都将成为子shell。但你可以使用call函数来创建可重用的方法。

log_success = (echo "\x1B[32m>> $1\x1B[39m")
log_error = (>&2 echo "\x1B[31m>> $1\x1B[39m" && exit 1)

install:
  @[ "$(AWS_PROFILE)" ] || $(call log_error, "AWS_PROFILE not set!")
  command1  # this line will be a subshell
  command2  # this line will be another subshell
  @command3  # Use `@` to hide the command line
  $(call log_error, "It works, yey!")

uninstall:
  @[ "$(AWS_PROFILE)" ] || $(call log_error, "AWS_PROFILE not set!")
  ....
  $(call log_error, "Nuked!")

7
基本上所有遵循 .PHONY : 的内容都被视为一些关键字,会被始终执行,而非 Phony 的目标则意味着它们是文件。 - Dacav
1
@fantastory,不,我认为它们是独立的。t2将依赖于t0t1t3。如果您需要这个,您应该将t3作为t2所需的必要条件,将t1作为t3所需的必要条件,将t0作为t1所需的必要条件。这意味着有三个不同的规则。但是,您应该进行验证。我不是100%确定。 - Dacav
3
"仅限于订单的先决条件"是独立的。 - Fantastory
4
我不明白在哪里保证了“clean”在“all”之前执行?仅仅将它们放在竖杠右侧并不意味着它们按顺序执行。仅限顺序依赖项意味着目标在此类操作后未必会被更新。这与依赖元素的顺序无关,对吗? - CygnusX1
2
你是对的@CygnusX1,仅限顺序依赖项与按特定顺序执行依赖项无关; 仅与在引用它的目标之前对依赖项进行排序有关。这个答案的“EDIT Aug 4”部分是错误的。 - giorgiosironi
显示剩余2条评论

20

你已经有了一个顺序解决方案,可以重写为:

fresh:
    $(MAKE) clean
    clear
    $(MAKE) all

这是正确的做法,也是非常安全的方法。

在GNU make中,通过正确的依赖关系图可以实现顺序目标执行:

fresh: _all
_all: _clear
    Recipe for all
_clear: _clean
    Recipe for clear
_clean:
    Recipe for clean

以上规则定义了以下依赖图:fresh <- _all <- _clear <- _clean,这保证了以下食谱执行顺序:清理的食谱清除的食谱所有的食谱

您可以使用以下方式将食谱共享到多个目标:

target1 target2 target…:
    recipe1

将您的脚本与上述概念合并的结果为:

all _all : 
    compile executable
clean _clean :
    rm -f *.o $(EXEC)
clear _clear :
    clear
fresh: _all
_all: _clear
_clear: _clean
使用来自https://github.com/pkoper/mk/chains.mk,您可以使用语法糖编写以下内容:
all all@fresh :
    compile executable
clean clean@fresh :
    rm -f *.o $(EXEC)
clear clear@fresh :
    clear

@fresh = clean clear all
include chains.mk

fresh: @fresh

或者更好的是:

all: compile

@fresh = clean clear compile
include chains.mk

fresh: @fresh

compile compile@fresh:
    compile executable
clear clear@fresh:
    clear
clean clean@fresh:
    rm -f *.o $(EXEC)

8
如果你从“fresh”目标中删除了“make all”行:
fresh :
    rm -f *.o $(EXEC)
    clear

您可以简单地运行命令make fresh all,它将作为make fresh; make all执行。 有些人可能认为这是第二个`make`实例,但它肯定不是`make`的子实例(一个`make`内部的`make`),这正是您尝试的结果。

1
make -j12 fresh all 失败了。不要中断并行构建! - bobbogo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接