我想将一些选项传递给编译器。 该选项必须在编译时计算 - 每次调用'make'时,而不是'cmake',因此execute_process命令不会删除它。 (是吗?)
例如,将日期传递给g ++编译器,如:
g++ prog.cpp -o prog -DDATETIME="17:09:2009,14:25"
但是在编译时计算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。
答案 0 :(得分: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)
文件“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}\"")
它执行命令(在这种情况下为“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;
}
如果您确实想在编译时计算编译器选项,
事情变得更加棘手。对于Bourne-shell生成器,您可以
在反引号中插入命令。如果你在弄清楚的时候生气了
引用,将命令的所有逻辑移到shell脚本中
你只需要在你的add_definitions()中加上mycommand.sh
:
if(UNIX)
add_definitions(`${CMAKE_CURRENT_SOURCE_DIR}/custom-options.sh`)
endif()
对于基于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)
将file1.h复制到file2.h。
无论如何,如果你不想使用CMake生成头文件,你需要调用单独的.bat / .sh脚本来生成头文件,或者使用echo来执行:
add_custom_command(OUTPUT custom.h COMMAND echo #define SOMETHING 1 > custom.h)
根据需要调整引用。
答案 1 :(得分:4)
上面的解决方案(使用单独的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}\"")
第一行运行shell命令svnversion
并将结果放入变量SVN_REV
。需要string(STRIP ...)
命令才能从输出中删除尾随的换行符。
请注意,这是假设正在运行的命令是跨平台的。如果没有,您可能需要为不同平台提供替代方案。例如,我使用Unix date
命令的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)
表示日期命令,其中win_date.bat
是一个以所需格式输出日期的bat文件。
文件./VersionInfo.cpp
中没有两个预处理器变量,但未在任何其他文件中设置。你可以拥有
- VersionInfo.cpp -
std::string version_build_time=BUILD_TIME;
std::string version_svn_rev=SVN_REV;
这似乎可以很好地跨平台工作,并最大限度地减少平台特定代码的数量。
答案 2 :(得分:2)
我会使用以下方法:
此示例代码:
--- CMakeLists.txt ---
PROJECT(Foo)
ADD_EXECUTABLE(RetreiveDateTime ${CMAKE_CURRENT_SOURCE_DIR}/datetime.cpp)
ADD_CUSTOM_TARGET(GenerateFooHeader
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/Generate.cmake
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS RetreiveDateTime)
ADD_EXECUTABLE(Foo "test.cpp" "${CMAKE_CURRENT_BINARY_DIR}/generated.h")
ADD_DEPENDENCIES(Foo GenerateFooHeader)
--- Generate.cmake ---
EXECUTE_PROCESS(COMMAND ${CMAKE_BINARY_DIR}/RetreiveDateTime OUTPUT_VARIABLE DATETIMESTRING)
MESSAGE(STATUS "DATETIME=\"${DATETIMESTRING}\"")
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/generated.h.in ${CMAKE_CURRENT_BINARY_DIR}/generated.h @ONLY)
--- generate.h.in ---
#pragma once
#define DATETIMESTRING "@DATETIMESTRING@"
--- datetime.cpp ---
#include <iostream>
#include <ctime>
#include <cstring>
int main(int, char*[])
{
time_t now;
time(&now);
tm * timeinfo = localtime(&now);
char * asstring = asctime(timeinfo);
asstring[strlen(asstring) - 1] = '\0'; // Remove trailing \n
std::cout << asstring;
return 0;
}
--- test.cpp ---
#include "generated.h"
#include <iostream>
int main(int, char*[])
{
std::cout << DATETIMESTRING << std::endl;
return 0;
}
这会生成一个标题“generated.h”,它会在每次构建时重新生成。如果你不需要DATETIME这个例子可以大大简化,因为CMake缺少这个功能,必须构建一个程序来模拟功能。
然而,在这样做之前,我会考虑两次以上。请记住,每次运行make时都会重新生成头文件,使目标在所有次时无效。您永远不会拥有一个被认为是最新的二进制文件。答案 3 :(得分:-1)
这有用吗?
d=`perl -e"print qq(Whatever calculated at runtime);"`; g++ prog.cpp -o prog -DDATETIME=$$d