2010-09-23 10 views
16

Con CMake puedo obtener la revisión de svn usando Subversion_WC_INFO. Sin embargo, esto solo ocurre en el momento de configuración; cada marca posterior utilizará esta revisión en caché.use CMake para obtener la revisión de svn de compilación

Me gustaría obtener la revisión svn en tiempo de compilación (es decir, cada vez que se ejecuta make). ¿Cómo puedo hacer esto?

Respuesta

29

Esto es más de un trabajo difícil de lo necesario. Puede ejecutar un programa en tiempo de compilación para extraer la información de la versión de subversión. CMake en sí mismo se puede utilizar como este programa utilizando su lenguaje de scripting. Puede ejecutar cualquier script CMake usando el indicador de línea de comando -P. El trozo de código para hacer esto sería (para ser colocado 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 la subversión revisión y lo escribe en un archivo de cabecera. Este archivo de encabezado solo se actualiza cuando es necesario para evitar la recompilación todo el tiempo. En su programa, debe incluir ese archivo de encabezado para obtener la definición de SVNVERSION.

El archivo CMakeLists.txt que lo que se necesita 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) 

Es necesario utilizar add_custom_target y add_custom_command ya que no hay entradas en el archivo svnheader.h aparece, es" de la nada "a cmake. Sin embargo, ya traté el tema de las reconstrucciones innecesarias en el archivo getsvn.cmake, por lo que reconstruir el objetivo "svnheader" es básicamente no operativo si la revisión de subversión no ha cambiado.

Por último, un ejemplo de archivo main.c:

#include "svnversion.h" 

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

respuesta impresionante . No sabía que los scripts de cmake podían ejecutarse en tiempo de compilación. – paleozogt

+0

Esto realmente no hace exactamente lo que uno esperaría. No actualiza automáticamente 'revision.h' cuando se llama a' make'. Su solución tiene que ser modificada levemente - Lo pondré en una respuesta, entonces puedo usar etiquetas de código ... – janitor048

+0

@janitor Acabo de probar esto de nuevo, como lo hice originalmente, y definitivamente funciona como se esperaba. Incluso obtiene un mensaje "Generando svnheader.h" en cada llamada a realizar, que no hace en su versión modificada. Además, 'revision.h' no está en el código, ¿entonces quizás tengas un error de copiar y pegar? – richq

1

No conozco ninguna funcionalidad específica de CMake para hacer esto, pero es bastante fácil hacerlo tú mismo, como uno de tus pasos de compilación. Ejecute svnversion desde un script y analice su salida, o (más fácil) ejecute el programa TortoiseSVN subwcrev (que también se ha portado a otros sistemas operativos, por lo que no es específico de Windows).

0

En realidad estoy buscando lo mismo y me encontré con esto: howto-use-cmake-with-cc-projects Parece mucho más simple, pero me pregunto si es tan eficiente como de rq responder.

+0

El "problema" con este enfoque es que solo actualiza la información de revisión en el momento de la configuración, es decir, al emitir 'cmake'. No se actualiza cuando se realizan compilaciones delta por '' '' '' '' ''. – janitor048

2

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 cual se almacena la información de revisión svn) cuando solo construye (pero no configura) la fuente , es decir, cuando simplemente se llama al make.

Aquí hay una versión ligeramente modificada de su procedimiento que debería proporcionar el comportamiento necesario. El crédito para la solución original va a richq, por supuesto.

Su guión getsvn.cmake se mantiene sin cambios, aquí es por el bien de la integridad (véase su respuesta a los comentarios y explicaciones)

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 CMake Documentation), concretamente especificando el objetivo con el cual el comando debería estar asociado. Luego se ejecuta cada vez que se genera el objetivo, junto con un objetivo vacío del ADD_CUSTOM_TARGET que siempre se considera obsoleto, lo que le da el comportamiento deseado.Aquí es lo que el CMakeLists.txt podría ser similar (de nuevo basado en el guión 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 guión getsvn.cmake se ejecuta cada vez que el revisiontag objetivo se construye, lo que significa que cada vez que se emite make. Pruebe comentando la línea con FILE(..) en getsvn.cmake y editando manualmente svnversion.h.txt.

+0

Gracias - ese objetivo de revisión me ayudó a hacer que esto funcione. –

8

Creo que encontré el problema @ janitor048 con la respuesta de @ richq. Desafortunadamente, no tengo la reputación suficiente para comentar su respuesta, tal vez alguien más puede copiar y pegar.

@richq utiliza un comando personalizado y un destino 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 destino personalizado. Mientras que un objetivo personalizado siempre se ejecutará, un comando personalizado no lo hará si su archivo de salida ya existe.

La solución consiste en 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 en realidad no verifica si este archivo se crea 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 utilizar git, utilizar esto para el guión 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) 
Cuestiones relacionadas