2

コード ベースをリファクタリングし、さまざまなコンポーネント間の直接的な依存関係を制限しようとしています。ソース ツリーには、src/a、src/b、src/c といういくつかの最上位ディレクトリがあります。

一連の制限を適用したいと考えています。

  • a のファイルは、b または c のファイルに依存できません
  • b 内のファイルはファイル a に依存できますが、c には依存できません
  • c のファイルは b のファイルに直接依存できますが、a のファイルには依存できません

最初のものを強制するのは簡単です。次のような暗黙のルールがあります。

build/a/%.o : src/a/%.cpp
  $(CXX) -I src/a $(OTHER_FLAGS) -o $@ $<

a の下のファイルが b または c のヘッダー ファイルをインクルードしようとすると、ヘッダーが見つからないため、ビルドは失敗します。

2 番目の規則には、src/a と src/b をインクルード ディレクトリとして指定する同様の規則があります。問題は建物 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 からのファイルが含まれ (許可されます)、さらに a からのファイルが含まれます (これも許可されます)。次のようなコードを防止したいと考えています。

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 を含める必要がありますが、これにより 2 番目のケースもコンパイルできます。

私の問題に対する答えは、コンパイラから生成された依存関係を調べ、潜在的に違法な依存関係を見つけ、ソース ファイルを調べて、これが直接の依存関係であるかどうかを判断するスクリプトを作成することだと思います。コンパイラやメイクファイルの構造を組み合わせてこれを行う合理的な方法はありますか?

問題があれば、GNU Make 3.81 と g++ 4.5.3 を使用していますが、可能であれば移植可能にしたいと考えています。

アップデート

私たちは、ルールに従うのに努力が必要なものではなく、ルールに違反するのに努力が必要なものを探しています. (過去の経験から、後者はうまくいかないことが示されています。)他の回答にはいくつかの良いアイデアがありますが、私はスクリプトを書くことを言っているものを受け入れます。その周り。

皆さんの回答に感謝します。

4

4 に答える 4

0

これを既存のコードベースに適用しているという事実を考慮して、私は「検証スクリプト」アプローチを選択します。

そのため、ビルドプロセスを変更し、ビルドが失敗したときに依存関係を一度に1つずつ切断する代わりに、問題のないファイルのリストが表示されます。次に、「全体像」を念頭に置いてコードベースをリファクタリングできます。行った変更は、以前と同じMakefileを使用して構築されるため、テストとデバッグが簡素化されます。

リファクタリングされると、分析スクリプトは、将来の更新を検証するためのコンプライアンスチェッカーとして引き続き使用できます。

このような分析の開始点として考えられるのは、makedependまたはを使用することcpp -MMです。たとえば、質問にリストしたcpp/hファイルを使用します。

[me @ home]$find。
。
./b
./b/bh
./a
./a/ah
./c
./c/C_bad.cpp
./c/C.cpp
./c/c_bad.h

[me @ home] $ cpp -MM -Ia -Ib -Ic */*。cpp
C_bad.o:c / C_bad.cpp a / ah
Co:c / C.cpp b / bh a / ah

[me @ home] $#これはヘッダーファイルでも機能します
[me @ home] $ cpp -Ia -Ib -Ic -MM c / c_bad.h
c_bad.o:c / c_bad.ha / ah

これらの出力を解析して各cppファイルの依存関係を判別し、非準拠のファイルにフラグを立てることは、かなり簡単なはずです。

このアプローチの欠点は、直接依存関係と間接依存関係を区別できないことです。そのため、それが問題になる場合は、ソースを検査して直接依存関係を選択するための追加の手順を含める必要があります。

于 2012-12-18T11:14:34.200 に答える
0

-Iオプションをターゲット固有にすることができます。

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

ただし、これは gnu-make 固有のものであるため、移植性はありません。

于 2012-12-17T17:57:13.007 に答える
0

-Isrc最も簡単な方法は、すべてのインクルード パスを に変更することです。インクルード ステートメントは、完全な相対パスを持ちます

#include <a/a.h>

例えば。これにより、コードを自動的にチェックすることがはるかに簡単になります (おそらくメイクファイルではなくコミット フックで)。


別の方法として、A ヘッダーと 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

これらの make ルールにより、A と B が正常にビルドできるようになります。

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

これにより、C は B (および B のヘッダー内のマクロ) を介してのみアクセスできるようになります。

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

これには、特に B のヘッダーである程度の規律が必要であることに注意してください。

于 2012-12-17T18:44:11.010 に答える
0

はい。しかし、それには手作業と訓練が必要です。

C をビルドするときは、src/b/*.h のヘッダーに依存できます。

プロジェクト B 内では、メイン ディレクトリ内のヘッダー ファイルは自己完結型であり、他のプロジェクトに依存してはなりません。B src/b/detail/*.h 内にサブディレクトリも必要です。ここでは、ヘッダー ファイルに src/a/*.h および src/b/*.h を含めることが許可されていますが、これは非公開の実装の詳細であり、b プロジェクトのソース ファイルでのみ使用できます。

于 2012-12-17T17:44:30.840 に答える