Makefile中的断言与if-else语句(仅在需要时运行)

huangapple go评论64阅读模式
英文:

Makefile assertions with if-else statements (How to run only when needed?)

问题

我在我的Makefile中定义了一些变量,当需要构建软件时,我将这些变量传递给编译器。然而,我需要确保这些变量在Makefile级别满足某些布尔恒等式。我目前正在执行类似以下的操作。

SHELL := /bin/bash

NUMBER := 1

all: test.x

%.x: %.c ASSERT
	@echo "Compiling $< -> $@ with NUMBER=$(NUMBER)."
	@gcc $< -D NUMBER=$(NUMBER) -o $@

ASSERT: Makefile
	@if (( $(NUMBER) < 0 || $(NUMBER) > 4 )); then \
		echo "ERROR: NUMBER must be positive and less than 4."; \
		exit 1; \
	fi
	@echo "Ran ASSERT. No errors found."

上述设置的问题在于,即使目标已经存在且没有发生更改,它也总是运行编译配方。

如何正确运行Makefile断言,以便仅在Makefile变量发生更改时创建目标?

英文:

I have some variables defined in my Makefile which I pass to a compiler when I need to build my software. However, I need to make sure that these variables satisfy certain boolean identities at the level of the Makefile. I am currently doing something like the following.

SHELL := /bin/bash

NUMBER := 1

all:	test.x

%.x:	%.c ASSERT
	 @echo &quot;Compiling $&lt; -&gt; $@ with NUMBER=$(NUMBER).&quot;
	 @gcc $&lt; -D NUMBER=$(NUMBER) -o $@

ASSERT:	Makefile
	@if (( $(NUMBER) &lt; 0 || $(NUMBER) &gt; 4 )); then \
		echo &quot;ERROR: NUMBER must be positive and less than 4.&quot;; \
		exit 1; \
	fi
	@echo &quot;Ran ASSERT. No errors found.&quot;

The problem with the above setup is that it always runs the compilation recipe even if the target already exists and nothing has changed.

How can I run Makefile assertions properly so that targets are made only when there are changes in the Makefile variables?

答案1

得分: 1

你提到所有的目标文件都依赖于先决条件ASSERT。但是这个先决条件并不存在,所以make会尝试创建它。一旦make尝试创建它,它就会被视为"非常新",因此任何依赖于它的目标都会被视为过时并重新构建。

然后,下次运行make时,它会发现先决条件ASSERT不存在,所以它会尝试创建它,并将其视为"非常新"。等等,依此类推。

如果你不希望重新构建一切,那么你的ASSERT的配方必须在成功时创建文件ASSERT

ASSERT: Makefile
        @...
        @touch $@
        @echo "运行 ASSERT。未发现错误。"

只要记住,如果你运行make NUMBER=10而不是编辑makefile,这不会产生任何影响。由于makefile没有更改,ASSERT不会过时,因此不会调用其配方。

补充说明

如果你只想验证没有人能够使用非法的NUMBER值,你可以直接使用make进行测试,而不是使用一个配方来执行它:

SHELL := /bin/bash

NUMBER := 1

$(if $(filter $(NUMBER), 0 1 2 3 4),\
   $(info NUMBER is good),\
   $(error ERROR: NUMBER must be positive and less than 4.))

all:    test.x

%.x:    %.c ASSERT
        @echo "Compiling $< -> $@ with NUMBER=$(NUMBER)."
        @gcc $< -D NUMBER=$(NUMBER) -o $@
英文:

You have said that all your object files depend on the prerequisite ASSERT. That prerequisite does not exist, so make will try to create it. Once make tries to create it, it will be considered "very new" and so any target that depends on it will be considered out of date and rebuilt.

Then the next time make runs, it sees that the prerequisite ASSERT does not exist, so it will try to create it and it will be considered "very new". Etc. etc.

If you don't want everything to be rebuilt, then your recipe for ASSERT must create the file ASSERT when it succeeds:

ASSERT: Makefile
        @...
        @touch $@
        @echo &quot;Ran ASSERT. No errors found.&quot;

Just remember that this won't have any impact if you run make NUMBER=10 rather than editing the makefile. Since the makefile isn't changed, ASSERT won't be out of date and so its recipe won't be invoked.

ETA

If all you want to do is verify that no one can use a NUMBER value that's illegal, ever, you can just test that directly with make rather than using a recipe to do it:

SHELL := /bin/bash

NUMBER := 1

$(if $(filter $(NUMBER), 0 1 2 3 4),\
   $(info NUMBER is good),\
   $(error ERROR: NUMBER must be positive and less than 4.))

all:    test.x

%.x:    %.c ASSERT
        @echo &quot;Compiling $&lt; -&gt; $@ with NUMBER=$(NUMBER).&quot;
        @gcc $&lt; -D NUMBER=$(NUMBER) -o $@

答案2

得分: 1

由于您的断言与 *.c 无关,您可以使 all 依赖于 ASSERT:

SHELL := /bin/bash

NUMBER := 1

all: ASSERT test.x

%.x: %.c 
    @echo "Compiling $< -> $@ with NUMBER=$(NUMBER)."
    @gcc $< -D NUMBER=$(NUMBER) -o $@

ASSERT: Makefile
    @if (( $(NUMBER) < 0 || $(NUMBER) > 4 )); then \
        echo "ERROR: NUMBER must be positive and less than 4."; \
        exit 1; \
    fi
    @echo "Ran ASSERT. No errors found."
英文:

As your assertion is not related to *.c, can you make all depend on ASSERT:

SHELL := /bin/bash

NUMBER := 1

all: ASSERT test.x

%.x: %.c 
    @echo &quot;Compiling $&lt; -&gt; $@ with NUMBER=$(NUMBER).&quot;
    @gcc $&lt; -D NUMBER=$(NUMBER) -o $@

ASSERT: Makefile
    @if (( $(NUMBER) &lt; 0 || $(NUMBER) &gt; 4 )); then \
        echo &quot;ERROR: NUMBER must be positive and less than 4.&quot;; \
        exit 1; \
    fi
    @echo &quot;Ran ASSERT. No errors found.&quot;

huangapple
  • 本文由 发表于 2023年2月18日 22:34:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/75494036.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定