如何将 Makefile 条件与 bash 命令一起使用作为 Makefile 规则的准备步骤?

How to use Makefile conditionals with bash commands as a preparation step for Makefile rules?

提问人:Roland Tóbiás 提问时间:11/11/2023 最后编辑:Roland Tóbiás 更新时间:11/11/2023 访问量:79

问:

出于一些实际原因,在工作目录中为 Makefile 规则准备位置对我来说会很有用。考虑一个假设的情况,如果值为 true,我需要在编译之前将文件移动到实际目录中。我尝试将条件与命令一起使用,但只有当我将其关闭为规则时,它才有效:$(VAR)test.fifeqmv

VAR=true

cond:
ifeq ($(VAR),true)
   @mv dir/test.f .
endif

test.x: test.o
   ifort -o test.x test.o

test.o: test.f
   ifort -c -o test.o test.f

但是,在这种情况下,仅执行规则,但不执行。 有什么办法吗condtest.xtest.o

ifeq ($(VAR),true)
     @mv dir/test.f .
endif

是否应该始终在不阻止 Makefile 中(其他)规则执行的情况下执行?

为了完整起见,我附上了我想处理的真正条件块:

ifeq ($(VAR),"./")
  @if [ -d mod ]; then \
     mv mod/* .; \
     rm -rf mod; \
     fi
  @if [ -d gen ]; then \
     mv gen/* .; \
     rm -rf gen; \
   fi
  @if [ -d extra ]; then \
     mv extra/* .; \
     rm -rf extra; \
     fi
  @if [ -d lnk ]; then \
     mv lnk/* .; \
     rm -rf lnk; \
     fi
else
   @if [ ! -d mod ]; then \
      mkdir -p mod; \
      mv mod_*.f* mod; \
   fi
   @if [ ! -d txt ]; then \
      mkdir -p txt; \
      mv *.txt txt; \
   fi
   @if [ ! -d gen ]; then \
      mkdir -p gen; \
      mv *.f* gen; \
   fi
   @if [ ! -d lnk ]; then \
      mkdir -p lnk; \
      mv *.o lnk; \
      mv *.mod lnk; \
   fi
endif
bash makefile 条件语句

评论

0赞 HolyBlackCat 11/11/2023
为什么不制定一个简单的规则来复制文件,比如?test.f: dir/test.fcp $< $@
0赞 Roland Tóbiás 11/11/2023
@HolyBlackCat 在我的真实案例中,我们谈论的是多个动作。我需要从目录 、 和 中移动所有(一堆)文件,然后删除这些目录,然后开始执行其他规则。在这种情况下,您有什么建议?genmodtxt
0赞 John Bollinger 11/11/2023
我的第一个倾向是建议你在更高的层次上解决你的问题,首先避免执行所有这些动作的需要。
0赞 John Bollinger 11/11/2023
在任何情况下,shell 命令都不能直接出现在 makefile 中,除非在配方中。Makefile 不是脚本,一旦你内化了它,你就会开始编写更好的脚本。
0赞 Roland Tóbiás 11/11/2023
@John Bollinger,我会通过在原始位置使用这些文件来避免这种情况,但我的合作者希望将所有文件都放在 CWD 中(而我不想要)。为此,我想有一个技巧将它们放在用户想要的地方(我非常讨厌 CWD 中呕吐的所有文件)。

答:

1赞 aazizzailani 11/11/2023 #1
VAR=true

ifeq ($(VAR),true)
    DUMMY_PREREQUISITE = dummy_prerequisite
else
    DUMMY_PREREQUISITE =
endif

$(DUMMY_PREREQUISITE):
    @mv dir/test.f .

test.x: $(DUMMY_PREREQUISITE) test.o
    ifort -o test.x test.o

test.o: $(DUMMY_PREREQUISITE) test.f
    ifort -c -o test.o test.f

这样,目标将始终在任何其他规则之前执行,从而确保在不影响 Makefile 中的其他规则的情况下执行条件块。dummy_prerequisite

评论

0赞 Roland Tóbiás 11/11/2023
感谢您发布此答案。但是,我遇到了一个错误:.Makefile:4: *** recipe commences before first target. Stop.
0赞 Philippe 11/11/2023
为什么有必要放入 test.x 规则?dummy_prerequisite
0赞 aazizzailani 11/11/2023
@RolandTóbiás 错误“Makefile:4: *** 配方在第一个目标之前开始。停止“,建议格式问题。确保 Makefile 中的行以制表符开头,而不是空格。@Philippe 在规则中包含可确保条件块在其他规则之前运行,如果为 true,则移动。这样可以保持顺序,确保在编译之前移动文件。dummy_prerequisitetest.xdir/test.fVARtest.x
0赞 Philippe 11/11/2023
因为已经取决于.如果您从规则中删除,它会起作用吗?test.odummy_prerequisitedummy_prerequisitetest.x
1赞 John Bollinger 11/11/2023
应该指出的是,这种方法要求几乎是每条规则的先决条件。这在原则上是可行的,但它很混乱,而且有点难以维护。它还排除了对任何事情的依赖内置规则。如果一个人想更有选择性地选择哪些规则所依赖,它就更混乱、更难维护。$(dummy_prerequisite)$(dummy_prerequisite)
2赞 MadScientist 11/11/2023 #2

To be clear, is not a shell and makefiles are not shell scripts. So you can't plop some shell scripting down anywhere in the makefile you want (whether it's prefixed with TAB or not) and have it work. can invoke a shell to run some shell scripting, but it will only do that for shell operations that appear in specific places. Any text outside of those specific places, is considered makefile text (even if it's preceded by a TAB) and you'll get parser errors if it's not valid makefile syntax (which most shell scripting is not).makemake

One place a shell script can appear is in the recipe of a rule (this is where it's indented with TAB: the TAB tells make which lines are part of the recipe and which are not). The issue with this is that the recipe of a rule is only invoked if the target of the rule needs to be rebuilt. There are various answers here which give some thoughts about how to do this.

The other place a shell script can appear (if you are using GNU Make) is in the function. So one option to solve your problem would be this:$(shell ..)

VAR := true

ifeq ($(VAR),true)
 __dummy := $(shell mv dir/test.f .)
endif

The assignment is there just in case the shell script generates some output to stdout.__dummy

Of course if the script to be invoked is very complex it will be annoying to put it into a single invocation of but it can be done: shell syntax is flexible enough for that. Or you can use multiple invocations. Or you can put all the commands into a separate shell script file and invoke that inside of .$(shell ...)$(shell ...)

Ultimately I think that John's idea above where there's a separate operation that users need to invoke to "prep" the workspace is the best one. This operation can be a makefile target like then you have:make setup

.PHONY: setup
setup:
        mv dir/file.f .

etc. Be sure that is not the first target in the makefile and it will only be run if the user runs .setupmake setup

评论

0赞 Roland Tóbiás 11/12/2023
Thanks for posting this answer. The function seems to be well suited for my stupid task.$(shell ...)