makefile 执行另一个目标

发布于 2024-09-10 12:58:54 字数 380 浏览 3 评论 0原文

我有一个类似这样的 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 实例的情况下获得相同的功能?

I have a makefile structured something like this:

all : 
    compile executable

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

I realized that I was consistently running "make clean" followed by "clear" in my terminal before running "make all". I like to have a clean terminal before I try and sift through nasty C++ compilation errors. So I tried to add a 3rd target:

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

This works, however this runs a second instance of make (I believe). Is there a right way to get the same functionality without running a 2nd instance of make?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

油焖大侠 2024-09-17 12:58:54

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

.PHONY : clearscr fresh clean all

all :
    compile executable

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

fresh : clean clearscr all

clearscr:
    clear

通过调用 make fresh,您首先获得 clean 目标,然后获得运行 clearclearscreen > 最后是完成这项工作的all

编辑 8 月 4 日

如果使用 make 的 -j 选项进行并行构建,会发生什么情况?
有一种方法可以修复顺序。来自制造手册,第 4.2 节:

但是,有时您会遇到这样的情况:您希望对要调用的规则施加特定的顺序,而不是在执行其中一个规则时强制更新目标。在这种情况下,您需要定义仅限订单的先决条件。可以通过在先决条件列表中放置管道符号 (|) 来指定仅顺序先决条件:管道符号左侧的任何先决条件都是正常的;右侧的任何先决条件都是仅顺序的: 目标:正常先决条件 |仅订购先决条件

正常的先决条件部分当然可能是空的。此外,您仍然可以为同一目标声明多行先决条件:它们会被适当地附加。请注意,如果您将同一个文件声明为普通先决条件和仅限订单先决条件,则普通先决条件优先(因为它们是仅限订单先决条件行为的严格超集)。

因此,makefile 变为

.PHONY : clearscr fresh clean all

all :
    compile executable

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

fresh : | clean clearscr all

clearscr:
    clear

EDIT Dec 5

运行多个 makefile 实例并不是什么大问题,因为任务中的每个命令都将是一个 子 shell 无论如何。但是您可以使用调用函数来获得可重用的方法。

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!")

Actually you are right: it runs another instance of make.
A possible solution would be:

.PHONY : clearscr fresh clean all

all :
    compile executable

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

fresh : clean clearscr all

clearscr:
    clear

By calling make fresh you get first the clean target, then the clearscreen which runs clear and finally all which does the job.

EDIT Aug 4

What happens in the case of parallel builds with make’s -j option?
There's a way of fixing the order. From the make manual, section 4.2:

Occasionally, however, you have a situation where you want to impose a specific ordering on the rules to be invoked without forcing the target to be updated if one of those rules is executed. In that case, you want to define order-only prerequisites. Order-only prerequisites can be specified by placing a pipe symbol (|) in the prerequisites list: any prerequisites to the left of the pipe symbol are normal; any prerequisites to the right are order-only: targets : normal-prerequisites | order-only-prerequisites

The normal prerequisites section may of course be empty. Also, you may still declare multiple lines of prerequisites for the same target: they are appended appropriately. Note that if you declare the same file to be both a normal and an order-only prerequisite, the normal prerequisite takes precedence (since they are a strict superset of the behavior of an order-only prerequisite).

Hence the makefile becomes

.PHONY : clearscr fresh clean all

all :
    compile executable

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

fresh : | clean clearscr all

clearscr:
    clear

EDIT Dec 5

It is not a big deal to run more than one makefile instance since each command inside the task will be a sub-shell anyways. But you can have reusable methods using the call function.

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!")
小姐丶请自重 2024-09-17 12:58:54

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

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< /code> <- _clean 保证以下配方执行顺序:clean 配方clear 配方recipe for all

可以使用以下方法与多个目标共享配方:将

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/ 你可以写:

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)

You already have a sequential solution which could be rewritten as:

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

This is correct and a very safe approach.

Sequential target execution is possible in GNU make with a proper dependency graph:

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

The above rules define the following dependency graph: fresh <- _all <- _clear <- _clean which guarantees the following recipe execution order: Recipe for clean, Recipe for clear, Recipe for all.

Recipes can be shared with multiple targets using:

target1 target2 target…:
    recipe1

Merging your script with the above concepts results in:

all _all : 
    compile executable
clean _clean :
    rm -f *.o $(EXEC)
clear _clear :
    clear
fresh: _all
_all: _clear
_clear: _clean

With syntactic sugar using chains.mk from https://github.com/pkoper/mk/ you can write:

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

Or better:

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)
別甾虛僞 2024-09-17 12:58:54

如果您从“fresh”目标中删除了 make all 行:

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

您只需运行命令 make fresh all,该命令将作为 make fresh; 执行;全部制作

有些人可能认为这是 make 的第二个实例,但它肯定不是 make 的子实例(make 中的 make),而这正是您的尝试似乎导致的结果。

If you removed the make all line from your "fresh" target:

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

You could simply run the command make fresh all, which will execute as make fresh; make all.

Some might consider this as a second instance of make, but it's certainly not a sub-instance of make (a make inside of a make), which is what your attempt seemed to result in.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文