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を想定できますが、私はそれが役に立たないと思います。
目標は、makeが実行されたときにジョブを実行するMakefile(Linuxの場合)をcmakeに強制的に生成させることです。
カスタム* .hファイルの作成は問題ありませんが、makeによってMakefile(または他のOSでは同等)によって開始される必要があります。 * .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"で置き換えます 任意のコマンドを使用して)、出力を変数に入れます _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ジェネレーターの場合は、
バックティック内にコマンドを挿入します。考えているときに怒った場合
引用して、コマンドのすべてのロジックをシェルスクリプト内に移動します
add_definitions()に mycommand.sh
を追加するだけです:
if(UNIX)
add_definitions(`${CMAKE_CURRENT_SOURCE_DIR}/custom-options.sh`)
endif()
Windowsバッチファイルベースのジェネレーターの場合は、はるかに巧妙です。
良い解決策はありません。問題は、 PRE_BUILD
コマンドが
実際のコンパイラと同じバッチファイルの一部として実行されない
呼び出し(詳細についてはBuildLog.htmを調べてください)
動作しませんでした( PRE_BUILD
ステップでcustom.batを生成してから
&quot; call custom.bat&quot;後で変数セットを取得します
コンパイラのコマンドラインで参照されます)。同等のものがある場合
バッチファイルのバックティックを使用すると、問題を解決できます。
これにより、いくつかのアイデアと出発点が得られます。
(避けられない反論の質問:あなたは本当には何ですか? やろうとしている?)
編集:CMakeを使用してヘッダーファイルを生成したくない理由はわかりません。 $ {CMAKE_COMMAND}を使用すると、Makefiles / .vcproj-filesの生成に使用されるCMakeに拡張されます。CMakeは実際にはポータブルMakefiles / .vcproj-filesをサポートしないため、ターゲットマシンでCMakeを再実行する必要があります。
CMakeには、この明確な理由のために、ユーティリティコマンドがたくさんあります(リストについては&quot; cmake -E&quot;を実行します)。たとえば、次のことができます
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」について心配する必要がないという利点があります。または&quot; \ D&quot;構文とクロスプラットフォームで動作します。
サンプルコード
-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 ...)
コマンドが必要です。
これは、実行されているコマンドがクロスプラットフォームであることを前提としていることに注意してください。そうでない場合は、異なるプラットフォーム用の代替が必要になる場合があります。たとえば、Unixの date
コマンドのcygwin実装を使用しており、次のものがあります。
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
は、目的の形式で日付を出力するbatファイルです。
2つのプリプロセッサ変数は、ファイル ./ VersionInfo.cpp
では使用できませんが、他のファイルでは設定されていません。そうすれば
-VersionInfo.cpp-
std::string version_build_time=BUILD_TIME;
std::string version_svn_rev=SVN_REV;
これはプラットフォーム間でうまく機能するようで、プラットフォーム固有のコードの量を最小限に抑えます。
次のアプローチを使用します。
- 現在の日付を標準出力に出力する実行可能ファイルを作成します(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;
}
これにより、ヘッダー「generated.h」が生成されます。それはすべてのビルドで再生成されます。 DATETIMEを必要としない場合、CMakeにはこの機能がなく、機能をシミュレートするプログラムをビルドする必要があるため、この例を大幅に簡素化できます。
ただし、これを行う前に2回以上考えます。 makeを実行するたびにヘッダーファイルが再生成され、すべての時点でターゲットが無効になることに注意してください。最新とみなされるバイナリは決してありません。
これは機能しますか?
d=`perl -e"print qq(Whatever calculated at runtime);"`; g++ prog.cpp -o prog -DDATETIME=$d