Используйте CMake для получения ревизии Subversion во время сборки - PullRequest
18 голосов
/ 23 сентября 2010

С помощью CMake я могу получить ревизию Subversion, используя Subversion_WC_INFO. Однако это происходит только во время настройки - каждая последующая сборка будет использовать эту кэшированную ревизию.

Я бы хотел получить ревизию SVN в время сборки (т. Е. Каждый раз, когда запускается Make). Как я могу это сделать?

Ответы [ 6 ]

32 голосов
/ 25 сентября 2010

Это скорее боль, чем нужно. Вы можете запустить программу во время сборки, чтобы извлечь информацию о версии из Subversion. Сам CMake может быть использован в качестве этой программы на языке сценариев. Вы можете запустить любой скрипт CMake, используя флаг командной строки -P. Кусок кода для этого будет (Для размещения в файле 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_target и add_custom_command, поскольку в файле svnheader.h нет входных данных, он "появляется из ниоткуда" для cmake. Я уже имел дело с проблемой ненужных пересборок в файле getsvn.cmake, поэтому пересборка цели «svnheader» в основном не работает, если версия subversion не изменилась.

Наконец, пример main.c file:

#include "svnversion.h"

int main(int argc, const char *argv[])
{
    printf("%d\n", SVNVERSION);
    return 0;
}
9 голосов
/ 02 октября 2011

Кажется, я обнаружил проблему, возникшую у @ janitor048 с ответом @ richq. К сожалению, у меня недостаточно репутации, чтобы комментировать его ответ - может быть, кто-то другой сможет скопировать и вставить.

@ richq использует как пользовательскую команду, так и пользовательскую цель. Пользовательская команда необходима, чтобы убедить CMake, что заголовок будет создан, в противном случае сценарий CMake может быть выполнен как команда для пользовательской цели. Хотя пользовательская цель всегда будет выполняться, пользовательская команда не будет выполняться, если ее выходной файл уже существует.

Обходной путь - добавить поддельную зависимость (воображаемый файл) к пользовательской цели и сообщить CMake, что пользовательская команда создает этот файл. Этого достаточно, чтобы гарантировать, что пользовательская команда всегда выполняется. К счастью, CMake на самом деле не проверяет, создан этот файл или нет.

Ричк имеет:

# 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 голосов
/ 16 августа 2011

Я обнаружил, что ответ, данный richq, не совсем соответствует ожиданиям, а именно: он не обновляет автоматически файл svnversion.h (в котором хранится информация о ревизии svn), когда создается (но не настраивается)source, т. е. при простом вызове make.

. Вот слегка измененная версия его процедуры, которая должна обеспечивать необходимое поведение.Конечно, кредит на оригинальное решение идет на richq.

Его 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 ), а именно указать цель, с которой должна быть связана команда.Затем он выполняется каждый раз при построении цели - вместе с пустой целью из 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.

вручную.
1 голос
/ 09 мая 2018

В идеале это будет комментарий к ответу richq, но у меня пока недостаточно репутации. Если бы администратор мог преобразовать его в один, это было бы здорово.

Одна проблема, которую я обнаружил (кроме той зависимости, которая была решена Марком), заключается в том, что поддержка ревизий SVN в CMake включает только «базовую ревизию», а не любые модификаторы (в частности, является ли это «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

1 голос
/ 23 сентября 2010

Я не знаю какой-либо специфичной для CMake функциональности, чтобы сделать это, но это достаточно легко сделать самостоятельно, как один из шагов вашей сборки. Запустите svnversion из скрипта и проанализируйте его вывод или (проще) запустите TortoiseSVN subwcrev программу (которая также была перенесена в другие операционные системы, поэтому она не относится к Windows).

0 голосов
/ 22 октября 2010

Я на самом деле ищу то же самое, и я нашел это: howto-use-cmake-with-cc-projects Кажется, это намного проще, но мне интересно, насколько он эффективен, как ответ RQ.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...