2010-09-23 3 views
16

Avec CMake je peux obtenir la révision de svn using Subversion_WC_INFO. Cependant, cela ne se produit qu'au moment de la configuration - chaque make suivant utilisera cette révision en cache.utiliser CMake pour obtenir la révision de svn de temps de construction

Je voudrais obtenir la révision de svn au le temps de construction (c.-à-d., Chaque fois que make est exécuté). Comment puis-je faire ceci?

Répondre

29

C'est plus douloureux à faire que ce qu'il doit être. Vous pouvez exécuter un programme au moment de la construction pour extraire les informations de version de subversion. CMake lui-même peut être utilisé comme ce programme en utilisant son langage de script. Vous pouvez exécuter n'importe quel script CMake en utilisant l'indicateur de ligne de commande -P. Le morceau de code pour ce faire serait (à placer dans le fichier 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) 

Cet extrait les informations de révision de la subversion et l'écrit dans un fichier d'en-tête. Ce fichier d'en-tête n'est mis à jour que lorsque cela est nécessaire pour éviter toute recompilation en permanence. Dans votre programme, vous incluez ce fichier d'en-tête pour obtenir le paramètre SVNVERSION.

Le fichier CMakeLists.txt que vous devez exécuter le script serait quelque chose comme ceci:

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

Vous devez utiliser add_custom_target et add_custom_command car il n'y a pas d'entrées dans le fichier svnheader.h, il « apparaît de nulle part "à cmake. J'ai déjà traité le problème des reconstructions inutiles dans le fichier getsvn.cmake, donc la reconstruction de la cible "svnheader" est fondamentalement un no-op si la révision de subversion n'a pas changé.

Enfin, un exemple de fichier main.c:

#include "svnversion.h" 

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

réponse Impressionnant . Je ne savais pas que les scripts cmake pouvaient être exécutés au moment de la construction. – paleozogt

+0

Cela ne fait pas exactement ce que l'on attendrait. Il ne met pas automatiquement à jour 'revision.h' quand' make' est appelé. Votre solution doit être légèrement modifiée - Je vais le mettre dans une réponse, donc je peux utiliser des balises de code ... – janitor048

+0

@janitor Tout testé à nouveau, comme je l'ai fait à l'origine, et il fonctionne vraiment comme prévu. Vous obtenez même un message "Generating svnheader.h" sur chaque appel à faire, ce que vous ne faites pas dans votre version modifiée. De plus, 'revision.h' n'est pas dans le code, alors peut-être que vous avez une erreur de copier-coller? – richq

1

Je ne connais aucune fonctionnalité spécifique à CMake pour ce faire, mais il est assez facile de le faire vous-même, comme l'une de vos étapes de construction. Exécutez svnversion à partir d'un script et analysez sa sortie, ou (plus facile) exécutez le programme subwcrev de TortoiseSVN (qui a également été porté sur d'autres systèmes d'exploitation, donc ce n'est pas spécifique à Windows).

0

Je suis à la recherche de la même chose et je trouve ceci: howto-use-cmake-with-cc-projects Il semble beaucoup plus simple mais je me demande si elle est aussi efficace que celle des rq répondre.

+0

Le "problème" de cette approche est qu'elle ne met à jour que les informations de révision au moment de la configuration, c'est-à-dire lors de l'émission de "cmake". Il n'est pas mis à jour lors de la construction de delta par les 'make's suivants. – janitor048

2

J'ai trouvé que la réponse donnée par richq ne correspond pas exactement à ce que l'on attendrait - à savoir qu'elle ne met pas automatiquement à jour le fichier svnversion.h (dans lequel les informations de révision svn sont stockées) en ne construisant que la source C'est-à-dire en appelant simplement make.

Voici une version légèrement modifiée de sa procédure qui devrait fournir le comportement nécessaire. Le crédit pour la solution originale va naturellement à richq.

Son scénario getsvn.cmake reste inchangé, ici il est dans l'intérêt de l'exhaustivité (voir sa réponse aux commentaires et explications)

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) 

Maintenant, l'astuce consiste à utiliser ADD_CUSTOM_COMMAND avec une signature différente (voir CMake Documentation), à savoir spécifier la cible avec laquelle la commande doit être associée. Il est ensuite exécuté chaque fois que la cible est construite - avec une cible vide de ADD_CUSTOM_TARGET qui est toujours considérée comme obsolète, ce qui donne le comportement souhaité.Voici ce que l'CMakeLists.txt pourrait ressembler (basé à nouveau sur le script par 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) 

Maintenant le script getsvn.cmake est exécuté chaque fois que le revisiontag cible est construit, ce qui signifie que chaque fois make est émis. Testez en commentant la ligne avec FILE(..) dans getsvn.cmake et en modifiant manuellement svnversion.h.txt.

+0

Merci - cette cible de revisiontag m'a aidé à faire fonctionner cela. –

8

je crois que je trouve le problème @ janitor048 a eu avec la réponse de @ richq. Malheureusement, je n'ai pas assez de réputation pour commenter sa réponse - peut-être que quelqu'un d'autre peut copier et coller.

@richq utilise à la fois une commande personnalisée et une cible personnalisée. La commande personnalisée est nécessaire pour convaincre cmake que l'en-tête sera créé, sinon le script cmake pourrait être exécuté en tant que commande pour la cible personnalisée. Alors qu'une cible personnalisée sera toujours exécutée, une commande personnalisée ne le sera pas si son fichier de sortie existe déjà.

La solution consiste à ajouter une dépendance bogus (un fichier imaginaire) à la cible personnalisée et dire CMake que la commande personnalisée crée ce fichier. Cela suffit pour garantir que la commande personnalisée est toujours exécutée. Heureusement, cmake ne vérifie pas si ce fichier est créé ou non.

richq a:

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

cela fonctionne pour moi:

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

Aussi, si quelqu'un souhaite utiliser git, utiliser pour le 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) 
Questions connexes