Question

Je voudrais passer quelques options à un compilateur. L'option devrait être calculée au moment de la compilation - à chaque fois que 'make' est appelé, pas quand 'cmake', la commande execute_process ne la coupe donc pas. (le fait-il?)

Par exemple, passer une date à un compilateur g ++ comme celui-ci:

g++ prog.cpp -o prog -DDATETIME="17:09:2009,14:25"

Mais avec DATETIME calculé lors de la compilation.

Avez-vous une idée de la façon de le faire dans CMake?

Modification des primes:

Une solution minimale sera acceptée.

Veuillez noter que je souhaite pouvoir exécuter une commande arbitraire au moment de la compilation, pas seulement "date".

Modifier 2:

Il doit fonctionner sous Linux, Windows (VS), Mingw, Cygwin et OS X. Vous ne pouvez pas assumer Ruby, Perl ou Python car ils ne sont pas standard sous Windows. Vous pouvez supposer que BOOST, mais je suppose que cela ne sert à rien.

Le but est de forcer cmake à générer un Makefile (dans le cas de Linux) qui, lorsque make sera exécuté, fera le travail.

Créer un fichier * .h personnalisé est acceptable, mais il doit être lancé par un Makefile (ou l’équivalent d’un autre système d’exploitation) par make. La création de * .h ne doit pas (et ne devrait pas obliger) utiliser cmake.

Était-ce utile?

La solution

Vous omettez certaines informations, telles que les plateformes dont vous avez besoin pour l’exécuter et s’il existe des outils supplémentaires que vous pouvez utiliser. Si vous pouvez utiliser Ruby, Perl, de Python, les choses deviennent beaucoup plus simples. Mauvais supposez que vous voulez exécuter à la fois pqlatform sur Unix et Windows et qu'il n'y a pas d'outils supplémentaires disponibles.

Si vous voulez que la sortie de la commande apparaisse dans un symbole de préprocesseur, le Le moyen le plus simple est de générer un fichier d’en-tête au lieu de bidouiller avec des paramètres de ligne de commande. Rappelez-vous que CMake a un mode script (-P) où il ne traite que les commandes de script du fichier, vous pouvez donc faire quelque chose comme ça:

CMakeLists.txt:

project(foo)  
cmake_minimum_required(VERSION 2.6)
add_executable(foo main.c custom.h)
include_directories(${CMAKE_CURRENT_BINARY_DIR})  
add_custom_command(OUTPUT custom.h 
    COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/custom.cmake)

Le fichier "custom.h " est généré au moment de la compilation par la commande " cmake -P custom.cmake " ;. custom.cmake ressemble à ceci:

execute_process(COMMAND uname -a 
    OUTPUT_VARIABLE _output OUTPUT_STRIP_TRAILING_WHITESPACE)
file(WRITE custom.h "#define COMPILE_TIME_VALUE \"${_output}\"")

Il exécute la commande (dans ce cas, "uname -a", vous la remplacerez). avec la commande de votre choix) et met la sortie dans la variable _output, qu'il écrit ensuite dans custom.h. Notez que cela ne fera que fonctionne si la commande affiche une seule ligne. (Si vous avez besoin de plusieurs lignes sortie, vous devrez écrire un custom.cmake plus complexe, en fonction de comment vous voulez les données multilignes dans votre programme.)

Le programme principal ressemble à ceci:

#include <stdio.h>
#include "custom.h"
int main()
{
  printf("COMPILE_TIME_VALUE: %s\n", COMPILE_TIME_VALUE);
  return 0;
}

Si vous voulez réellement calculer les options du compilateur au moment de la compilation, les choses deviennent beaucoup plus compliquées. Pour les générateurs Bourne-shell, vous pouvez simplement insère la commande dans les backticks. Si vous vous énervez en découvrant en citant, déplacez toute la logique de votre commande dans un script shell afin il vous suffit de mettre mycommand.sh dans votre add_definitions ():

if(UNIX)
  add_definitions(`${CMAKE_CURRENT_SOURCE_DIR}/custom-options.sh`)
endif()

Pour les générateurs basés sur des fichiers de commandes Windows, les choses sont beaucoup plus difficiles, et je ne pas avoir une bonne solution. Le problème est que les commandes PRE_BUILD ne sont pas exécutés dans le même fichier batch que le compilateur réel invocation (étudiez le BuildLog.htm pour plus de détails), donc mon idée de départ ne fonctionnait pas (générer un custom.bat dans une étape PRE_BUILD puis faire " appelez custom.bat " dessus pour obtenir un ensemble variable qui peut être plus tard référencé dans la ligne de commande du compilateur). S'il y a un équivalent de des backticks dans les fichiers de commandes, cela résoudrait le problème.

J'espère que cela vous donne quelques idées et points de départ.

(Passons maintenant à l'inévitable contre-question: qu'êtes-vous vraiment essayant de faire?)

EDIT: Je ne vois pas pourquoi vous ne voulez pas laisser CMake être utilisé pour générer le fichier d’en-tête. Utiliser $ {CMAKE_COMMAND} étendra le CMake utilisé pour générer les fichiers Makefiles / .vcproj, et comme CMake ne supporte pas vraiment les fichiers Makefiles / .vcproj portables, vous devrez réexécuter CMake sur les machines cibles.

CMake a également un tas de commandes d’utilitaires (Exécuter "cmake -E" pour une liste) pour cette raison explicite. Vous pouvez par exemple faire

add_custom_command(OUTPUT custom.h COMMAND ${CMAKE_COMMAND} -E copy file1.h file2.h)

pour copier fichier1.h dans fichier2.h.

Quoi qu'il en soit, si vous ne souhaitez pas générer les fichiers d'en-tête à l'aide de CMake, vous devrez soit appeler des scripts .bat / .sh distincts pour générer le fichier d'en-tête, soit le faire à l'aide de echo:

add_custom_command(OUTPUT custom.h COMMAND echo #define SOMETHING 1 > custom.h)

Ajustez les guillemets si nécessaire.

Autres conseils

La solution ci-dessus (utiliser un fichier de script CMake distinct pour générer un fichier d’en-tête) semble très flexible mais un peu compliquée pour ce qui est fait dans l’exemple.

Une alternative consiste à définir une propriété COMPILE_DEFINITIONS sur un fichier source individuel ou sur une cible, auquel cas les variables de préprocesseur définies ne seront définies que pour le fichier source ou les fichiers de la cible sont compilés.

Les propriétés COMPILE_DEFINITIONS ont un format différent de celui utilisé dans la commande add_definitions et présentent l’avantage de ne pas avoir à se soucier de " -D " ou "\ D" syntaxe et ils travaillent sur plusieurs plates-formes.

Exemple de code

- CMakeLists.txt -

execute_process(COMMAND svnversion
    WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
    OUTPUT_VARIABLE SVN_REV)
string(STRIP ${SVN_REV} SVN_REV)

execute_process(COMMAND date "+%Y-%m-%d-%H:%M"
    OUTPUT_VARIABLE BUILD_TIME)
string(STRIP ${BUILD_TIME} BUILD_TIME)

set_source_files_properties(./VersionInfo.cpp
    PROPERTIES COMPILE_DEFINITIONS SVN_REV=\"${SVN_REV}\";BUILD_TIME=\"${BUILD_TIME}\"")

La première ligne exécute une commande shell svnversion et place le résultat dans la variable SVN_REV . La commande string (STRIP ...) est nécessaire pour supprimer les caractères de fin de ligne de la sortie.

Notez que ceci suppose que la commande en cours d'exécution est multi-plateforme. Sinon, vous aurez peut-être besoin d'alternatives pour différentes plateformes. Par exemple, j'utilise l'implémentation cygwin de la commande Unix date et j'ai:

if(WIN32)
 execute_process(COMMAND cmd /C win_date.bat
    OUTPUT_VARIABLE BUILD_TIME)
else(WIN32)
  execute_process(COMMAND date "+%Y-%m-%d-%H:%M"
    OUTPUT_VARIABLE BUILD_TIME)
endif(WIN32)
string(STRIP ${BUILD_TIME} BUILD_TIME)

pour les commandes de date, où win_date.bat est un fichier bat qui affiche la date au format souhaité.

Les deux variables de préprocesseur ne sont pas disponibles dans le fichier ./ VersionInfo.cpp mais ne sont définies dans aucun autre fichier. Vous pourriez alors avoir

- VersionInfo.cpp -

std::string version_build_time=BUILD_TIME;
std::string version_svn_rev=SVN_REV;

Cela semble bien fonctionner sur toutes les plateformes et minimise la quantité de code spécifique à la plate-forme.

J'utiliserais l'approche suivante:

  1. Créez un fichier exécutable qui affiche la date du jour sur la sortie standard (cette fonctionnalité n'est pas disponible pour CMake)
  2. Ajouter une cible toujours considérée comme obsolète
  3. Laisser la cible invoquer un autre script CMake
  4. Laisser le script CMake invoqué générer un fichier d’en-tête

Exemple de code pour cela:

--- CMakeLists.txt ---

PROJECT(Foo)
ADD_EXECUTABLE(RetreiveDateTime ${CMAKE_CURRENT_SOURCE_DIR}/datetime.cpp)
ADD_CUSTOM_TARGET(GenerateFooHeader
                  COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/Generate.cmake
                  WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
                  DEPENDS RetreiveDateTime)
ADD_EXECUTABLE(Foo "test.cpp" "${CMAKE_CURRENT_BINARY_DIR}/generated.h")
ADD_DEPENDENCIES(Foo GenerateFooHeader)

--- Generate.cmake ---

EXECUTE_PROCESS(COMMAND ${CMAKE_BINARY_DIR}/RetreiveDateTime OUTPUT_VARIABLE DATETIMESTRING)
MESSAGE(STATUS "DATETIME=\"${DATETIMESTRING}\"")
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/generated.h.in ${CMAKE_CURRENT_BINARY_DIR}/generated.h @ONLY)

--- generate.h.in ---

#pragma once

#define DATETIMESTRING "@DATETIMESTRING@"

--- datetime.cpp ---

#include <iostream>
#include <ctime>
#include <cstring>

int main(int, char*[])
{
 time_t now;
 time(&now);
 tm * timeinfo = localtime(&now);

 char * asstring = asctime(timeinfo);
 asstring[strlen(asstring) - 1] = '\0'; // Remove trailing \n
 std::cout << asstring;
 return 0;
}

--- test.cpp ---

#include "generated.h"

#include <iostream>

int main(int, char*[])
{
 std::cout << DATETIMESTRING << std::endl;
 return 0;
}

Il en résulte un en-tête " généré.h " qui est régénéré sur chaque build. Si vous n'avez pas besoin de DATETIME, cet exemple pourrait être considérablement simplifié, car CMake ne dispose pas de cette fonctionnalité et un programme doit être créé pour simuler cette fonctionnalité.

Je penserais cependant plus que deux fois avant de faire ceci. Rappelez-vous que le fichier d'en-tête sera régénéré à chaque exécution de make, rendant votre cible invalide à tous . Vous ne pourrez jamais avoir un fichier binaire considéré comme étant à jour.

Est-ce que ça marche?

d=`perl -e"print qq(Whatever calculated at runtime);"`; g++ prog.cpp -o prog -DDATETIME=$d
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top