CMake가 생성한 Makefile 내에서 컴파일 타임에 명령을 실행하는 방법은 무엇입니까?

StackOverflow https://stackoverflow.com/questions/1438535

문제

일부 옵션을 컴파일러에 전달하고 싶습니다.옵션은 컴파일 타임에 계산되어야 합니다. 즉, 'cmake'가 아닌 'make'가 호출될 때마다 계산되어야 하므로,execute_process 명령은 이를 잘라내지 않습니다.(그렇습니까?)

예를 들어 다음과 같이 g++ 컴파일러에 날짜를 전달합니다.

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

그러나 DATETIME은 컴파일 타임에 계산됩니다.

CMake에서 어떻게 할 수 있는지 아시나요?

현상금 편집:

최소한의 해킹 솔루션이 허용됩니다.

'날짜'뿐만 아니라 컴파일 타임에 임의의 명령을 실행할 수 있기를 원합니다.

편집 2:

Linux, Windows(VS), Mingw, Cygwin 및 OS X에서 작동해야 합니다.Ruby, Perl 또는 Python은 Windows에서 표준이 아니기 때문에 가정할 수 없습니다.BOOST를 가정할 수도 있지만 아무 소용이 없을 것 같습니다.

목표는 cmake가 make가 실행될 때 작업을 수행하는 Makefile(Linux의 경우)을 생성하도록 강제하는 것입니다.

사용자 정의 *.h 파일을 생성하는 것은 괜찮지만 Makefile(또는 다른 OS에서는 이에 상응하는 파일)에 의해 make에 의해 시작되어야 합니다.*.h를 생성할 때 cmake를 사용할 필요는 없습니다(사용해서도 안 됩니다).

도움이 되었습니까?

해결책

이를 실행 해야하는 플랫폼 및 추가 도구가있는 경우 사용할 수있는 플랫폼과 같은 정보를 남기고 있습니다.Python의 Ruby, Perl을 사용할 수 있다면 상황이 훨씬 간단 해집니다.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", 원하는 명령으로 대체 할 것입니다), variable _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 생성기의 경우 백 티크 내부에 명령을 삽입하면됩니다.인용을 알아 내면서 화를 내면 명령의 모든 논리를 쉘 스크립트 안에 옮기면 mycommand.sh add_definitions()에서:

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

Windows 배치 파일 기반 생성기의 경우 물건은 훨씬 더 까다 롭고 좋은 솔루션이 없습니다.문제는 PRE_BUILD 명령은 실제 ​​컴파일러 호출과 동일한 배치 파일의 일부로 실행되지 않으므로 (자세한 내용은 BuildLog.htm을 연구) 초기 아이디어가 작동하지 않았습니다 (Custom.bat 생성 PRE_BUILD 단계를 밟은 다음 나중에 컴파일러 명령 줄에서 참조 할 수있는 변수 세트를 가져 오려면 "Concert.Bat"을 수행하십시오).배치 파일에 백 티크가있는 경우 문제가 해결됩니다.

이것이 몇 가지 아이디어와 출발점을 제공하기를 바랍니다.

(이제 피할 수 없는 반대 질문으로:너 뭐야 정말하려고?)

편집하다:헤더 파일을 생성하는 데 CMake를 사용하지 않으려는 이유가 무엇인지 잘 모르겠습니다.${CMAKE_COMMAND}를 사용하면 Makefiles/.vcproj 파일을 생성하는 데 사용되는 CMake로 확장되며 CMake는 실제로 휴대용 Makefiles/.vcproj 파일을 지원하지 않으므로 대상 컴퓨터에서 CMake를 다시 실행해야 합니다.

CMake에는 이러한 명시적인 이유로 여러 유틸리티 명령(목록을 보려면 "cmake -E" 실행)도 있습니다.예를 들어 할 수 있습니다

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

file1.h를 file2.h로 복사합니다.

어쨌든 CMake를 사용하여 헤더 파일을 생성하지 않으려면 별도의 .bat/.sh 스크립트를 호출하여 헤더 파일을 생성하거나 echo를 사용하여 수행해야 합니다.

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

필요에 따라 인용을 조정합니다.

다른 팁

위의 솔루션 (별도의 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 ...) 출력에서 후행 Newline 문자를 제거하려면 명령이 필요합니다.

실행중인 명령이 크로스 플랫폼이라고 가정합니다. 그렇지 않은 경우 다른 플랫폼에 대한 대안이 필요할 수 있습니다. 예를 들어 UNIX의 Cygwin 구현을 사용합니다. date 명령 및 가지고 있습니다 :

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;

이것은 플랫폼에서 잘 작동하고 플랫폼 별 코드의 양을 최소화하는 것으로 보입니다.

다음 접근 방식을 사용합니다.

  1. 현재 날짜를 STDOUT에 인쇄하는 실행 파일을 만듭니다 (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 "@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;
}

이로 인해 모든 빌드에서 재생되는 헤더 "생성 .H"가 발생합니다. DateTime이 필요하지 않은 경우 CMAKE 에이 기능이 부족하고 기능을 시뮬레이션하기 위해 프로그램을 구축해야 하므로이 예제를 실질적으로 단순화 할 수 있습니다.

그러나 나는 이것을하기 전에 두 번 이상 생각할 것입니다. 헤더 파일은 매번 실행 될 때마다 재생성되며 대상이 유효하지 않습니다. 모두 타임스. 당신은 할 것입니다 절대 최신으로 간주되는 이진이 있습니다.

이것이 작동합니까?

d=`perl -e"print qq(Whatever calculated at runtime);"`; g++ prog.cpp -o prog -DDATETIME=$$d
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top