2009-09-17 5 views
33

Я хотел бы передать некоторые параметры компилятору. Этот параметр должен быть рассчитан во время компиляции - каждый раз, когда вызывается «make», а не когда «cmake», поэтому команда execute_process не разрезает его. (Это делает?)Как запустить команду во время компиляции в Makefile, сгенерированном CMake?

Например пропускание даты в г ++ компилятор вроде этого:

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

Но с DATETIME вычисляется во время компиляции.

Любая идея, как это сделать в CMake?

Баунти редактировать:

наименьших хаком решение будет принято.

Обратите внимание, что я хочу, чтобы иметь возможность вызывать произвольную команду во время компиляции, а не только «дата».

Edit 2:

Он должен работать на Linux, Windows (VS), Mingw, Cygwin и OS X. Вы не можете предположить, Ruby, Perl или Python, поскольку они не являются стандартными для Windows, , Вы можете предположить BOOST, но я думаю, что это бесполезно.

Цель состоит в том, чтобы заставить cmake генерировать Makefile (в случае Linux), когда при выполнении make выполнит эту работу.

Создание пользовательского * .h файла в порядке, но оно должно быть инициировано Makefile (или эквивалентом на другой ОС) make. Для создания * .h не нужно (и не должно) использовать cmake.

ответ

45

Вы уезжаете некоторую информацию, например, какие платформы вам нужно запустить это, и если есть какие-либо дополнительные средства, которые можно использовать. Если вы можете использовать Ruby, Perl, Python, все станет намного проще. Я буду предположить, что вы хотите запускать как на платформе Unix, так и на Windows pqlatform и , что дополнительных инструментов нет.

Если вы хотите получить результат команды в символе препроцессора, самым простым способом является создание файла заголовка вместо того, чтобы возиться с с параметрами командной строки. Помните, что CMake есть скрипт-режим (-P), где он только обрабатывает команды сценария в файле, так что вы можете сделать что-то вроде этого:

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) 

Файл " custom.h "генерируется во время компиляции командой" cmake -P custom.cmake ". custom.cmake выглядит следующим образом:

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

Он выполняет команду (в данном случае «uname -a», вы замените его с любой командой, которую вы хотите), и помещаете вывод в переменной _OUTPUT , который затем записывается в custom.h.Обратите внимание, что это будет работать только , если команда выводит одну строку. (Если вам нужно многострочные выхода, вам придется написать более сложную custom.cmake, в зависимости от , как вы хотите многострочные данные в вашу программу.)

Основная программа выглядит следующим образом:

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

Если вы действительно хотите рассчитать параметры компилятора во время компиляции, то все станет намного сложнее. Для генераторов Bourne-shell вы можете просто вставить команду внутри backticks. Если вы злитесь в то время как выяснить квотирование, переместить всю логику вашей команды внутри оболочки-скрипта так вам нужно только положить mycommand.sh в ваших add_definitions():

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

Для окна на основе пакетного файла генераторов вещей намного сложнее, и у меня нет хорошего решения. Проблема в том, что команды PRE_BUILD не выполняются как часть того же командного файла, что и действительный вызов компилятора (изучите BuildLog.htm для получения дополнительной информации), поэтому моя первоначальная идея не работала (генерирование custom.bat в PRE_BUILD, а затем выполните «call custom.bat» на нем, чтобы получить набор переменных, который позже может быть , упомянутый в командной строке компилятора). Если в пакетных файлах есть эквивалент обратных ссылок, это решит проблему.

Надеюсь, это дает некоторые идеи и отправные точки.

(Теперь к неизбежному встречный вопрос: что вы действительно пытается сделать?)

EDIT: Я не знаю, почему вы не хотите, чтобы CMake можно использовать для генерации заголовок-файла. Использование $ {CMAKE_COMMAND} будет расширяться до CMake, используемого для генерации файлов Makefiles/.vcproj, и поскольку CMake действительно не поддерживает переносные файлы Makefiles/.vcproj, вам нужно будет перезапустить CMake на целевых компьютерах.

У CMake также есть куча служебных команд (запустите «cmake -E» для списка) по этой явной причине. Вы можете, например, сделать

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

для копирования файла1.h в файл2.h.

Во всяком случае, если вы не хотите, чтобы генерировать заголовок-файлы с помощью CMake, вам нужно будет либо ссылаться на отдельные .bat/.sh скриптов для создания файла заголовка, или сделать это с помощью эха:

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

При необходимости отрегулируйте цитату.

+0

Я уточнил свой вопрос. Не могли бы вы разъяснить вам ответ в этом свете? –

-1

Это работает?

d=`perl -e"print qq(Whatever calculated at runtime);"`; g++ prog.cpp -o prog -DDATETIME=$$d 
+0

Вопрос только о CMake, а не о perl/bash –

+0

@ Łukasz Lew: Разве это невозможно вывести эту строку внутри CMake? – Curd

+2

Да, но это было бы выполнено в момент cmake не на время. –

2

Я хотел бы использовать следующий подход:

  1. Создание исполняемого файла, который печатает текущую дату на стандартный вывод (CMake не хватает этой функции)
  2. Добавить цель, которая всегда считается от даты
  3. Пусть цель вызвать другой сценарий CMake
  4. Пусть вызывается CMake сценарий создания файла заголовка

Пример кода для этого:

--- --- 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 "@[email protected]" 

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

Это приводит к заголовочной "generated.h", который регенерируется при каждой сборке. Если вам не нужен DATETIME, этот пример может быть существенно упрощен, так как у CMake не хватает этой функции, и программа должна быть построена для имитации функциональности.

Я бы, однако, подумал более чем дважды, прежде чем делать это. Помните, что заголовочный файл будет регенерирован каждый раз, когда выполняется make, что делает вашу цель недействительной на всего раз. Вы будете никогда имеют двоичный файл, который считается актуальным.

4

Решение выше (с использованием отдельного файла сценария CMake для создания файла заголовка) кажется очень гибким, но немного сложным для того, что делается в этом примере.

Альтернативой является установление свойства COMPILE_DEFINITIONS в отдельном исходном файле и/или цели, и в этом случае скомпилированные определенные препроцессорные переменные будут установлены только для исходного файла или файлы в целевом файле.

Свойства COMPILE_DEFINITIONS имеют формат, отличный от используемого в команде add_definitions, и имеют то преимущество, что вам не нужно беспокоиться о синтаксисе «-D» или «\ D», и они работают кросс-платформенно.

Пример кода

- 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}\"") 

Первая строка запускает команду оболочки svnversion и помещает результат в переменной SVN_REV. Команда string(STRIP ...) необходима для удаления завершающих символов новой строки из вывода.

Обратите внимание, что предполагается, что выполняемая команда является кросс-платформенной. Если нет, вам может потребоваться альтернатива для разных платформ.Например, я использую реализацию CYGWIN из date команды Unix, и имею:

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) 

для даты команд, где win_date.bat является летучей мышью файл, который выводит дату в нужном формате.

Две препроцессорные переменные недоступны в файле ./VersionInfo.cpp, но не установлены ни в какие другие файлы. Вы могли бы иметь

- VersionInfo.cpp -

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

Это, кажется, работает хорошо на разных платформах и минимизирует объем платформы кода.

+2

Проблема, которую я вижу, заключается в том, что файл VersionInfo.cpp не изменится и, следовательно, не будет перекомпилирован. Как бы вы решили это? Простое выполнение перекомпиляции вручную сделало бы всю магию cmake менее полезной, поскольку она не будет делать больше, чем прямое использование макросов '__DATE__' и' __TIME__'. –

Смежные вопросы