Domanda

Vorrei passare alcune opzioni a un compilatore. L'opzione dovrebbe essere calcolata in fase di compilazione, ogni volta che viene invocato 'make', non quando 'cmake', quindi il comando execute_process non la interrompe. (lo fa?)

Ad esempio passare una data a un compilatore g ++ in questo modo:

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

Ma con DATETIME calcolato al momento della compilazione.

Qualche idea su come farlo in CMake?

Modifica della taglia:

Sarà accettata una soluzione almeno hacker.

Nota che voglio essere in grado di eseguire un comando arbitrario in fase di compilazione, non solo 'data'.

Modifica 2:

Deve funzionare su Linux, Windows (VS), Mingw, Cygwin e OS X. Non puoi assumere Ruby, Perl o Python in quanto non sono standard su Windows. Puoi supporre BOOST, ma immagino che non sia utile.

L'obiettivo è forzare cmake a generare Makefile (nel caso di Linux) che quando make viene eseguito, farà il lavoro.

La creazione del file personalizzato * .h è ok, ma deve essere avviata da un Makefile (o equivalente su un altro sistema operativo) da make. La creazione di * .h non deve (e non dovrebbe) usare cmake.

È stato utile?

Soluzione

Stai tralasciando alcune informazioni, ad esempio quali piattaforme ti servono per eseguirlo e se ci sono altri strumenti che puoi usare. Se puoi usare Ruby, Perl, di Python, le cose diventano molto più semplici. Malato supponiamo che tu voglia eseguire sia su Unix che su Windows che non ci sono strumenti extra disponibili.

Se si desidera l'output del comando in un simbolo di preprocessore, il il modo più semplice è generare un file header invece di armeggiare con parametri della riga di comando. Ricorda che CMake ha una modalità script (-P) dove elabora solo i comandi di script nel file, quindi puoi farlo fai qualcosa del genere:

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)

Il file " custom.h " viene generato in fase di compilazione dal comando "cmake -P custom.cmake " ;. custom.cmake è simile al seguente:

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

Esegue il comando (in questo caso " uname -a " ;, lo sostituirai con qualunque comando tu voglia), e mette l'output nella variabile _output, che quindi scrive in custom.h. Si noti che questo sarà solo funziona se il comando genera una sola riga. (Se hai bisogno di multilinea output, dovrai scrivere un custom.cmake più complesso, a seconda di come desideri i dati multilinea nel tuo programma.)

Il programma principale è simile al seguente:

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

Se desideri effettivamente calcolare le opzioni del compilatore al momento della compilazione, le cose diventano molto più complicate. Per i generatori Bourne-shell puoi semplicemente inserire il comando all'interno di backtick. Se ti arrabbi mentre cerchi di capire citando, sposta tutta la logica del tuo comando all'interno di uno script di shell così devi solo inserire mycommand.sh nelle tue add_definitions ():

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

Per i generatori basati su file batch di Windows le cose sono molto più complicate, e io non ho una buona soluzione. Il problema è che i comandi PRE_BUILD non vengono eseguiti come parte dello stesso file batch del compilatore effettivo invocazione (studia BuildLog.htm per i dettagli), quindi la mia idea iniziale non ha funzionato (generare un custom.bat in un passaggio PRE_BUILD e quindi farlo " chiama custom.bat " su di esso per ottenere un set di variabili che può essere successivamente indicato nella riga di comando del compilatore). Se esiste un equivalente di backtick in file batch, che risolverebbero il problema.

Spero che questo dia alcune idee e punti di partenza.

(Ora all'inevitabile contro-domanda: che cosa sei davvero stai cercando di farlo?)

EDIT: non sono sicuro del motivo per cui non si desidera consentire a CMake di essere utilizzato per generare il file di intestazione. L'uso di $ {CMAKE_COMMAND} si espanderà al CMake utilizzato per generare i file Makefiles / .vcproj e poiché CMake non supporta realmente i file Makefile / .vcproj portatili, sarà necessario rieseguire CMake sui computer di destinazione.

CMake ha anche una serie di comandi di utilità (Esegui " cmake -E " per un elenco) per questo motivo esplicito. Ad esempio puoi farlo

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

per copiare file1.h in file2.h.

In ogni caso, se non si desidera generare i file di intestazione utilizzando CMake, sarà necessario invocare script separati .bat / .sh per generare il file di intestazione oppure farlo utilizzando echo:

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

Modifica le quotazioni secondo necessità.

Altri suggerimenti

La soluzione sopra (utilizzando un file di script CMake separato per generare un file di intestazione) sembra molto flessibile ma un po 'complicata per ciò che viene fatto nell'esempio.

Un'alternativa è impostare una proprietà COMPILE_DEFINITIONS su un singolo file di origine e / o destinazione, nel qual caso le variabili pre-processore definite verranno impostate solo per il file di origine o i file nella destinazione vengono compilati.

Le proprietà COMPILE_DEFINITIONS hanno un formato diverso da quello utilizzato nel comando add_definitions e hanno il vantaggio di non doverti preoccupare di " -D " o " \ D " sintassi e funzionano su più piattaforme.

Codice di esempio

- 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 prima riga esegue un comando shell svnversion e inserisce il risultato nella variabile SVN_REV . Il comando string (STRIP ...) è necessario per rimuovere i caratteri newline finali dall'output.

Notare che ciò presuppone che il comando in esecuzione sia multipiattaforma. In caso contrario, potrebbe essere necessario disporre di alternative per piattaforme diverse. Ad esempio, utilizzo l'implementazione cygwin del comando Unix date e ho:

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)

per i comandi di data, dove win_date.bat è un file bat che genera la data nel formato desiderato.

Le due variabili pre-processore non sono disponibili nel file ./VersionInfo.cpp ma non sono impostate in nessun altro file. Potresti quindi avere

- VersionInfo.cpp -

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

Questo sembra funzionare bene su piattaforme diverse e minimizza la quantità di codice specifico per piattaforma.

Vorrei usare il seguente approccio:

  1. Crea un eseguibile che stampa la data corrente su stdout (CMake non ha questa funzionalità)
  2. Aggiungi un target che è sempre considerato obsoleto
  3. Consenti al target di invocare un altro script CMake
  4. Consenti allo script CMake invocato di generare un file di intestazione

Codice di esempio per questo:

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

Ciò si traduce in un'intestazione "generate.h" che si rigenera su ogni build. Se non hai bisogno di DATETIME questo esempio potrebbe essere sostanzialmente semplificato in quanto CMake non ha questa funzione e un programma deve essere costruito per simulare la funzionalità.

Vorrei tuttavia pensare più di due volte prima di farlo. Ricorda che il file di intestazione verrà rigenerato ogni volta che viene eseguito, rendendo la destinazione non valida in tutte volte. non non avrai mai un file binario considerato aggiornato.

Funziona?

d=`perl -e"print qq(Whatever calculated at runtime);"`; g++ prog.cpp -o prog -DDATETIME=$d
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top