nmake 警告 U4006:特殊宏未定义:'$@"'

Posted

技术标签:

【中文标题】nmake 警告 U4006:特殊宏未定义:\'$@"\'【英文标题】:nmake warning U4006: special macro undefined : '$@"'nmake 警告 U4006:特殊宏未定义:'$@"' 【发布时间】:2019-12-09 23:07:32 【问题描述】:

我想做一个简单的事情,如果目标名称是“enclose_io_memfs.obj” - 用clang构建它,如果它是另一个目标 - 用通用cl构建它。 所以我在 Makefile 中有下一个代码:

.c.obj:
    $(ECHO) target $@
    $(ECHO) target $(@)
!if "$@" == "enclose_io_memfs.obj"
    clang-cl -c enclose_io_memfs.c
    $(ECHO) clang used
!else
    cl -nologo -c enclose_io_memfs.c
    $(ECHO) cl used
!endif

但输出是:

./win32/Makefile.sub(1162) : warning U4006: special macro undefined : '$@"'
target enclose_io_memfs.obj
target enclose_io_memfs.obj
        cl -nologo -c enclose_io_memfs.c
enclose_io_memfs.c
cl used
compiling repu1sion_02 enclose_io_memfs.c

几个问题:

为什么 $@ 在前一行可用,而在 if 语句中不可用? 还有其他检测所需目标的方法吗?因为 .c.obj 调用了大约 30 个不同的目标

【问题讨论】:

以下两个答案似乎都没有回答您的问题。或许您可以详细说明它们为何不足? 【参考方案1】:

!if/!then 的内容是在读取 makefile 时完成的,而不是在调用目标时完成的。您正在尝试在 C 中的预处理器条件语句中使用变量。

以任何方式创建您的if/then,使您的makefile 在其中执行文件。我不是nmake/dos shell 专家,但对于bash,它会是这样的:

.c.obj:
    $(ECHO) target $@
    if [[ "$@" == "enclose_io_memfs.obj" ]] ;\
    then                                     \
        clang-cl -c $(@:.obj=.c)            ;\
        $(ECHO) clang used                  ;\
    else                                     \
        cl -nologo -c $(@:.obj=.c)          ;\
        $(ECHO) cl used                     ;\
    fi

【讨论】:

这当然需要你有一个 UNIX shell 并让nmake 来调用它而不是标准的 Windows 命令 shell... 是的;这就是我在回答中所说的。希望 OP 至少现在了解问题的根源并可以根据需要进行调整。【参考方案2】:

正如 Carl Norum 在他们的 answer 中指出的那样(也可以在 How do I use a conditional in nmake 中查看我的评论),!IF 在 makefile 被解析时使用,而 $@ 只有意思是当makefile在被解析之后运行

一种解决方案是以下makefile:

all: foo.obj bar.obj

.c.obj:
    @cmd /c if $(@B)==foo (                         \
    echo clang will be used to create $@ from $**   \
    ) else (                                        \
    echo cl will be used create $@ from $**         \
    )

给出:

clang will be used to create foo.obj from foo.c
cl will be used create bar.obj from bar.c

更新:更好、更具可扩展性的解决方案是以下 makefile:

CLANG_SOURCES = foo1.c foo2.c
CL_SOURCES    = bar1.c bar2.c

CLANG_OBJECTS = $(CLANG_SOURCES:.c=.obj)
CL_OBJECTS    = $(CL_SOURCES:.c=.obj)

all: $(CLANG_OBJECTS) $(CL_OBJECTS)

$(CLANG_OBJECTS):
    @echo using clang to create $@ from $?

$(CL_OBJECTS):
    @echo using cl to create $@ from $?

然后nmake -nologo 将给出(假设*.c 文件存在):

using clang to create foo1.obj from foo1.c
using clang to create foo2.obj from foo2.c
using cl to create bar1.obj from bar1.c
using cl to create bar2.obj from bar2.c

更新 2: 一个完整的工作示例。生成文件:

CLANG_SOURCES = foo1.c foo2.c
CL_SOURCES    = bar1.c bar2.c

CLANG_OBJECTS = $(CLANG_SOURCES:.c=.obj)
CL_OBJECTS    = $(CL_SOURCES:.c=.obj)
OBJECTS       = $(CLANG_OBJECTS) $(CL_OBJECTS)

CFLAGS        = -nologo
CLANG_CL      = C:\Progra~2\LLVM32\bin\clang-cl

all: $(OBJECTS)

$(OBJECTS): some_header.h

$(CLANG_OBJECTS):
    $(CLANG_CL) $(CFLAGS) -c $*.c

clean:
    del $(OBJECTS) 2>NUL

给,nmake -nologo:

        C:\Progra~2\LLVM32\bin\clang-cl -nologo -c foo1.c
        C:\Progra~2\LLVM32\bin\clang-cl -nologo -c foo2.c
        cl -nologo /c bar1.c bar2.c
bar1.c
bar2.c
Generating Code...

这里为方便起见,我们使用 nmake 的预定义推理规则来生成 $(CL_OBJECTS)

注意 nmake 不允许在这里使用 $<some_header.h 会导致 $?$** 出现问题。我之前的建议是错误的。

另外请注意,尽管在上面不明显,nmake 保留了目标文件对相应源文件的隐式依赖,即使添加了 clang 规则。例如:

C:\Users\Joe>nmake -nologo

C:\Users\Joe>copy /b foo1.c +,,
        1 file(s) copied.

C:\Users\Joe>nmake -nologo
        C:\Progra~2\LLVM32\bin\clang-cl -nologo -c foo1.c

C:\Users\Joe>copy /b bar1.c +,,
        1 file(s) copied.

C:\Users\Joe>nmake -nologo
        cl -nologo /c bar1.c
bar1.c

(这里copy 命令用作“触摸”:参见https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/copy/ 的示例。)

【讨论】:

编辑:对于touch,另见devblogs.microsoft.com/oldnewthing/?p=3843。

以上是关于nmake 警告 U4006:特殊宏未定义:'$@"'的主要内容,如果未能解决你的问题,请参考以下文章

字节顺序宏未正确定义[关闭]

导致非标准行为的 #pragma 是不是会导致 __STDC__ 宏未定义为 1?

Hive 宏未返回预期结果

Excel xlsm 文件宏未显示

nmake:如何为包含重新定义宏?

CLI 上的 NMake 宏定义,其中包含一个空格