Makefile、ヘッダーの依存関係


97

ルールを持つメイクファイルがあるとしましょう

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

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

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


以下に私の答えを書いて、私は関連リストを調べて見つけました:stackoverflow.com/questions/297514/…これは重複しているようです。クリス・ドッドの答えは私のものと同じですが、異なる命名規則を使用しています。
dmckee ---元モデレーターの子猫2010

回答:


116

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


2
このトリックは好きdependですが、ソースファイルが変更された場合にのみ実行するにはどうすればよいですか?それは関係なく毎回実行されるようです...
追跡

2
@chase:ええと、オブジェクトファイルへの依存関係を誤って作成しましたが、明らかにソースファイルにあるはずであり、2つのターゲットの依存関係の順序も間違っていました。それが私が記憶からタイプするために得るものです。やってみよう。
dmckee ---元モデレーターの子猫

4
すべてのファイルの前にプレフィックスを追加して、別のディレクトリにあることを示す方法はありbuild/file.oますか?
RiaD

SRCSをOBJECTSに変更しました。OBJECTSは* .oファイルのリストです。これは、依存が毎回実行されるのを防ぐようであり、ヘッダーファイルのみの変更もキャッチしました。これは以前のコメントに反するようです。何か不足していますか?
BigBrownBear00 2013

2
なぜセミコロンが必要なのですか?それを使わずに、または-MF ./.dependを最後の引数にしないで試した場合、最後のファイルの依存関係のみが$(SRCS)に保存されます。
humodz 2014年

71

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

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

(やや新しい)-MMDオプションはおそらくあなたが望むものです。完全を期すために、複数のsrc dirsとビルドdirsをサポートするmakefileの例にコメントを付けます。ビルドディレクトリのない単純なバージョンについては、[ 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)

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

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

以下と同等です。

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

で言及したように:Makefile単一のターゲットに対する複数の依存関係行?


1
私はこの解決策が好きです。make defineコマンドを入力したくありません。便利!!
Robert

1
OBJ変数の値にスペルミスがあります。「CPP必ず読む」CPPS
ctrucza

1
これが私の好ましい答えです。+1してください。これは、このページで意味のある唯一のものであり、再コンパイルが必要なすべての状況をカバーします(私が見ることができるものについて)(不要なコンパイルを避けながら、十分です)
Joost

1
初期状態では、hppとcppの両方が同じディレクトリにあるにもかかわらず、ヘッダーを見つけることができませんでした。
villasv 2016

1
ソースファイル(a.cppb.cpp)がにある./src/場合、その置換は行われ$(OBJ)=./build/src/a.o ./build/src/b.oませんか?
ガロア2018年

26

ここに投稿したように、 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に設定すると、生成されたファイル .dには、obj / _file__c.oではなく、ファイル .o が含まれます


4
これを試すと、すべての.oファイルが空のファイルとして作成されます。私のオブジェクトはビルドサブフォルダーにあり($ OBJECTSにはbuild / main.o build / smbus.o build / etc ...が含まれています)、これにより、明らかにバグで説明したように.dファイルが確実に作成されますが、 -MMと-MFを削除するとビルドされますが、.oファイルはまったくビルドされません。
bobpaul 2012年

1
-MTを使用すると、回答の最後の行のメモが解決され、各依存関係リストのターゲットが更新されます。
Godric Seer 2013

3
@bobpaul man gccは、「前処理後に停止する」を-MM意味するため-E-MMD代わりに必要です:stackoverflow.com/a/30142139/895245
Ciro Santilli郝海东冠状病六四事件法轮機能

23

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

includes = $(wildcard include/*.h)

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

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

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


おかげで、私はこれを必要以上に複雑にすることにしました
Mike

15
これは機能しますが、これに伴う問題は、小さな変更が加えられるたびにすべてのオブジェクトファイルが再コンパイルされることです。つまり、100個のソース/ヘッダーファイルがあり、1つだけに小さな変更を加えると、100個すべてが再コンパイルされます。 。
ニコラスハミルトン

1
ヘッダーファイルが変更されるたびにすべてのファイルが再構築されるため、これは非常に非効率的な方法であることを示すために、回答を実際に更新する必要があります。他の答えははるかに優れています。
xaxxon 2015年

2
これは非常に悪い解決策です。確かに小規模なプロジェクトで機能しますが、どのような生産規模のチームとビルドでも、これはひどいコンパイル時間につながり、make clean all毎回実行するのと同等になります。
Julien Guertault 2016年

私のテストでは、これはまったく機能しません。gcc行は全く実行されず、内蔵のルール(%o: %.cルール)が代わりに実行されます。
Penghe Geng

4

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

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

-include $(DEPS)

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

3

これはうまく機能し、指定されたサブディレクトリも処理します:

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

gcc 4.8.3でテストしました


3

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

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

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


1

私はこの解決策を好んでいます。MichaelWilliamsonの承認された回答よりも、ソース+インラインファイル、ソース+ヘッダー、最後にソースのみの変更をキャッチします。ここでの利点は、ほんの少しの変更を加えただけではライブラリ全体が再コンパイルされないことです。いくつかのファイルを含むプロジェクトでは、それほど大きな考慮事項ではありません。10個または100個のソースがある場合は、違いがわかります。

COMMAND= gcc -Wall -Iinclude ...

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

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

%.o: %.cpp
    $(COMMAND)

2
これは、対応する実装ファイル以外のcppファイルの再コンパイルを必要とするヘッダーファイルに何もない場合にのみ機能します。
matec 2014年


0

* .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 と組み合わせて使用するときにこのパラメーターが必要でない理由がわかりません(ソフィーのバージョンのように))。この組み合わせでは、*。oファイルの完全パスを* .dファイルに書き込みます。この組み合わせがない場合、-MMDはディレクトリコンポーネントを含まない純粋なファイル名のみを* .dファイルに書き込みます。たぶん誰かが-MMDが-cと組み合わされたときにフルパスを書き込む理由を知っています。g ++のmanページにヒントはありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.