svn - tutorial - Use CMake para obtener la revisión de Subversion en tiempo de compilación
cmake win64 download (6)
Con CMake puedo obtener la revisión de Subversion_WC_INFO
usando Subversion_WC_INFO
. Sin embargo, esto solo ocurre en el momento de la configuración; cada marca subsiguiente usará esta revisión en caché.
Me gustaría obtener la revisión svn en el momento de la compilación (es decir, cada vez que se ejecuta Make). ¿Cómo puedo hacer esto?
Creo que encontré el problema que tenía @ janitor048 con la respuesta de @ richq. Desafortunadamente, no tengo suficiente reputación para comentar su respuesta, tal vez alguien más pueda copiar y pegar.
@richq usa tanto un comando personalizado como un objetivo personalizado. El comando personalizado es necesario para convencer a CMake de que se creará el encabezado, de lo contrario, el script CMake podría ejecutarse como un comando para el objetivo personalizado. Si bien siempre se ejecutará un destino personalizado, un comando personalizado no lo hará si su archivo de salida ya existe.
La solución es agregar una dependencia falsa (un archivo imaginario) al destino personalizado, y decirle a CMake que el comando personalizado crea ese archivo. Esto es suficiente para garantizar que el comando personalizado siempre se ejecute. Afortunadamente, CMake no comprueba realmente si este archivo está creado o no.
richq tiene:
# 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)
Esto funciona para mí:
# 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)
Además, si alguien desea usar Git, use esto para el script 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)
Descubrí que la respuesta dada por richq no hace exactamente lo que uno esperaría, es decir, no actualiza automáticamente el archivo svnversion.h
(en el que se almacena la información de revisión svn) cuando solo se construye (pero no se configura) la fuente, es decir cuando simplemente llamando a make
.
Aquí hay una versión ligeramente modificada de su procedimiento que debería proporcionar el comportamiento necesario. El crédito por la solución original va a richq, por supuesto.
Su script getsvn.cmake
permanece sin cambios, aquí está para completar (vea su respuesta para comentarios y explicación)
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)
Ahora el truco es usar ADD_CUSTOM_COMMAND
con una firma diferente (ver Documentación de CMake ), es decir, especificar el destino con el que se debe asociar el comando. Luego se ejecuta cada vez que el objetivo se ADD_CUSTOM_TARGET
, junto con un objetivo vacío de ADD_CUSTOM_TARGET
que siempre se considera desactualizado, esto proporciona el comportamiento deseado. Así es como podría verse el CMakeLists.txt
(basado nuevamente en el script de 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)
Ahora el script getsvn.cmake
se ejecuta cada vez que se getsvn.cmake
el getsvn.cmake
revisión de destino, lo que significa que cada vez make
se emite make
. getsvn.cmake
prueba comentando la línea con FILE(..)
en getsvn.cmake
y editando manualmente svnversion.h.txt
.
Esto es más doloroso de lo que debe ser. Puede ejecutar un programa en el momento de la compilación para extraer la información de la versión de Subversion. CMake se puede usar como este programa usando su lenguaje de scripting. Puede ejecutar cualquier script de CMake utilizando el indicador de línea de comando -P. El fragmento de código para hacer esto sería (para ubicarse en el archivo 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)
Esto extrae la información de revisión de subversión y la escribe en un archivo de encabezado. Este archivo de encabezado solo se actualiza cuando es necesario para evitar volver a compilar todo el tiempo. En su programa, incluiría ese archivo de encabezado para obtener en la definición de SVNVERSION.
El archivo CMakeLists.txt
que necesitarías para ejecutar el script sería algo como esto:
# 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
usar add_custom_target
y add_custom_command
ya que no hay entradas para el archivo svnheader.h, "aparece de la nada" a cmake. Sin embargo, ya traté el problema de las reconstrucciones innecesarias en el archivo getsvn.cmake, por lo que la reconstrucción del objetivo "svnheader" es básicamente no operativa si la revisión de subversión no ha cambiado.
Finalmente, un ejemplo de archivo main.c
:
#include "svnversion.h"
int main(int argc, const char *argv[])
{
printf("%d/n", SVNVERSION);
return 0;
}
Idealmente, esto sería un comentario sobre la respuesta de richq, pero todavía no tengo suficiente reputación. Si un administrador pudiera convertirlo en uno, sería genial.
Uno de los problemas que encontré (aparte de la dependencia que resolvió Mark) es que el soporte de revisión de SVN en CMake solo incluye la "revisión base", no cualquier modificador (en particular si es una compilación "M" (modificada)) .
Así que en lugar de la sugerida getsvn.cmake
, usé esto:
# 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)
Y como no solo quiero la revisión del directorio actual, paso SOURCE_ROOT (el directorio para la parte superior del árbol SVN) al comando personalizado, en lugar de SOURCE_DIR, y trabajo el valor correcto para eso en otro lugar en mi árbol CMakeLists.txt
No conozco ninguna funcionalidad específica de CMake para hacer esto, pero es bastante fácil hacerlo usted mismo, como uno de sus pasos de compilación. Ejecute svnversion
desde un script y analice su salida, o (más fácil) ejecute el programa subwcrev
de TortoiseSVN (que también se ha transferido a otros sistemas operativos, por lo que no es específico de Windows).
Realmente estoy buscando lo mismo y encontré esto: howto-use-cmake-with-cc-projects Parece mucho más simple, pero me pregunto si es tan eficiente como la respuesta de rq.