计算的宏名称

Posted

技术标签:

【中文标题】计算的宏名称【英文标题】:Computed macro names 【发布时间】:2019-01-07 12:31:41 【问题描述】:

GNU Make 程序证明了computed names 的特性。我必须使用 Microsoft nmake 程序,并且需要检查宏是否具有指定值或至少已定义。

makefile 定义了一个宏FOO,其值为DEVICE。此外,宏PLAT_DEVICE 可能是用值1 定义的。在 GNU make 语法中,您可以使用

FOO=DEVICE
PLAT_DEVICE=1

!if "$(PLAT_$(FOO))" == "1"
!message I am here.
!endif

FOO 的值定义了此处检查的其他宏。不幸的是,nmake 不明白这一点。条件总是为假,消息永远不会显示。

如何使用 nmake 来实现?

【问题讨论】:

【参考方案1】:

编辑:这是我的第一个答案,并没有那么有用。请参阅我关于使用环境变量的第二个答案。


很遗憾,NMAKE 没有 GNU Make 中的计算变量名。

但它确实允许从其他宏构造宏名称:请参阅https://docs.microsoft.com/en-us/cpp/build/defining-an-nmake-macro?view=vs-2017。

因此,根据您的情况,以下解决方法可能有效:

DEVICE = 1
PLAT_DEVICE_$(DEVICE) =

!ifdef PLAT_DEVICE_1
!message I am here.
!endif

或者,作为这个想法的微小变化:

DEVICE = 1
PLAT_DEVICE_$(DEVICE) = plat_device

!if "plat_device" == "$(PLAT_DEVICE_1)"
!message I am here.
!endif

【讨论】:

【参考方案2】:

简答:您可以使用环境变量代替make宏来计算名称,例如,这个makefile:

FOO=DEVICE
PLAT_DEVICE=1

!if [set PLAT_DEVICE=$(PLAT_DEVICE)] # add macro to environment
!endif

!if [cmd /c if "%PLAT_$(FOO)%"=="1" exit 1] # do test
!message I am here.
!endif

将给予:

>nmake -l
I am here.

或者这个变种,

FOO=DEVICE
PLAT_DEVICE=42

!if [set FOO=$(FOO)] && \
    [set PLAT_DEVICE=$(PLAT_DEVICE)]
!endif

!if [cmd /c if "%PLAT_$(FOO)%"=="42" exit 1]
!message Test 1a: I am here.
!endif

!if [cmd /c if "%PLAT_%FOO%%"=="42" exit 1]
!message Test 1b: I am here too.
!endif

all:
    @     echo Test 2a: %%PLAT_$(FOO)%%
    @call echo Test 2b: %%PLAT_%%FOO%%%%

将给予:

>nmake -l
Test 1a: I am here.
Test 1b: I am here too.
Test 2a: 42
Test 2b: 42

这个答案的一个主要缺点是必须为每个 make 宏显式地完成导出。 nmake -l 开关只是 /nologo 的缩写(未记录?)。


更长的答案。 上面的 makefile 使用了几种技术或技巧。我将尝试在包含四个项目的列表中解开这些内容。

第 1 项: 第一个注释 nmake 与 GNU make 不同,它具有持久性环境变量(在 nmake 中也称为 变量)。例如,

all: bar
    @echo Test 3: %%BAR%%

bar:
    @set BAR=Hello World!

给予:

>nmake -l
Test 3: Hello World!

第 2 项:您可以将宏转换为变量,或创建变量,至少在两个地方:

在配方命令行中,如第 1 项所示,或 在预处理期间执行的命令中,方括号[...]

例如,

AAA = FOO
BBB = BAR
FOO_BAR = This works!
FOO_BAZ = This also works!

!if [set FOO_BAR=$(FOO_BAR)] && \
    [set FOO_BAZ=$(FOO_BAZ)] && \
    [set CCC=%$(AAA)_$(BBB)%]
!endif

all:
    @echo Test 4: %%$(AAA)_$(BBB)%%
    @echo Test 5: %%CCC%%

给予:

>nmake -l
Test 4: This works!
Test 5: This works!

>nmake -l BBB=BAZ
Test 4: This also works!
Test 5: This also works!

关于这个有两个奇怪的地方。首先,似乎每个变量都必须使用自己的命令进行设置。例如,

!if [set FOO_BAR=$(FOO_BAR) && set FOO_BAZ=$(FOO_BAZ)]
!endif

不起作用(我可能在这里遗漏了一些明显的东西)。其次,&& 连接词在这里几乎是无关紧要的:它不会在 nmake 中短路,我们会丢弃结果,所以可能像 + 这样的其他任何东西也可以正常工作。

第 3 项: 第 2 项并没有真正说明嵌套:显示的嵌套是变量中的宏。但真正的嵌套确实有效。生成文件:

AAA = FOO
BBB = BAR
FOO_BAR = This works!

!if [set AAA=$(AAA)] && \
    [set BBB=$(BBB)] && \
    [set FOO_BAR=$(FOO_BAR)]
!endif

!if [cmd /c if "%%AAA%_%BBB%%"=="This works!" exit 1]
!message Test 6: I am here
!endif

all:
    @call echo Test 7: %%%%AAA%%_%%BBB%%%%

将给予:

>nmake -l
Test 6: I am here
Test 7: This works!

在配方中,call 似乎需要模拟延迟扩展;见Stephan's answer 至Variables are not behaving as expected。测试 6 中不需要它,=="This works!" 测试。

第4项:预处理测试如下:

!if [cmd /c if "%PLAT_$(FOO)%"=="42" exit 1]
!message Test 1a: I am here.
!endif

!if [cmd /c if "%PLAT_%FOO%%"=="42" exit 1]
!message Test 1b: I am here too.
!endif

类似于 Unix 的 test 命令,除了这里的 TRUE=1。

【讨论】:

以上是关于计算的宏名称的主要内容,如果未能解决你的问题,请参考以下文章

Xcode 7 - 宏名称必须是标识符

您可以在#define 宏名称中使用 a.b 表示法吗?

<命令行>:2:10: 缺少宏名称

如何为多行宏调用获取宏名称 __LINE__?

预定义宏,C语言预定义的宏详解

MFC 消息类型