2011-07-28 80 views
7

是否有支持C/C++預處理器標誌(如-DCOMPILE_WITHOUT_FOO)的依賴關係的最佳做法?這裏是我的問題:#defines依賴關係的最佳實踐?

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

我想這樣做是有依靠#ifdef語句被重新編譯所有文件:

> setenv COMPILE_WITHOUT_FOO 
> make 
    g++ FileWithIfdefFoo.cpp 

我不想什麼是有如果該值重新編譯一切的COMPILE_WITHOUT_FOO沒有改變。

我有一個原始的Python腳本工作(見下文),基本上寫一個頭文件FooDefines.h,然後比較它,看看是否有什麼不同。如果是,它將替換FooDefines.h,然後傳統的源文件依賴項接管。該定義是而不是-D在命令行上傳遞。缺點是我現在必須在使用#ifdef的任何源文件中包含FooDefines.h,並且每個#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進行文本搜索並以此方式創建依賴關係。我會把它歸類爲shenanigan,如果你正在編寫共享代碼,我會建議你從同事那裏尋找相當可觀的收入。

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

傑指出,「對文件做出日期時間戳觸發器」。

從理論上講,你可以讓你的主makefile,叫做m1,包含第二個叫做m2的makefile的變量。 m2將包含所有預處理器標誌的列表。

您可以爲您的程序制定規則,具體取決於m2是最新的。

使m2生效的規則是導入所有的環境變量(並因此導入#include指令)。

訣竅是,製作m2的規則將檢測是否存在與以前版本的差異。如果是這樣,它將啓用一個變量來強制「全部製造」和/或爲主要目標進行清理。否則,它只會更新m2上的時間戳,而不會觸發完整的重製。

最後,普通目標(make all)的規則將源於m2的預處理器指令並根據需要應用它們。

理論上這聽起來很簡單/可能,但實際上GNU Make很難讓這種類型的東西起作用。我相信它可以做到。

+0

其實很簡單。 「include cppflagsfile」和「$(ENVDEPOBJS):cppflagsfile」已經可以做到這一點。 – thiton

1

您的問題似乎是通過autoconfautoheader來處理它,將變量的值寫入config.h文件。如果這不可行,請考慮從文件中讀取「-D」指令並將標誌寫入該文件。

在任何情況下,您都必須避免只依賴於環境變量的構建。你無法告訴環境何時改變。有一個確定的需要將變量存儲在一個文件中,最簡潔的方法是通過autoconf,autoheader和一個源代碼以及多個構建樹;對於編譯上下文的每個切換,重新執行第二種最簡潔的方式;而第三種最清潔的方式是一個包含所有可變編譯器開關的文件,其中依賴於這些開關的所有對象都依賴於它們自己。

當您選擇實施第三種方式時,切記不要不必要地更新此文件,例如,通過在臨時位置構建並在diff上有條件地複製它,然後使規則根據標誌有條件地重建文件。

1

一種方法是將每個#define的前一個值存儲在文件中,並在makefile中使用條件來強制更新該文件,只要當前值與前一個值不匹配。任何依賴於該宏的文件都會將該文件包含爲依賴項。

這裏是一個例子。如果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條件語句,並有該值已經不確定它是對最近一次運行該文件中的某些指示。