2012-12-17 65 views
2

我們正在重構我們的代碼庫,並試圖限制不同組件之間的直接依賴關係。我們的源碼樹有幾個頂級目錄:src/a,src/b和src/c。makefile可以在C++中強制實現依賴性限制

我們要執行一套restirctions的:

  • 文件中不能在B或C
  • 文件依賴的文件在B可以依賴於文件,但不是在C C
  • 文件可以直接取決於b中的文件但不是

執行第一個操作很簡單。我有這樣一個隱含的規則:

build/a/%.o : src/a/%.cpp 
    $(CXX) -I src/a $(OTHER_FLAGS) -o [email protected] $< 

如果嘗試下一個文件,包括從B或C頭文件作爲標題沒有找到構建失敗。

第二個規則有一個類似的規則,它將src/a和src/b指定爲include目錄。建設c出現了問題。以下是允許的。

src/c/C.cpp 
#include "b.h" 
void C() { ... } 

src/b/b.h 
#include "a.h" 
class B { ... }; 

src/a/a.h 
class A { ... }; 

這裏,來自c的文件包括來自b(允許)的文件,該文件又包括來自(也允許)的文件。我們要防止這樣的代碼:

src/c/C_bad.cpp 
// Direct inclusion of a 
#include "a.h" 

src/c/c_bad.h 
// Direct inclusion of a 
#include "a.h" 

對於允許的情況下進行編譯,在SRC/C建立檔案必須包括-Isrc/A編譯命令,但允許第二種情況也編譯。

我懷疑我的問題的答案是編寫一個腳本,它查看編譯器生成的依賴關係,發現潛在的非法依賴關係,然後查看源文件以確定這是否是直接依賴關係。有沒有合理的方法來組合編譯器和/或makefile結構?

如果有問題,我們使用的是GNU Make 3.81和g ++ 4.5.3,但如果可能的話,我們希望是便攜式的。

更新

我們正在尋找的東西它需要努力違反規定,沒有一個地方需要努力遵循的規則。 (過去的經驗表明,後者不太可能奏效。)雖然在其他答案中有一些很好的想法,但我接受寫一個腳本的人,因爲那是一個花費最多努力工作的人周圍。

感謝大家的回答。

回答

0

考慮到您正在將此應用於現有代碼庫的事實,我會選擇「驗證腳本」方法。

因此,而不是修改構建過程,並在同一時間切斷依賴一個作爲構建失敗的,你就會得到帶有那些非訴文件的列表。然後,您可以在考慮「全局圖」的情況下重構代碼庫,並且您所做的任何更改都將使用與以前相同的Makefiles來構建,從而簡化測試和調試。

一旦重構,分析腳本可以繼續用作符合性檢查,以驗證未來的更新。

用於這種分析的一個可能的起點是使用makedependcpp -MM。例如,使用你在問題中所列出的CPP/h的文件:

 
[[email protected]]$ find . 
. 
./b 
./b/b.h 
./a 
./a/a.h 
./c 
./c/C_bad.cpp 
./c/C.cpp 
./c/c_bad.h 

[[email protected]]$ cpp -MM -Ia -Ib -Ic */*.cpp 
C_bad.o: c/C_bad.cpp a/a.h 
C.o: c/C.cpp b/b.h a/a.h 

[[email protected]]$ # This also works for header files 
[[email protected]]$ cpp -Ia -Ib -Ic -MM c/c_bad.h 
c_bad.o: c/c_bad.h a/a.h 

它應該是相當直接的解析這些輸出以確定每個CPP文件的相關性和標誌了那些非兼容。

的缺點這種方法是,它不能直接和間接依賴區分,所以如果該事項可能需要包括額外的步驟來查看源代碼,並挑選出的直接依賴。

0

是的。但它需要一些手動的努力和紀律。

在構建C時,您可以依賴src/b/*中的標題。

項目內部B主目錄中的任何頭文件應該是自包含的,並且不依賴於其他項目。您還需要B src/b/detail/*。h中的子目錄。在這裏,頭文件可以包含src/a/*。h和src/b/*。h,但這是一個私有的實現細節,只適用於b項目的源文件。

+0

如果我從SRC/B/b.h(而不是從詳細的目錄)#包括「A.H」,可以構建做出失敗,或者我們將不得不依靠一個代碼審查,明白了嗎?看起來你也可以在src/b/detail/b.h中包含c/c.h,並且不會失敗。那是對的嗎? –

0

可以使-I選擇目標明確:

build/b/%.o: CPPFLAGS += -Isrc/a 
build/c/%.o: CPPFLAGS += -Isrc/b 

這是特定於GNU的化妝,雖然如此,它不便於攜帶。

0

最簡單的方法是改變你的包括對-Isrc一切路徑。包含語句然後具有完整的相對路徑

#include <a/a.h> 

例如。這使得更容易自動檢查代碼(可能在提交掛鉤而不是makefile中)。


或者,你可以做在一個宏和B頭討厭的東西:

// src/a/a.h 
#ifndef SRC_A_H 
#define SRC_A_H 
#ifndef ALLOW_A 
#error "you're not allowed to include A headers here" 
#endif 
//... 

// src/b/b.h 
#ifndef SRC_B_H 
#define SRC_B_H 
#ifdef ALLOW_A_INDIRECT 
#define ALLOW_A 
#endif 

#include <a/a.h> 
//... 
#ifdef ALLOW_A_INDIRECT 
#undef ALLOW_A 
#endif 
#endif // include guard 

現在這些制定規則,將允許A和B build ok:

build/a/%.o: CPPFLAGS += -DALLOW_A 
build/b/%.o: CPPFLAGS += -DALLOW_A 

,這將只允許通過B C接入(與宏在B的頭)

build/c/%.o: CPPFLAGS += -DALLOW_A_INDIRECT 

注意這需要一些紀律特別是在B的頭,但我想,如果坐在與現有的包括防護裝置,它。 ..確定,它實際上仍然非常討厭。