如何在编译时在CMake生成的Makefile中运行命令?

Łuk*_*Lew 33 build-process cmake pre-compilation

我想将一些选项传递给编译器.该选项必须在编译时计算 - 每次调用'make'时,而不是'cmake',因此execute_process命令不会删除它.(可以?)

例如,将日期传递给g ++编译器,如下所示:

g++ prog.cpp -o prog -DDATETIME="17:09:2009,14:25"
Run Code Online (Sandbox Code Playgroud)

但是在编译时计算DATETIME.

知道如何在CMake中做到这一点吗?

赏金编辑:

最不受欢迎的解决方案将被接受.

请注意,我希望能够在编译时执行任意命令,而不仅仅是'date'.

编辑2:

它必须适用于Linux,Windows(VS),Mingw,Cygwin和OS X.您不能假设Ruby,Perl或Python,因为它们在Windows上是非标准的.你可以假设BOOST,但我想这没用.

目标是强制cmake生成Makefile(在Linux的情况下),当make执行时,将完成工作.

创建自定义*.h文件是可以的,但它必须由make的Makefile(或其他操作系统上的等效文件)启动.*.h的创建不必(也不应该)使用cmake.

Jes*_*erE 45

您将遗漏一些信息,例如您需要在哪些平台上运行此信息,以及是否有任何其他工具可供使用.如果你可以使用Python的Ruby,Perl,事情会变得简单得多.我假设你想在Unix和Windows pqlatform上运行,并且没有额外的工具可用.

如果您希望在预处理程序符号中输出命令,则最简单的方法是生成头文件,而不是摆弄命令行参数.请记住,CMake有一个脚本模式(-P),它只处理文件中的脚本命令,所以你可以这样做:

的CMakeLists.txt:

project(foo)  
cmake_minimum_required(VERSION 2.6)
add_executable(foo main.c custom.h)
include_directories(${CMAKE_CURRENT_BINARY_DIR})  
add_custom_command(OUTPUT custom.h 
    COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/custom.cmake)
Run Code Online (Sandbox Code Playgroud)

文件"custom.h"在编译时由命令"cmake -P custom.cmake"生成.custom.cmake看起来像这样:

execute_process(COMMAND uname -a 
    OUTPUT_VARIABLE _output OUTPUT_STRIP_TRAILING_WHITESPACE)
file(WRITE custom.h "#define COMPILE_TIME_VALUE \"${_output}\"")
Run Code Online (Sandbox Code Playgroud)

它执行命令(在本例中为"uname -a",您将用您希望的任何命令替换它),并将输出放在变量_output中,然后将其写入custom.h.请注意,这仅在命令输出单行时才有效.(如果您需要多行输出,则必须编写更复杂的custom.cmake,具体取决于您希望多行数据进入程序的方式.)

主程序如下所示:

#include <stdio.h>
#include "custom.h"
int main()
{
  printf("COMPILE_TIME_VALUE: %s\n", COMPILE_TIME_VALUE);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

如果你真的想在编译时计算编译器选项,事情变得更加棘手.对于Bourne-shell生成器,您只需将命令插入反引号即可.如果您在搞清楚引用时生气,请将命令的所有逻辑移到shell脚本中,这样您只需要输入mycommand.shadd_definitions():

if(UNIX)
  add_definitions(`${CMAKE_CURRENT_SOURCE_DIR}/custom-options.sh`)
endif()
Run Code Online (Sandbox Code Playgroud)

对于基于Windows批处理文件的生成器来说,事情很棘手,我没有一个好的解决方案.问题是PRE_BUILD命令不是作为与实际编译器调用相同的批处理文件的一部分执行(详细研究BuildLog.htm),所以我最初的想法不起作用(在一个PRE_BUILD步骤中生成custom.bat 然后执行在其上"调用custom.bat"以获取一个变量集,稍后可以在编译器命令行中引用该变量集.如果批处理文件中有相同的反引号,则可以解决问题.

希望这能给出一些想法和起点.

(现在到了不可避免的反问:你真的 想做什么?)

编辑:我不知道为什么你不想让CMake用于生成头文件.使用$ {CMAKE_COMMAND}将扩展为用于生成Makefile/.vcproj文件的CMake,并且由于CMake不支持可移植的Makefile/.vcproj文件,因此您需要在目标计算机上重新运行CMake.

由于这个明确的原因,CMake还有一堆实用程序命令(为列表运行"cmake -E").你可以这样做

add_custom_command(OUTPUT custom.h COMMAND ${CMAKE_COMMAND} -E copy file1.h file2.h)
Run Code Online (Sandbox Code Playgroud)

将file1.h复制到file2.h.

无论如何,如果你不想使用CMake生成头文件,你需要调用单独的.bat/.sh脚本来生成头文件,或者使用echo来执行:

add_custom_command(OUTPUT custom.h COMMAND echo #define SOMETHING 1 > custom.h)
Run Code Online (Sandbox Code Playgroud)

根据需要调整报价.


ako*_*sky 5

上面的解决方案(使用单独的 CMake 脚本文件来生成头文件)看起来非常灵活,但对于示例中所做的事情来说有点复杂。

另一种方法是在单个源文件和/或目标上设置 COMPILE_DEFINITIONS 属性,在这种情况下,定义的预处理器变量将只为源文件或目标中的文件被编译设置。

COMPILE_DEFINITIONS 属性的格式与 add_definitions 命令中使用的格式不同,其优点是您无需担心“-D”或“\D”语法,并且它们可以跨平台工作。

示例代码

-- CMakeLists.txt --

execute_process(COMMAND svnversion
    WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
    OUTPUT_VARIABLE SVN_REV)
string(STRIP ${SVN_REV} SVN_REV)

execute_process(COMMAND date "+%Y-%m-%d-%H:%M"
    OUTPUT_VARIABLE BUILD_TIME)
string(STRIP ${BUILD_TIME} BUILD_TIME)

set_source_files_properties(./VersionInfo.cpp
    PROPERTIES COMPILE_DEFINITIONS SVN_REV=\"${SVN_REV}\";BUILD_TIME=\"${BUILD_TIME}\"")
Run Code Online (Sandbox Code Playgroud)

第一行运行一个 shell 命令svnversion并将结果放在变量中SVN_REVstring(STRIP ...)需要该命令从输出中删除尾随换行符。

请注意,这是假设正在运行的命令是跨平台的。如果不是,您可能需要为不同的平台提供替代方案。例如,我使用 Unixdate命令的 cygwin 实现,并且有:

if(WIN32)
 execute_process(COMMAND cmd /C win_date.bat
    OUTPUT_VARIABLE BUILD_TIME)
else(WIN32)
  execute_process(COMMAND date "+%Y-%m-%d-%H:%M"
    OUTPUT_VARIABLE BUILD_TIME)
endif(WIN32)
string(STRIP ${BUILD_TIME} BUILD_TIME)
Run Code Online (Sandbox Code Playgroud)

对于日期命令,其中win_date.bat是以所需格式输出日期的 bat 文件。

这两个预处理器变量在文件中不可用,./VersionInfo.cpp但未在任何其他文件中设置。然后你可以有

-- 版本信息.cpp --

std::string version_build_time=BUILD_TIME;
std::string version_svn_rev=SVN_REV;
Run Code Online (Sandbox Code Playgroud)

这似乎可以很好地跨平台工作,并最大限度地减少了特定于平台的代码量。

  • 我看到的一个问题是文件 VersionInfo.cpp 不会改变,因此不会被重新编译。你会如何解决这个问题?简单地触发手动重新编译会使整个 cmake 魔法变得不那么有用,因为它只会直接使用 `__DATE__` 和 `__TIME__` 宏。 (2认同)