CMake:如何打破 PRE_LINK 无限循环?
CMake: how to break a PRE_LINK infinite loop?
我正在尝试使用内部版本号自动标记我的应用程序登录行。这个应用程序是一个没有图形的普通香草 C 应用程序 UI;它用于命令行,因此它是 "simple" 一个。
登录 ID 位于 "template" 源文件中,该文件由 CMake 使用 configure_file()
命令自定义。最近,我想在这个登录 ID 中包含一个内部版本号。因此,定制不能再在 CMake 时静态完成,而是每次调用 make 时。
要实现这一点,在 CMake 中有两种可能性:
- add_custom_target(),但即使源树中没有任何其他变化也不会反映树的状态,它也会被触发;
- add_custom_command(),只有当应用程序(目标)需要再次linked时才会触发。
我选择了第二种解决方案,但没有成功。
这是我的 CMakeLists.txt 的摘录,登录 ID 在文件 ErrAux.c 中(模板在PROJECT_SOURCE_DIR,配置在PROJECT_BINARY_DIR):
add_executable(anathem ... ${PROJECT_BINARY_DIR}/ErrAux.c ...)
add_custom_command(TARGET anathem PRE_LINK
COMMAND "${CMAKE_COMMAND}" "-DVERS=${PROJECT_VERSION}"
"-DSRC=${PROJECT_SOURCE_DIR}"
"-DDST=${PROJECT_BINARY_DIR}"
-P "${CMAKE_HOME_DIRECTORY}/BuildNumber.cmake"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
COMMENT "Numbering build"
VERBATIM
)
这会在 link 步骤之前启动脚本 BuildNumber.cmake。它计算下一个内部版本号并自定义 ErrAux.c 和 configure_file()
.
它工作正常,除了...
它发生在 make 序列的后期,对 ErrAux.c 的更新未被注意到。 executable 中的登录 ID 包含之前的内部版本号。
下次我 运行 make, make 注意到生成的 ErrAux.c 比它的目标模块年轻,导致它被再次编译,这反过来又导致 link 触发内部版本号更新。即使没有其他文件发生更改并且无法打破此循环,也会发生这种情况。这在编译日志中清楚地显示:
Scanning dependencies of target anathem
[ 13%] Building C object AnaThem/CMakeFiles/anathem.dir/ErrAux.c.o
[ 14%] Linking C executable anathem
Numbering build
3.0.0-45
[ 36%] Built target anathem
关键似乎是 add_custom_command(TARGET ...)
不能像 add_custom_command(OUTPUT ...)
那样指定输出文件。但后一种形式无法在 PRE_LINK 模式下触发。
作为变通方法,我强制编译到 "refresh" 目标模块:
add_custom_command(TARGET anathem PRE_LINK
COMMAND "${CMAKE_COMMAND}" "-DVERS=${PROJECT_VERSION}"
"-DSRC=${PROJECT_SOURCE_DIR}"
"-DDST=${PROJECT_BINARY_DIR}"
-P "${CMAKE_HOME_DIRECTORY}/BuildNumber.cmake"
COMMAND echo "Numbering"
COMMAND echo "${CMAKE_C_COMPILER}" "$(C_DEFINES)" "$(C_INCLUDES)" "$(C_FLAGS)" -c "${PROJECT_BINARY_DIR}/ErrAux.c"
COMMAND "${CMAKE_C_COMPILER}" "$(C_DEFINES)" "$(C_INCLUDES)" "$(C_FLAGS)" -c "${PROJECT_BINARY_DIR}/ErrAux.c"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
COMMENT "Numbering build"
VERBATIM
)
自定义登录标识后强制进行显式编译。它模仿在各种 Makefile 中发现的内容,并且我的生产不安全。 CMake 和 make.
都是作弊技巧
更新:需要选项 -c
才能将 link 步骤推迟到最终的应用程序链接过程。
此添加在 link 中造成严重破坏,如日志所示,您在其中看到双重编译(标准 make 和add_custom_command()
一个):
Scanning dependencies of target anathem
[ 13%] Building C object AnaThem/CMakeFiles/anathem.dir/ErrAux.c.o
[ 14%] Linking C executable anathem
Numbering build
3.0.0-47
Numbering
/usr/bin/cc -DANA_DEBUG=1 -I/home/prog/projects/AnaLLysis/build/AnaThem -I/home/prog/projects/AnaLLysis/AnaThem -g /home/prog/projects/AnaLLysis/build/AnaThem/ErrAux.c
/usr/lib/gcc/x86_64-redhat-linux/6.3.1/../../../../lib64/crt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
collect2: error: ld returned 1 exit status
AnaThem/CMakeFiles/anathem.dir/build.make:798: recipe for target 'AnaThem/anathem' failed
make[2]: *** [AnaThem/anathem] Error 1
如果我强制进行完全重新编译,以确保编译所有源代码,包括 *main.c*,我在 `main` 上得到相同的错误。
唯一合乎逻辑的解释是我的手动 C 调用是错误的,并且以某种方式破坏了重要信息。我用 *readelf* 检查过,`main` 仍然在 *main.c.o* 的符号 table 中,并且 link 步骤(来自文件 * link.txt*)。
更新:即使 link 正确,我仍然遇到无限循环综合症。生成的应用程序的登录 ID 仍然落后于实际构建计数器。
有人能告诉我正确的方向吗?
仅供参考,我是 CMake 的新手,所以我可能会做错事。不要犹豫批评我的错误。
解决的关键是把生成的模块放在make希望找到的地方。 CMake 以非平凡的方式组织构建树。
我在 add_custom_command()
添加的编译中的缺点是相信默认情况下二进制文件将存储在 "usual" CMake 位置。由于我手动伪造了我的编译器命令,所以情况并非如此。
我在源目录中找到了模块,这是 WORKING_DIRECTORY
选项的结果,名称为 ErrAux.o 而不是 ErrAux.c.o.
为了获得正确的行为,我强制输出位置:
-o "${PROJECT_BINARY_DIR}/CMakeFiles/anathem.dir/ErrAux.c.o"
现在,当我再次 运行 make 时,什么也没有发生,因为什么都没有改变。
附带问题
为了使解决方案可移植(如果需要),CMakeFiles 和 anathem.dir 目录是否有 CMake 变量?或者在后一种情况下,对于当前目标作为 "anathem" 作为 add_custom_command()
?
中的目标名称
我正在尝试使用内部版本号自动标记我的应用程序登录行。这个应用程序是一个没有图形的普通香草 C 应用程序 UI;它用于命令行,因此它是 "simple" 一个。
登录 ID 位于 "template" 源文件中,该文件由 CMake 使用 configure_file()
命令自定义。最近,我想在这个登录 ID 中包含一个内部版本号。因此,定制不能再在 CMake 时静态完成,而是每次调用 make 时。
要实现这一点,在 CMake 中有两种可能性:
- add_custom_target(),但即使源树中没有任何其他变化也不会反映树的状态,它也会被触发;
- add_custom_command(),只有当应用程序(目标)需要再次linked时才会触发。
我选择了第二种解决方案,但没有成功。
这是我的 CMakeLists.txt 的摘录,登录 ID 在文件 ErrAux.c 中(模板在PROJECT_SOURCE_DIR,配置在PROJECT_BINARY_DIR):
add_executable(anathem ... ${PROJECT_BINARY_DIR}/ErrAux.c ...)
add_custom_command(TARGET anathem PRE_LINK
COMMAND "${CMAKE_COMMAND}" "-DVERS=${PROJECT_VERSION}"
"-DSRC=${PROJECT_SOURCE_DIR}"
"-DDST=${PROJECT_BINARY_DIR}"
-P "${CMAKE_HOME_DIRECTORY}/BuildNumber.cmake"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
COMMENT "Numbering build"
VERBATIM
)
这会在 link 步骤之前启动脚本 BuildNumber.cmake。它计算下一个内部版本号并自定义 ErrAux.c 和 configure_file()
.
它工作正常,除了...
它发生在 make 序列的后期,对 ErrAux.c 的更新未被注意到。 executable 中的登录 ID 包含之前的内部版本号。
下次我 运行 make, make 注意到生成的 ErrAux.c 比它的目标模块年轻,导致它被再次编译,这反过来又导致 link 触发内部版本号更新。即使没有其他文件发生更改并且无法打破此循环,也会发生这种情况。这在编译日志中清楚地显示:
Scanning dependencies of target anathem
[ 13%] Building C object AnaThem/CMakeFiles/anathem.dir/ErrAux.c.o
[ 14%] Linking C executable anathem
Numbering build
3.0.0-45
[ 36%] Built target anathem
关键似乎是 add_custom_command(TARGET ...)
不能像 add_custom_command(OUTPUT ...)
那样指定输出文件。但后一种形式无法在 PRE_LINK 模式下触发。
作为变通方法,我强制编译到 "refresh" 目标模块:
add_custom_command(TARGET anathem PRE_LINK
COMMAND "${CMAKE_COMMAND}" "-DVERS=${PROJECT_VERSION}"
"-DSRC=${PROJECT_SOURCE_DIR}"
"-DDST=${PROJECT_BINARY_DIR}"
-P "${CMAKE_HOME_DIRECTORY}/BuildNumber.cmake"
COMMAND echo "Numbering"
COMMAND echo "${CMAKE_C_COMPILER}" "$(C_DEFINES)" "$(C_INCLUDES)" "$(C_FLAGS)" -c "${PROJECT_BINARY_DIR}/ErrAux.c"
COMMAND "${CMAKE_C_COMPILER}" "$(C_DEFINES)" "$(C_INCLUDES)" "$(C_FLAGS)" -c "${PROJECT_BINARY_DIR}/ErrAux.c"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
COMMENT "Numbering build"
VERBATIM
)
自定义登录标识后强制进行显式编译。它模仿在各种 Makefile 中发现的内容,并且我的生产不安全。 CMake 和 make.
都是作弊技巧更新:需要选项 -c
才能将 link 步骤推迟到最终的应用程序链接过程。
此添加在 link 中造成严重破坏,如日志所示,您在其中看到双重编译(标准 make 和add_custom_command()
一个):
Scanning dependencies of target anathem
[ 13%] Building C object AnaThem/CMakeFiles/anathem.dir/ErrAux.c.o
[ 14%] Linking C executable anathem
Numbering build
3.0.0-47
Numbering
/usr/bin/cc -DANA_DEBUG=1 -I/home/prog/projects/AnaLLysis/build/AnaThem -I/home/prog/projects/AnaLLysis/AnaThem -g /home/prog/projects/AnaLLysis/build/AnaThem/ErrAux.c
/usr/lib/gcc/x86_64-redhat-linux/6.3.1/../../../../lib64/crt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
collect2: error: ld returned 1 exit status
AnaThem/CMakeFiles/anathem.dir/build.make:798: recipe for target 'AnaThem/anathem' failed
make[2]: *** [AnaThem/anathem] Error 1
更新:即使 link 正确,我仍然遇到无限循环综合症。生成的应用程序的登录 ID 仍然落后于实际构建计数器。
有人能告诉我正确的方向吗?
仅供参考,我是 CMake 的新手,所以我可能会做错事。不要犹豫批评我的错误。
解决的关键是把生成的模块放在make希望找到的地方。 CMake 以非平凡的方式组织构建树。
我在 add_custom_command()
添加的编译中的缺点是相信默认情况下二进制文件将存储在 "usual" CMake 位置。由于我手动伪造了我的编译器命令,所以情况并非如此。
我在源目录中找到了模块,这是 WORKING_DIRECTORY
选项的结果,名称为 ErrAux.o 而不是 ErrAux.c.o.
为了获得正确的行为,我强制输出位置:
-o "${PROJECT_BINARY_DIR}/CMakeFiles/anathem.dir/ErrAux.c.o"
现在,当我再次 运行 make 时,什么也没有发生,因为什么都没有改变。
附带问题
为了使解决方案可移植(如果需要),CMakeFiles 和 anathem.dir 目录是否有 CMake 变量?或者在后一种情况下,对于当前目标作为 "anathem" 作为 add_custom_command()
?