34

いくつかのオプションをコンパイラに渡したいと思います。オプションはコンパイル時に計算する必要があります-'cmake'ではなく、'make'が呼び出されるたびに、execute_processコマンドはそれをカットしません。(そうですか?)

たとえば、次のようなg++コンパイラに日付を渡します。

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

ただし、DATETIMEはコンパイル時に計算されます。

CMakeでそれを行う方法はありますか?

バウンティ編集:

最小限のハックの解決策が受け入れられます。

'date'だけでなく、コンパイル時に任意のコマンドを実行できるようにしたいことに注意してください。

編集2:

Linux、Windows(VS)、Mingw、Cygwin、OS Xで動作する必要があります。Ruby、Perl、PythonはWindowsでは標準ではないため、想定できません。BOOSTを想定することはできますが、役に立たないと思います。

目標は、cmakeにMakefile(Linuxの場合)を生成させることです。Makefileは、makeが実行されるとジョブを実行します。

カスタム*.hファイルの作成は問題ありませんが、Makefile(または他のOSでは同等のもの)によってmakeによって開始される必要があります。* .hの作成では、cmakeを使用する必要はありません(使用する必要はありません)。

4

4 に答える 4

46

これを実行する必要があるプラットフォームや、使用できる追加のツールがあるかどうかなど、いくつかの情報を省略しています。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 シェル ジェネレーターの場合は、コマンドをバッククォート内に挿入するだけです。mycommand.sh引用を理解しているときに腹が立った場合は、コマンドのすべてのロジックをシェルスクリプト内に移動して、 add_definitions()に入れるだけでよいようにします。

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

Windows のバッチ ファイル ベースのジェネレーターの場合、事態はかなりトリッキーであり、適切な解決策がありません。問題は、PRE_BUILDコマンドが実際のコンパイラ呼び出しと同じバッチ ファイルの一部として実行されないことです (詳細については BuildLog.htm を調べてください) 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)

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

于 2009-09-23T21:47:02.147 に答える
6

上記の解決策 (別の 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 ...)コマンドは、出力から末尾の改行文字を削除するために必要です。

これは、実行中のコマンドがクロスプラットフォームであると想定していることに注意してください。そうでない場合は、さまざまなプラットフォーム用の代替手段が必要になる場合があります。たとえば、私は Unixdateコマンドの 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)

date コマンドの場合win_date.bat、 は目的の形式で日付を出力するバッチ ファイルです。

2 つのプリプロセッサ変数はファイルでは使用できませんが./VersionInfo.cpp、他のファイルでは設定されません。あなたはそれから持つことができます

-- VersionInfo.cpp --

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

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

于 2012-10-10T18:00:12.100 に答える
2

次のアプローチを使用します。

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

これにより、ビルドごとに再生成されるヘッダー「 generated.h 」が生成されます。CMake にはこの機能がなく、機能をシミュレートするにはプログラムをビルドする必要があるため、DATETIME が必要ない場合は、この例を大幅に簡略化できます。

ただし、これを行う前に、よく考えます。make が実行されるたびにヘッダーファイルが再生成されるため、ターゲットが常に無効になることに注意してください。最新と見なされるバイナリは決してありません。

于 2009-09-29T15:09:10.543 に答える
-1

これは機能しますか?

d=`perl -e"print qq(Whatever calculated at runtime);"`; g++ prog.cpp -o prog -DDATETIME=$$d
于 2009-09-17T12:57:34.097 に答える