使用CMake获取构建时Subversion修订版

时间:2010-09-23 16:48:28

标签: svn cmake

使用CMake,我可以使用Subversion_WC_INFO获取Subversion版本。但是,这仅在配置时发生 - 每个后续make都将使用此缓存版本。

我想在构建时间获得svn修订版(即,每次运行Make时)。我怎么能这样做?

6 个答案:

答案 0 :(得分:32)

这比需要做的更痛苦。您可以在构建时运行程序以从subversion中提取版本信息。 CMake本身可以使用其脚本语言作为此程序使用。您可以使用-P命令行标志运行任何CMake脚本。执行此操作的代码段(将放置在文件getsvn.cmake中):

# the FindSubversion.cmake module is part of the standard distribution
include(FindSubversion)
# extract working copy information for SOURCE_DIR into MY_XXX variables
Subversion_WC_INFO(${SOURCE_DIR} MY)
# write a file with the SVNVERSION define
file(WRITE svnversion.h.txt "#define SVNVERSION ${MY_WC_REVISION}\n")
# copy the file to the final header only if the version changes
# reduces needless rebuilds
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different
                        svnversion.h.txt svnversion.h)

这将提取subversion修订信息并将其写入头文件。此头文件仅在需要时更新,以避免一直重新编译。在您的程序中,您将包含该头文件以获取SVNVERSION定义。

运行脚本所需的CMakeLists.txt文件将是这样的:

# boilerplate
cmake_minimum_required(VERSION 2.8)
project(testsvn)

# the test executable
add_executable(test main.c ${CMAKE_CURRENT_BINARY_DIR}/svnversion.h)

# include the output directory, where the svnversion.h file is generated
include_directories(${CMAKE_CURRENT_BINARY_DIR})

# a custom target that is always built
add_custom_target(svnheader ALL
    DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/svnheader.h)

# creates svnheader.h using cmake script
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/svnheader.h
    COMMAND ${CMAKE_COMMAND} -DSOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}
                         -P ${CMAKE_CURRENT_SOURCE_DIR}/getsvn.cmake)

# svnversion.h is a generated file
set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/svnversion.h
    PROPERTIES GENERATED TRUE
    HEADER_FILE_ONLY TRUE)

# explicitly say that the executable depends on the svnheader
add_dependencies(test svnheader)

您需要使用add_custom_targetadd_custom_command,因为svnheader.h文件没有输入,它“从无处出现”到cmake。我已经在getsvn.cmake文件中处理了不必要的重建问题,所以如果subversion版本没有改变,重建“svnheader”目标基本上是一个无操作。

最后,示例main.c文件:

#include "svnversion.h"

int main(int argc, const char *argv[])
{
    printf("%d\n", SVNVERSION);
    return 0;
}

答案 1 :(得分:9)

我相信我发现问题@ janitor048有@ richq的答案。不幸的是,我没有足够的声誉评论他的答案 - 也许其他人可以复制和粘贴。

@richq使用自定义命令和自定义目标。自定义命令必须说服CMake创建标头,否则CMake脚本可以作为自定义目标的命令执行。虽然将始终执行自定义目标,但如果其输出文件已存在,则不会执行自定义命令。

解决方法是向自定义目标添加虚假依赖项(虚构文件),并告诉CMake自定义命令创建该文件。这足以确保始终执行自定义命令。幸运的是,CMake实际上并没有检查是否创建了这个文件。

richq有:

# a custom target that is always built
add_custom_target(svnheader ALL
    DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/svnheader.h)

# creates svnheader.h using cmake script
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/svnheader.h
    COMMAND ${CMAKE_COMMAND} -DSOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}
                         -P ${CMAKE_CURRENT_SOURCE_DIR}/getsvn.cmake)

这对我有用:

# a custom target that is always built
add_custom_target(svnheader ALL
    DEPENDS svn_header ) # svn_header is nothing more than a unique string

# creates svnheader.h using cmake script
add_custom_command(OUTPUT svn_header ${CMAKE_CURRENT_BINARY_DIR}/svnheader.h
    COMMAND ${CMAKE_COMMAND} -DSOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}
                         -P ${CMAKE_CURRENT_SOURCE_DIR}/getsvn.cmake)

此外,如果有人想使用Git,请将其用于CMake脚本:

#create a pretty commit id using git
#uses 'git describe --tags', so tags are required in the repo
#create a tag with 'git tag <name>' and 'git push --tags'

find_package(Git)
if(GIT_FOUND)
    execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags RESULT_VARIABLE res_var OUTPUT_VARIABLE GIT_COM_ID )
    if( NOT ${res_var} EQUAL 0 )
        set( GIT_COMMIT_ID "git commit id unknown")
        message( WARNING "Git failed (not a repo, or no tags). Build will not contain git revision info." )
    endif()
    string( REPLACE "\n" "" GIT_COMMIT_ID ${GIT_COM_ID} )
else()
    set( GIT_COMMIT_ID "unknown (git not found!)")
    message( WARNING "Git not found. Build will not contain git revision info." )
endif()

set( vstring "//version_string.hpp - written by cmake. changes will be lost!\n"
             "const char * VERSION_STRING = \"${GIT_COMMIT_ID}\"\;\n")

file(WRITE version_string.hpp.txt ${vstring} )
# copy the file to the final header only if the version changes
# reduces needless rebuilds
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different
                        version_string.hpp.txt ${CMAKE_CURRENT_BINARY_DIR}/version_string.hpp)

答案 2 :(得分:2)

我发现richq给出的答案并不完全符合人们的期望 - 即它只是在构建时不会自动更新文件svnversion.h(其中存储了svn修订信息)(但没有配置) )来源,即简单地调用make

这是他的程序的略微修改版本,应该提供必要的行为。原始解决方案的功劳当然是富裕。

他的getsvn.cmake剧本保持不变,这是为了完整(见他的评论和解释的答案)

INCLUDE(FindSubversion)
Subversion_WC_INFO(${SOURCE_DIR} MY)
FILE(WRITE svnversion.h.txt "#define SVNVERSION ${MY_WC_REVISION}\n")
EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E copy_if_different
      svnversion.h.txt svnversion.h)

现在的诀窍是使用具有不同签名的ADD_CUSTOM_COMMAND(请参阅CMake Documentation),即指定与命令关联的目标。然后,每次构建目标时都会执行它 - 与来自ADD_CUSTOM_TARGET的空目标一起被认为是过时的,这会产生所需的行为。这是CMakeLists.txt的样子(再次基于richq的脚本):

CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
PROJECT(testsvn)

# the test executable
ADD_EXECUTABLE(test main.c)

# include the output directory, where the svnversion.h file is generated
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})

# a custom target that is always built
ADD_CUSTOM_TARGET(revisiontag ALL)

# creates svnversion.h using cmake script
ADD_CUSTOM_COMMAND(TARGET revisiontag COMMAND ${CMAKE_COMMAND}
   -DSOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR} 
   -P ${CMAKE_CURRENT_SOURCE_DIR}/getsvn.cmake)

# explicitly say that the executable depends on custom target
add_dependencies(test revisiontag)

现在,每次构建目标版本标签时都会执行脚本getsvn.cmake,这意味着每次都会发出make。通过在FILE(..)中使用getsvn.cmake注释掉该行并手动修改svnversion.h.txt进行测试。

答案 3 :(得分:1)

我不知道任何特定于CMake的功能,但作为您的构建步骤之一,您可以轻松完成。从脚本运行svnversion并解析其输出,或者(更简单)运行TortoiseSVN的subwcrev程序(它也已移植到其他操作系统,因此它不是特定于Windows的。)

答案 4 :(得分:1)

理想情况下,这将是对richq答案的评论,但我还没有足够的声誉。如果管理员可以将其转换为1,那就太棒了。

我发现的一个问题(除了由Mark解决的依赖关系之外)是CMake中的SVN修订支持仅包括“基本修订版”,而不包括任何修饰符(特别是它是否为“M”(修改后) )建造)。

所以我没有使用建议的getsvn.cmake,而是使用了这个:

# Set current SVN version into a variable
execute_process(COMMAND svnversion ${SOURCE_ROOT} OUTPUT_VARIABLE MY_WC_REVISION OUTPUT_STRIP_TRAILING_WHITESPACE)
# write a file with the SVNVERSION define
file(WRITE svnversion.h.txt "#define SVN_REV \"${MY_WC_REVISION}\"\n")
# copy the file to the final header only if the version changes
# reduces needless rebuilds
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different
                    svnversion.h.txt svnversion.h)

因为我不只是想要修改当前目录,所以我将SOURCE_ROOT(SVN树顶部的目录)传递给自定义命令,而不是SOURCE_DIR,我计算出正确的值在我的CMakeLists.txt树中的其他地方

答案 5 :(得分:0)

我实际上在找同样的东西,我发现了这个:howto-use-cmake-with-cc-projects 这似乎更简单,但我想知道它是否与rq的答案一样有效。