web-dev-qa-db-ja.com

CMakeによって生成されたMakefile内でコンパイル時にコマンドを実行する方法は?

コンパイラーにいくつかのオプションを渡したいのですが。オプションはコンパイル時に計算する必要があります-'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を使用する必要はありません(使用する必要もありません)。

33
Łukasz Lew

これを実行する必要があるプラットフォームや、使用できる追加のツールがあるかどうかなど、いくつかの情報を省略しています。 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に書き込みます。これは、コマンドが1行を出力する場合にのみ機能することに注意してください。 (複数行の出力が必要な場合は、プログラムに複数行のデータをどのように入れたいかに応じて、より複雑な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を調べる)ため、私の最初のアイデアが機能しなかった(custom.batをPRE_BUILDステップを実行し、その上で "call custom.bat"を実行して、後でコンパイラのコマンドラインで参照できる変数セットを取得します。バッチファイルに同等のバックティックがある場合、それで問題が解決します。

これがいくつかのアイデアと出発点を与えることを願っています。

(さて、避けられない反問:あなたは何ですか本当に何をしようとしているのですか?)

編集:CMakeを使用してヘッダーファイルを生成させたくない理由はわかりません。 $ {CMAKE_COMMAND}を使用すると、Makefiles/.vcproj-filesの生成に使用されるCMakeに展開されます。CMakeは実際にはポータブルMakefiles/.vcproj-filesをサポートしていないため、ターゲットマシンで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)

必要に応じて引用を調整します。

45
JesperE

上記の解決策(別の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}\"")
_

1行目は、シェルコマンド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;
_

これはプラットフォーム間でうまく機能するようで、プラットフォーム固有のコードの量を最小限に抑えます。

4
user1735629

私は次のアプローチを使用します:

  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 "@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にはこの機能がなく、機能をシミュレートするプログラムを作成する必要があるため、この例は大幅に簡略化できます。

しかし、これを行う前に、私は二度以上考えるでしょう。ヘッダーファイルはmakeが実行されるたびに再生成され、ターゲットをall回無効にすることに注意してください。あなたはneverが最新と見なされるバイナリを持ちます。

2
larsmoa