CMake가 생성한 Makefile 내에서 컴파일 타임에 명령을 실행하는 방법은 무엇입니까?
-
08-07-2019 - |
문제
일부 옵션을 컴파일러에 전달하고 싶습니다.옵션은 컴파일 타임에 계산되어야 합니다. 즉, '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;
이것은 플랫폼에서 잘 작동하고 플랫폼 별 코드의 양을 최소화하는 것으로 보입니다.
다음 접근 방식을 사용합니다.
- 현재 날짜를 STDOUT에 인쇄하는 실행 파일을 만듭니다 (CMAKE는이 기능이 부족합니다).
- 항상 구식으로 간주되는 대상 추가
- 대상이 다른 CMake 스크립트를 호출하도록하십시오
- 호출 된 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