Подтвердить что ты не робот

Используйте CMake для получения версии svn времени сборки

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

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

4b9b3361

Ответ 1

Это больше боль, чем нужно. Вы можете запустить программу во время сборки, чтобы извлечь информацию о версии из подрывной деятельности. Сам CMake можно использовать в качестве этой программы, используя свой язык сценариев. Вы можете запустить любой CMake script с помощью флага командной строки -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, который вам нужен для запуска script, будет примерно таким:

# 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;
}

Ответ 2

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

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

Обходной путь заключается в том, чтобы добавить фиктивную зависимость (воображаемый файл) к настраиваемой цели и сообщить 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 script:

#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)

Ответ 3

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

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

Его getsvn.cmake script остается неизменным, здесь он для полноты (см. его ответ для комментариев и объяснений)

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 (основанное снова на script на 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)

Теперь script getsvn.cmake выполняется каждый раз, когда создается целевая ревизия tag, что означает каждый раз, когда выдается make. Протестируйте, прокомментировав строку с помощью FILE(..) в getsvn.cmake и вручную отредактировав svnversion.h.txt.

Ответ 4

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

Ответ 5

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