2011-07-28 14 views
7

-DCOMPILE_WITHOUT_FOOのようなC/C++プリプロセッサフ​​ラグの依存関係をサポートするためのベストプラクティスはありますか?私はしたくない何#definesへの依存関係のベストプラクティス?

> setenv COMPILE_WITHOUT_FOO 
> make 
    g++ FileWithIfdefFoo.cpp 

値があれば、すべてを再コンパイルする必要があるために:

> setenv COMPILE_WITHOUT_FOO 
> make <Make system reads environment, sets -DCOMPILE_WITHOUT_FOO> 
    <Compiles nothing, since no source file has changed> 

は、私は何をしたいのは、再コンパイルを取得#ifdef文に依存しているすべてのファイルを持っている。ここに私の問題ですCOMPILE_WITHOUT_FOOの変更はありません。

私は基本的にヘッダファイルFooDefines.hを書き込んだ後、何かが違うかどうかを調べるために、基本的なPythonスクリプト(下記参照)を使用しています。そうであれば、FooDefines.hを置き換え、従来のソースファイルの依存関係を引き継ぎます。定義はではなく、-Dというコマンドラインでが渡されます。欠点は、FooDefines.h#ifdefを使用する任意のソースファイルに含める必要があり、また、#ifdefごとに新しい動的生成ヘッダーファイルがあることです。これを行うためのツールやプリプロセッサの使用を避ける方法があれば、私はすべて耳にします。

import os, sys 
def makeDefineFile(filename, text): 
    tmpDefineFile = "/tmp/%s%s"%(os.getenv("USER"),filename) #Use os.tempnam? 
    existingDefineFile = filename 

    output = open(tmpDefineFile,'w') 
    output.write(text) 
    output.close() 

    status = os.system("diff -q %s %s"%(tmpDefineFile, existingDefineFile)) 

    def checkStatus(status): 
     failed = False 
     if os.WIFEXITED(status): 
      #Check return code 
      returnCode = os.WEXITSTATUS(status) 
      failed = returnCode != 0 
     else: 
      #Caught a signal, coredump, etc. 
      failed = True 
     return failed,status 

    #If we failed for any reason (file didn't exist, different, etc.) 
    if checkStatus(status)[0]: 
     #Copy our tmp into the new file 
     status = os.system("cp %s %s"%(tmpDefineFile, existingDefineFile)) 
     failed,status = checkStatus(status) 
     print failed, status 
     if failed: 
      print "ERROR: Could not update define in makeDefine.py" 
      sys.exit(status) 

答えて

0

ファイルの日付タイムスタンプでトリガーを作成します。それに依存するファイルよりも新しい依存ファイルは、再コンパイルを開始します。それぞれのオプションの定義を別々の.hファイルに入れ、それらの依存関係がmakefileで表されるようにする必要があります。オプションを変更すると、それに依存するファイルが自動的に再コンパイルされます。

ファイルを含むインクルードファイルを考慮すると、ソースの構造を変更する必要はありません。すべての個々の設定ファイルを含む "BuildSettings.h"ファイルを含めることができます。

厄介な問題は、include guardsを解析するほどスマートにした場合です。ファイル名の衝突やインクルードディレクトリ検索の順序が含まれているため、コンパイルに問題が発生しました。

ここで言及しておきますが、自分のIDEが自動的にそれらの依存関係を作成するのに十分スマートであるかどうかを確認してください。 IDEに追加するのは素晴らしいことです。

+0

私が使用しているIDEのバグレポートが追加されました。運があればすぐにそれを直すかもしれない – Jay

2

私は、自動的に決定することはできないと信じています。プリプロセッサディレクティブはコンパイルされません。一般的に言えば、私が定義に依存するならば、私は完全な再コンパイルを行うことを期待しています。 DEBUGはおなじみの例です。

右のの方法はありません。あなたが正しい方法でそれを行うことができない場合、可能な限り最善の方法は、おそらくあなたの最良の選択肢です。 COMPILE_WITH_FOOのテキスト検索を行い、そのように依存関係を作成します。私はこれを宣誓証人として分類し、共有コードを書いている場合は、同僚からのかなりの買いを求めることをお勧めします。

CMakeには、これを簡単にするための機能がいくつかあります。これを行うカスタムターゲットを作成します。あなたのシンボルに依存するファイルのリストを維持しながら、ここで問題を交換することができます。あなたのテキスト検索は、それが変更された場合でもそのファイルを生成する可能性があります。私は、wgetのタイムスタンプに基づいて静的データリポジトリを再構築する必要があるかどうかをチェックする同様の手法を使用しました。

Cheetahは有用かもしれない別のツールです。

私がそうだったら、私は完全な再構築を行うと思います。

+0

残念ながら、私はあなたが正しいと思います。このチームでは、メカニズムの使用を強制するのは問題ではありません(特にQMakeの上に私たちのmakeシステムを構築して以来)。しかし、これを行うための標準的な、あるいはより好ましい方法はありません。プリプロセッサは、絶対に必要な場合にのみ使用する必要があります。 –

+0

条件付きコンパイルは常に面倒です。それを日常的/統合的なビルドと共に控えめに使用することは、私がこれまで見てきた最も難しい解決策です。ほとんどの組織が私設支店でこれを行うように設定されていないという警告があります。 –

3

これは確かに素敵なアプローチではありませんが、それは動作します:

find . -name '*cpp' -o -name '*h' -exec grep -l COMPILE_WITHOUT_FOO {} \; | xargs touch 

マクロCOMPILE_WITHOUT_FOOのためにあなたのソースコードを見て、タイムスタンプを更新する各ファイルを、「触れる」ということ。 makeを実行すると、それらのファイルは再コンパイルされます。

あなたはackがインストールされている場合は、このコマンドを簡素化することができます。

ack -l --cpp COMPILE_WITHOUT_FOO | xargs touch 
+0

これは興味深いアプローチです。依存関係はお互いに依存するのではなく、平らな傾向があるので、grepは十分だと思います。残念なことに私の場合は、チェックアウトされていない限り、ファイルを読み取り専用状態にしておくPerforceがあります。私がgitを使用していた場合、あなたのソリューションは完璧です。 –

+0

Boo for perforce – ojblass

+0

@Walter - 私はPerforceも使用しています。ディレクトリ全体をチェックアウトする必要があります。これにより、これを使用することができます。しかし、変更をチェックインするときは、最初に "変更されていないファイルを元に戻す"ことを忘れないでください。 – Tim

0

ジェイは、「ファイルの日付のタイムスタンプにトリガを作る」ことを指摘しました。

理論的には、m1という名前のメインのメイクファイルをm2と呼ばれる第2のメークファイルから変数をインクルードすることができます。 m2には、すべてのプリプロセッサフ​​ラグのリストが含まれます。

あなたのプログラムのメイクルールは、最新のm2に依存している可能性があります。

m2を作成するルールは、すべての環境変数(したがって#includeディレクティブ)をインポートすることです。

これは、以前のバージョンとの差分があるかどうかを検出するためのルールです。そうであれば、それは主要な目標のために "すべてを作る"こと、および/またはきれいにする変数を可能にするでしょう。それ以外の場合は、m2のタイムスタンプを更新し、完全なリメイクをトリガーしません。

最後に、通常のターゲット(make all)のルールは、m2からのプリプロセッサディレクティブをソースとし、必要に応じて適用します。

これは簡単ですが理論的には可能ですが、実際にはGNU Makeはこの種のものを動作させるのがはるかに難しいです。私はそれができると確信しています。

+0

実際はかなり簡単です。 "include cppflagsfile"と "$(ENVDEPOBJS):cppflagsfile"はすでにトリックを行います。 – thiton

1

問題は、autoconfautoheaderで扱い、変数の値をconfig.hファイルに書き込むように調整されているようです。それが不可能な場合は、ファイルから "-D"ディレクティブを読み取り、そのファイルにフラグを書き込むことを検討してください。

すべての状況において、環境変数のみに依存するビルドは避けなければなりません。あなたは環境がいつ変わったかを伝える方法がありません。変数をファイルに格納する必要があります。最もクリーンな方法は、autoconf、autoheader、ソースと複数のビルドツリーです。コンパイルコンテキストの各スイッチのために再 - configure - で二番目にきれいな方法; 3番目にクリーンな方法は、これらのスイッチに依存するすべてのオブジェクトが依存するすべての変更可能なコンパイラスイッチを含むファイルです。

3つ目の方法を実装する場合、このファイルを不必要に更新しないでください。それを一時的な場所に構築し、それを条件付きでdiffにコピーして、フラグを使ってルールを条件付きで再構築することができます。

1

これを行う1つの方法は、各#defineの前の値をファイルに保存し、現在の値が前の値と一致しない場合には、そのファイルを強制的に更新するためにmakefileのconditionalsを使用することです。そのマクロに依存するファイルには、そのファイルが依存関係として含まれます。

ここは例です。file.cが変更されたか、変数COMPILE_WITHOUT_FOOが前回と異なる場合、file.oが更新されます。 $(shell)を使用して、現在の値とファイルenvvars/COMPILE_WITHOUT_FOOに格納されている値を比較します。それらが異なる場合は、常に更新されるforceに依存するそのファイルのコマンドを作成します。あなたは未定義のマクロを持つサポートしたい場合は

file.o: file.c envvars/COMPILE_WITHOUT_FOO 
    gcc -DCOMPILE_WITHOUT_FOO=$(COMPILE_WITHOUT_FOO) $< -o [email protected] 

ifneq ($(strip $(shell cat envvars/COMPILE_WITHOUT_FOO 2> /dev/null)), $(strip $(COMPILE_WITHOUT_FOO))) 
force: ; 
envvars/COMPILE_WITHOUT_FOO: force 
    echo "$(COMPILE_WITHOUT_FOO)" > envvars/COMPILE_WITHOUT_FOO 
endif 

は、あなたがifdefifndef条件文を使用し、その値は、それが実行された最後の時間を未定義たファイルに何らかの指示を持っている必要があります。