web-dev-qa-db-ja.com

Makefile、ヘッダーの依存関係

ルール付きのメイクファイルがあるとしましょう

%.o: %.c
 gcc -Wall -Iinclude ...

ヘッダーファイルが変更されるたびに* .oを再構築する必要があります。依存関係のリストを作成するのではなく、/includeが変更されたら、ディレクトリ内のすべてのオブジェクトを再構築する必要があります。

これに対応するためにルールを変更する良い方法を考えることはできません、私は提案を受け入れています。ヘッダーのリストをハードコーディングする必要がない場合のボーナスポイント

86
Mike

GNUコンパイラを使用している場合、コンパイラは依存関係のリストを作成できます。Makefileフラグメント:

depend: .depend

.depend: $(SRCS)
        rm -f ./.depend
        $(CC) $(CFLAGS) -MM $^ -MF  ./.depend;

include .depend

または

depend: .depend

.depend: $(SRCS)
        rm -f ./.depend
        $(CC) $(CFLAGS) -MM $^ > ./.depend;

include .depend

SRCSは、ソースファイルのリスト全体を指す変数です。

ツールmakedependもありますが、gcc -MM

113
dmckee

ほとんどの回答は、驚くほど複雑または誤っています。しかし、シンプルで堅牢な例が他の場所に投稿されています[ codereview ]。確かに、gnuプリプロセッサが提供するオプションは少しわかりにくいです。ただし、-MMを使用したビルドターゲットからのすべてのディレクトリの削除は文書化されており、バグ[ gpp ]ではありません。

デフォルトでは、CPPはメイン入力ファイルの名前を取り、anyディレクトリコンポーネントを削除しますおよび「.c」などの任意のファイルサフィックス。プラットフォームの通常のオブジェクトサフィックスを追加します。

(多少新しい)-MMDオプションはおそらくあなたが望むものです。完全を期すために、複数のsrc dirsをサポートし、いくつかのコメント付きのdirをビルドするメイクファイルの例を示します。ビルドディレクトリのない単純なバージョンについては、[ codereview ]を参照してください。

CXX = clang++
CXX_FLAGS = -Wfatal-errors -Wall -Wextra -Wpedantic -Wconversion -Wshadow

# Final binary
BIN = mybin
# Put all auto generated stuff to this build dir.
BUILD_DIR = ./build

# List of all .cpp source files.
CPP = main.cpp $(wildcard dir1/*.cpp) $(wildcard dir2/*.cpp)

# All .o files go to build dir.
OBJ = $(CPP:%.cpp=$(BUILD_DIR)/%.o)
# Gcc/Clang will create these .d files containing dependencies.
DEP = $(OBJ:%.o=%.d)

# Default target named after the binary.
$(BIN) : $(BUILD_DIR)/$(BIN)

# Actual target of the binary - depends on all .o files.
$(BUILD_DIR)/$(BIN) : $(OBJ)
    # Create build directories - same structure as sources.
    mkdir -p $(@D)
    # Just link all the object files.
    $(CXX) $(CXX_FLAGS) $^ -o $@

# Include all .d files
-include $(DEP)

# Build target for every single object file.
# The potential dependency on header files is covered
# by calling `-include $(DEP)`.
$(BUILD_DIR)/%.o : %.cpp
    mkdir -p $(@D)
    # The -MMD flags additionaly creates a .d file with
    # the same name as the .o file.
    $(CXX) $(CXX_FLAGS) -MMD -c $< -o $@

.PHONY : clean
clean :
    # This should remove all generated files.
    -rm $(BUILD_DIR)/$(BIN) $(OBJ) $(DEP)

単一のターゲットに複数の依存行がある場合、依存関係は単純に結合されるため、この方法は機能します。

a.o: a.h
a.o: a.c
    ./cmd

以下と同等です:

a.o: a.c a.h
    ./cmd

単一のターゲットに対して複数の依存関係行を作成しますか?

60
Sophie

here を投稿したように、gccは依存関係を作成し、同時にコンパイルできます。

DEPS := $(OBJS:.o=.d)

-include $(DEPS)

%.o: %.c
    $(CC) $(CFLAGS) -MM -MF $(patsubst %.o,%.d,$@) -o $@ $<

'-MF'パラメーターは、依存関係を保存するファイルを指定します。

'-include'の先頭にあるダッシュは、.dファイルが存在しない場合(最初のコンパイル時など)に続行するようにMakeに指示します。

-oオプションに関するgccのバグがあるようです。オブジェクトのファイル名をobj/_file__c.oに設定すると、生成されたfile。dにはfileが含まれます。o、obj/_file__c.oではありません。

25
Martin Fido

次のようなものはどうですか:

includes = $(wildcard include/*.h)

%.o: %.c ${includes}
    gcc -Wall -Iinclude ...

ワイルドカードを直接使用することもできますが、複数の場所でワイルドカードが必要であることがわかります。

これは、すべてのオブジェクトファイルがすべてのヘッダーファイルに依存することを前提としているため、小さなプロジェクトでのみうまく機能することに注意してください。

23

上記のMartinのソリューションは素晴らしい働きをしますが、サブディレクトリにある.oファイルを処理しません。 Godricは、-MTフラグがその問題を処理するが、同時に.oファイルが正しく書き込まれないようにすることを指摘しています。以下は、これらの問題の両方を処理します。

DEPS := $(OBJS:.o=.d)

-include $(DEPS)

%.o: %.c
    $(CC) $(CFLAGS) -MM -MT $@ -MF $(patsubst %.o,%.d,$@) $<
    $(CC) $(CFLAGS) -o $@ $<
4
michael

これにより、ジョブが正常に実行され、指定されているサブディレクトリも処理されます。

    $(CC) $(CFLAGS) -MD -o $@ $<

gcc 4.8.3でテスト済み

3
g24l

ここに2つのライナーがあります。

CPPFLAGS = -MMD
-include $(OBJS:.c=.d)

これは、OBJSにすべてのオブジェクトファイルのリストがある限り、デフォルトのmakeレシピで機能します。

1
tbodt

Sophieの answer を少し修正したバージョンで、*。dファイルを別のフォルダーに出力できます(依存関係ファイルを生成する興味深い部分のみを貼り付けます)。

$(OBJDIR)/%.o: %.cpp
# Generate dependency file
    mkdir -p $(@D:$(OBJDIR)%=$(DEPDIR)%)
    $(CXX) $(CXXFLAGS) $(CPPFLAGS) -MM -MT $@ $< -MF $(@:$(OBJDIR)/%.o=$(DEPDIR)/%.d)
# Generate object file
    mkdir -p $(@D)
    $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@

パラメーターに注意してください

-MT $@

生成された* .dファイルのターゲット(オブジェクトファイル名)に、ファイル名だけでなく* .oファイルへのフルパスが含まれていることを確認するために使用されます。

-MMDと-c(Sophieの version のように)を組み合わせて使用する場合、このパラメーターが必要ない理由はわかりません)。この組み合わせでは、*。oファイルの完全パスを* .dファイルに書き込むようです。この組み合わせがない場合、-MMDは* .dファイルにディレクトリコンポーネントなしで純粋なファイル名のみを書き込みます。たぶん、誰かが-cと組み合わせたときに-MMDが完全なパスを書き込む理由を知っているかもしれません。 g ++のマニュアルページにはヒントがありません。

0
MaximumFPS

次は私のために働く:

DEPS := $(OBJS:.o=.d)

-include $(DEPS)

%.o: %.cpp
    $(CXX) $(CFLAGS) -MMD -c -o $@ $<
0
Marcel Keller

マイケル・ウィリアムソンによる受け入れられた答えよりも、このソリューションのほうが好きです。それは、sources + inlineファイル、そしてsources + headers、そして最後にsourcesへの変更をキャッチします。ここでの利点は、わずかな変更を加えただけではライブラリ全体が再コンパイルされないことです。数個のファイルを含むプロジェクトではそれほど大きな考慮事項ではありません。10個または100個のソースがある場合は、違いに気付くでしょう。

COMMAND= gcc -Wall -Iinclude ...

%.o: %.cpp %.inl
    $(COMMAND)

%.o: %.cpp %.hpp
    $(COMMAND)

%.o: %.cpp
    $(COMMAND)
0