2017-12-09 8 views
0

私はCMakeやGNU Autotoolsのようなツールを知っていますが、私はCとC++のプロジェクトに使うために、自分自身でユニバーサルビルドシステムを作成しようとしています。私はそれがどのように動作するのかを簡単に説明し、うまくいけば誰かが改善かより良いデザインを提案することができます。普遍的なmakeベースのビルドシステムの設計

ビルドシステムは、プロジェクトのサブディレクトリの1つにあります(私はGitサブモジュールとしてインポートします)。プロジェクトのルートディレクトリには、いくつかのマクロを定義し、前記サブディレクトリからのメインメイクファイルを含むラッパーメイクファイルがあります。これはほとんどの作業を行います。つまり、のライブラリlib、バイナリbinなどのライブラリを出力し、ソースコードとDocBookドキュメントの自動依存関係を処理します。事実上の標準ターゲット:すべて,テスト,クリーン,インストールなどがあります。

ここでは、2つのバイナリ、fooとbarを構築するどのようなラッパーのmakefileだ、次のようになります。

# foo-specific macros 
FOO_SRC_FILES = foo1.c foo2.c foo3.c 
FOO_OBJ_FILES = $(FOO_SRC_FILES:.c=.o) 
FOO_BIN_FILE = foo 

# bar-specific macros 
BAR_SRC_FILES = bar1.c bar2.c 
BAR_OBJ_FILES = $(BAR_SRC_FILES:.c=.o) 
BAR_BIN_FILE = bar 

# Inform the build system about them 
SRC_FILES = $(FOO_SRC_FILES) $(BAR_SRC_FILES) 
OBJ_FILES = R(BAR_OBJ_FILES) $(BAR_OBJ_FILES) 
BIN_FILES = $(FOO_BIN_FILE) $(BAR_BIN_FILE) 

# Only install the binaries. If I were building a library, I would instead 
# select the "lib" and perhaps "include" directories. 
INSTALL = bin 
INSTALL_DIR = /usr/share 

# Use the build system 
include build/build.mk 

は今ここに問題があります。 build.mkはパターンルールを使用して依存関係ファイルとオブジェクトファイルを作成できますが、OBJ_FILESと、BIN_FILESの1つのみです。私はこのようになりますビルドシステムでは、次のようなパターンルールを置くのであれば:

$(BIN_DIR)/$(BIN_FILES): $(OBJ_FILES:%=$(OBJ_DIR)/%) $(LIB_FILES:%=$(LIB_DIR)/%) | $(BIN_DIR) 
    $(CC) $(LDFLAGS) -o [email protected] $(OBJ_FILES:%=$(OBJ_DIR)/%) -L $(LIB_DIR) $(LIB_FILES:lib%.a=-l %) 

その後、FOOバーが行うすべてとその逆とし、リンクに依存するであろう。 、

$(BIN_DIR)/$(FOO_BIN_FILE): $(FOO_OBJ_FILES:%=$(OBJ_DIR)/%) $(FOO_LIB_FILES:%=$(LIB_DIR)/%) | $(BIN_DIR) 
    $(CC) $(LDFLAGS) -o [email protected] $(FOO_OBJ_FILES:%=$(OBJ_DIR)/%) -L $(LIB_DIR) $(FOO_LIB_FILES:lib%.a=-l %) 

$(BIN_DIR)/$(BAR_BIN_FILE): $(BAR_OBJ_FILES:%=$(OBJ_DIR)/%) $(BAR_LIB_FILES:%=$(LIB_DIR)/%) | $(BIN_DIR) 
    $(CC) $(LDFLAGS) -o [email protected] $(BAR_OBJ_FILES:%=$(OBJ_DIR)/%) -L $(LIB_DIR) $(BAR_LIB_FILES:lib%.a=-l %) 

同じ問題が同様のライブラリに適用されるの:だから私がやって終わることは、彼らがbuild.mkに属しているように感じていても、ラッパーメイクファイルにこれらのルールを置くようにユーザーに求めていますコース。このルールは、ほぼそのままコピーして貼り付けることができます。プレフィックスのみを変更する必要があります(たとえば、FOOまたはBAR)。含まこの問題を解決するために

アイデア:

  • (例えば、fooの用とバーのための別の)別のもののために別々のラッパーmakefileを持っているユーザーに尋ねるが、それはちょうどひどいです。
  • 少し変更してm4を使っていくつかの前処理をしていますが、より洗練された解決策が存在しないかぎり、それを通過したくありません。

私は本当にいくつかのアイデアに感謝します。

PS:最後の2つのコードサンプルのパターンマッチング式は、テキスト関数で置き換えることができますが、それらはGNU Make固有です。私が使ったスタイルは移植性が高く、実際にはPOSIX標準の次のバージョンの追加リストに載っています。

答えて

0

自分のCプロジェクトのために同様のシステムを開発し始めましたが、私が使用するロジックはGNU Make特有の機能に依存しています。

主な考え方は、$(eval)$(call)の組み合わせを使用して、ビルドシステムのロジックを定義し、プロジェクトツリーに適用することです。

SRC := foo.c foo_bar.c bar.c 
TARGET := foo_bar 
SRC_DIR := src 
OBJ_DIR := obj 

は、私が実際にはマクロで変数を定義し、展開されます。 そうするためには、私は私のディレクトリとサブディレクトリ私はSrcs.mkに名前を付ける以下の形式のメイクファイルの断片のそれぞれに持っています$(call)とし、$(eval)に渡します。それは、このように定義されています:

define get_local_variables 
include Srcs.mk 
$1SRC := $(SRC) 
$1SRC_DIR := $(SRC_DIR) 
$1OBJ_DIR := $(OBJ_DIR) 
$1TARGET := $(TARGET) 
TARGET := 
SRC := 
SRC_DIR := 
OBJ_DIR := 

$(call get_local_variables, $(DIR))$(DIR)の内容に置き換え$1で、上記に拡大していきます。その後、Makefileのフラグメントとして扱われます。$(eval)

このようにして、私は自分のディレクトリごとにディレクトリごとの変数を埋めます。 私は、この原則を使用して、この変数を使用する一握りの規則または他の規則を持っています。

### Macros ### 
obj = $(patsubst %.c,$($1OBJ_DIR)/%.o,$($1SRC)) 

define standard_rules 
$($1TARGET): $(obj) 
    $$(LINK) 

$(obj): $($1OBJ_DIR)/%.o:$($1SRC_DIR)/%.c | $($1OBJ_DIR) 
    $$(COMPILE) 

endef 

変数は、その後拡大し$(eval)によってようメイクファイルの断片を読み、$(call)計算されます。

(私は静的パターンルールを使用しますが、そのアイデアには本質的ではありません)。

アイデアは基本的には、ディレクトリを名前空間の一種として定義し、データを添付して機能を実行することです。 私の実際のシステムはちょっと複雑ですが、その全体のアイデアです。

$(eval)$(call)をエミュレートする方法がある場合(これらはGNU make固有のものだとは思いますが、わかりません)、その方法を試すことができます。

また、各ディレクトリに変数SUBDIRSを追加し、現在のマクロで実行される同じマクロを再帰的に実行することで、非再帰的なmakeを実装することもできます。しかし、makeの展開と評価の順序でそれを混乱させないように、慎重に行われていなければなりません。 だからget_local_variablesと評価され、残りのマクロはになる前にと評価されます。

(見たい場合は、私のプロジェクトはGithubのアカウントに表示されます)make-build-systemですが、これで十分です^)。

ただし、状況が悪くなったときにデバッグするのは非常に面倒です。少なくとも$(call)または$(eval)の拡張で、(少なくとも、GNU)が基本的にエラーを(もしあれば)キャッチしてください。

0

prorabというGNU makeの独自の非再帰的ビルドシステムを開発しました。このシステムでは、次のように問題を解決しました。あなたの問題を解決するため

アプローチは@VannTenが、私は次のバイナリのためのルールを構築定義する前に、すべての状態変数をきれいにするマクロを使用することを除いて、彼の答えで説明したものに似ています。

例えば、2つのバイナリをビルドするメイクファイルは、次のようになります。

include prorab.mk 

this_name := AppName 
this_ldlibs += -lsomelib1 
this_cxxflags += -I../src -DDEBUG 
this_srcs := main1.cpp MyClass1.cpp 

$(eval $(prorab-build-app)) 

$(eval $(prorab-clear-this-vars)) 

this_name := AnotherppName 
this_ldlibs += -lsomelib1 
this_cxxflags += -I../src -DDEBUG 
this_srcs := main2.cpp MyClass2.cpp 

$(eval $(prorab-build-app)) 

ので、この例では、2つのバイナリ構築します:AppNameAnotherppNameを。 ビルドがthis_ -prefixed変数の数を設定し、すべてのビルドの定義に展開$(eval $(prorab-build-app))を呼び出すことで設定されて見ることができるように、クリーンなどのルールをインストールします。

$(eval $(prorab-clear-this-vars))を呼び出すと、this_ -prefixed variablesがすべてクリアされ、次のバイナリのためにゼロから再び定義できるようになります。

また、prorab.mkを含む最初の行は、もちろん、すべてのthis_の修正済みの変数も消去するので、makefileを安全にインクルードすることができます。

あなたはここにhttps://github.com/igagis/prorab/blob/master/wiki/TutorialBasicConcepts.md

+0

そのおかげでビルドシステムの概念についての詳細を読むことができます。残念ながら、移植可能なmakefileを書く必要があるので、私はPOSIXに従います。したがって、私はevalのようなmake関数を使うことはできません。 –

+0

さて、これにはおそらくエレガントな解決策はありません。実際、 'GNU make'は移植性が高く、すべてのLinuxディストリビューション、MacOS X、Windows(Cygwin)に存在します。ですから、 'GNU make 'が利用できない非常にまれなシステムをサポートする必要があるのでしょうか?あなたの移植性の必要性を再考したいかもしれません。 – igagis

関連する問題