web-dev-qa-db-ja.com

Makefile(自動依存関係生成)

簡単に言えば、

#basic makefile rule
target: dependencies
    recipe

問題:依存関係を自動的に生成したい。

たとえば、私はこれを回したいと思っています:

#one of my targets
file.o: file.cpp 1.h 2.h 3.h 4.h 5.h 6.h 7.h 8.h another.h lots.h evenMore.h
    $(COMPILE)

これに:

#one of my targets
file.o: $(GENERATE)
    $(COMPILE)

そして、それが可能かどうかはあまりわかりません。

私が知っていること:

このコンパイラフラグを使用できます。

g++ -MM file.cpp

そして、適切なターゲットと依存関係を返します。
したがって、例から、次のように返されます。

file.o: file.cpp 1.h 2.h 3.h 4.h 5.h 6.h 7.h 8.h another.h lots.h evenMore.h  

ただし、 'make'では、ルールのターゲットまたは依存関係セクションにシェルコードを明示的に書き込むことはできません:(
私は Shell という「make」関数があることを知っています

しかし、これを依存関係としてプラグインしてパースマジックを行うことはできません。これは、ターゲットを表すマクロ$ @に依存しているためです。または、少なくともそれが問題だと思います。

"file.cpp"依存関係をこのmakefile関数で置き換えるだけでも試しましたが、これも機能しません。

#it's suppose to turn the $@ (file.o) into file.cpp
THE_CPP := $(addsuffix $(.cpp),$(basename $@))

#one of my targets
file.o: $(THE_CPP) 1.h 2.h 3.h 4.h 5.h 6.h 7.h 8.h another.h lots.h evenMore.h
    $(COMPILE)
#this does not work

Google全体で、2つの解決策があるようです。どちらも完全には把握していません。
From GNU Make Manual

GNU Make Manual one is out-of-date

だから私の究極の質問は:私がやりたいようにそれを行うことは可能ですか?
そうでない場合、誰かがこれらのサイトの1つからコードを分解し、それらがどのように機能するかを詳細に説明できます。必要に応じて、これらの方法の1つを実装しますが、理解する前に、コードのチャンクをメイクファイルに貼り付けるだけで疲れています。

28
Trevor Hickey

GCCの新しいバージョンには、-MDと一緒に使用できる-MPオプションがあります。プロジェクトのCPPFLAGS変数に単に-MPと-MDを追加し(C++をコンパイルするためのカスタムレシピを作成しなかった)、 "-include $(SRC:.cpp = .d)"行を追加しました。

-MDおよび-MPを使用すると、依存関係(奇妙なsedを使用する必要がない)とダミーターゲット(ヘッダーファイルを削除してもエラーが発生しないようにする)の両方を含む依存関係ファイルが提供されます。

25
teambob

すばらしい答えですが、私のビルドでは、ビルドタイプ(つまり、デバッグとリリース)に基づいて.objファイルをサブディレクトリに配置しました。たとえば、デバッグをビルドする場合、すべてのオブジェクトファイルをbuild/debugフォルダーに配置します。上記の複数行のsedコマンドで正しい宛先フォルダーを使用しようとするのは骨の折れる作業でしたが、いくつかの実験を行った後、自分のビルドに最適なソリューションを見つけました。うまくいけば、それは他の誰かにも役立つでしょう。

これがスニペットです:

# List my sources
CPP_SOURCES := foo.cpp bar.cpp

# If I'm debugging, change my output location
ifeq (1,$(DEBUG))
  OBJ_DIR:=./obj/debug
  CXXFLAGS+= -g -DDEBUG -O0 -std=c++0x
else
  CXXFLAGS+= -s -O2 
  OBJ_DIR:=./obj/release
endif

# destination path macro we'll use below
df = $(OBJ_DIR)/$(*F)

# create a list of auto dependencies
AUTODEPS:= $(patsubst %.cpp,$(OBJ_DIR)/%.d,$(CPP_SOURCES))

# include by auto dependencies
-include $(AUTODEPS)

.... other rules

# and last but not least my generic compiler rule
$(OBJ_DIR)/%.o: %.cpp 
    @# Build the dependency file
    @$(CXX) -MM -MP -MT $(df).o -MT $(df).d $(CXXFLAGS) $< > $(df).d
    @# Compile the object file
    @echo " C++ : " $< " => " $@
    @$(CXX) -c $< $(CXXFLAGS) -o $@

詳細については、私の汎用ビルドルールでのCXXの最初の実行が興味深いものです。 「sed」コマンドを使用していないことに注意してください。新しいバージョンのgccは、必要なすべてを実行します(私はgcc 4.7.2を使用しています)。

-MMは、プロジェクトヘッダーを含むがシステムヘッダーは含まないメインの依存関係ルールを構築します。このままにしておくと、.objファイルのパスが正しくありません。したがって、-MTオプションを使用して、.obj宛先への「実際の」パスを指定します。 (私が作成した「df」マクロを使用)。
また、2番目の-MTオプションを使用して、結果の依存関係ファイル(つまり:.dファイル)が正しいパスを持ち、それがターゲットリストに含まれているため、ソースと同じ依存関係があることを確認しますファイル。

最後に重要なこととして、-MPオプションが含まれています。これは、ヘッダーを削除してmakeがエラーを生成する場合に発生する問題を解決するために、各ヘッダーにスタブルールを作成するようにgccに指示します。

Sedにパイプする代わりにすべての依存関係の生成にgccを使用しているので、ビルドはより速いと思います(ただし、この時点ではビルドが比較的小さいので、まだ証明していません)。私がこれを改善できる方法を見つけたら、私は常に提案を受け入れます。楽しい

7
Zoccadoum

記録のために、これは依存関係を自動的に生成する方法です。

CPPFLAGS = -std=c++1y -MD -MP 

SRC = $(wildcard *.cpp)
all: main

main: $(SRC:%.cpp=%.o)
    g++ $(CPPFLAGS) -o $@ $^

-include $(SRC:%.cpp=%.d)

コンパイラー・フラグ-MDおよび-MPが役立ちます。

5
Trevor Hickey

まず、THE_CPP=$(patsubst %.o,%.cpp,$@)

次に、make -pを実行して、makeの組み込みルールを理解できます。

通常の方法は、メイクファイルの依存関係を*.mdファイルに生成することです。

%.o: %.c
       $(COMPILE.c) $(OUTPUT_OPTION) $< -MMD -MF $(patsubst %.c,%.md,$@)

そして後であなたのMakefileそれらを含む のようなもので

-include $(wildcard *.md)

しかし、 omake など、他の多くのビルダーの使用を検討することもできます

私は検索で$(Shell ...)関数を使用することを好みます。以下は、私のメイクファイルの1つのサンプルです。

SRCDIR = src
OBJDIR = obj
LIBDIR = lib
DOCDIR = doc

# Get Only the Internal Structure of Directories from SRCDIR
STRUCTURE := $(Shell find $(SRCDIR) -type d)

#Filter-out hidden directories
STRUCTURE := $(filter-out $(Shell find $(SRCDIR)/.* -type d),$(STRUCTURE))

# Get All Files From STRUCTURE
CODEFILES := $(addsuffix /*,$(STRUCTURE))
CODEFILES := $(wildcard $(CODEFILES))


## Filter Only Specific Files
SRCFILES := $(filter %.c,$(CODEFILES))
HDRFILES := $(filter %.h,$(CODEFILES))
OBJFILES := $(subst $(SRCDIR),$(OBJDIR),$(SRCFILES:%.c=%.o))
DOCFILES := $(addprefix $(DOCDIR)/,             \
            $(addsuffix .md,                    \
            $(basename $(SRCFILES))))


# Filter Out Function main for Libraries
LIBDEPS := $(filter-out $(OBJDIR)/main.o,$(OBJFILES))

このアプローチでは、最初にすべての内部ディレクトリ構造を任意の深さで取得します。次に、構造内のすべてのファイルを取得します。現時点では、filter、filter-out、addsuffixなどを使用して、毎回必要なものを正確に取得できます。

この例は* .cファイルを対象としていますが、*。cppに変更することもできます。

1
user4713908

うーん!ベータ版の投稿のコードをなんとかして小さなテストプロジェクトで作業することができました。
私が注意する必要があるのは、これに遭遇する可能性のある他の人のために、bashシェル(私がそうでした)を使用している場合、作成からエスケープするには、ポンド記号の前にエスケープ文字を追加する必要があります。式の残りの部分はコメントです。 (コードの4行目を参照)

%.o : %.cpp  
    g++ -c -MD -o $@ $<  
    cp $*.d $*.P; \  
    sed -e 's/\#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \  
        -e '/^$$/ d' -e 's/$$/ :/' < $*.d >> $*.P; \  
    rm -f $*.d  
-include *.P  

GNU Make、3rd Edition。)でプロジェクトを管理する で見つけた情報を共有したいと思います。これは、この問題に関するいくつかの重要な問題を指摘しており、まだ完全には把握していません。
Makeマニュアルページ にある方法と同様の方法が本に記載されています。
これは次のようになります:

include $(subst .c,.d,$(SOURCES))

%.d: %.c
    $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
    sed 's,\($*\).o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
    rm -f $@.$$$$

これは私が信じていることです。
すぐに、「make」はすべてのソースファイルに「.d」ファイルを含めたいと考えています。
最初は.dファイルが存在しないため、不足しているすべての.dファイルを作成するために、コードのチャンクが繰り返し実行されます。
これは、すべての.dファイルが作成されてmakefileに含まれるまで、makeが何度も最初から繰り返されることを意味します。
各「.d」ファイルはベータが言ったものです:依存関係のセットとレシピなしのターゲット。

ヘッダーファイルが変更された場合、それらに含まれているルールでは、最初に依存関係を更新する必要があります。これは私を少し思い起こさせるものですが、コードのチャンクを再び呼び出すことができるのはなぜですか? .dファイルの更新に使用されるので、.hファイルが変更された場合、どのように呼び出されますか?これとは別に、オブジェクトのコンパイルにはデフォルトのルールが使用されていることに気付きました。この説明に対する明確化/誤解はありがたいです。


この本の後半で、この方法の問題と、高度な自動依存関係生成の実装にも存在すると私が信じている問題を指摘しています。
問題1:非効率的です。 'make'は、.dファイルを作成するたびに再起動する必要があります
問題2:makeは、欠落しているすべての.dファイルに対して警告メッセージを生成します。これはほとんどの場合迷惑であり、追加することで非表示にできますincludeステートメントの前の「-」。
問題3:不要になったためにsrcファイルを削除すると、次にコンパイルしようとすると「make」がクラッシュします一部の.dファイルには依存関係として欠落しているsrcがあり、そのsrcを再作成するルールがないため、makeはそれ以上の処理を拒否します。

これらの問題の修正はTromeyの方法であると彼らは言っていますが、コードはWebサイトのコードとは非常に異なって見えます。多分それは彼らがいくつかのマクロを使用し、それを関数呼び出しにし、それをわずかに異なるように書いたからだ。まだ調査中ですが、これまでに得たいくつかの発見を共有したいと思います。うまくいけば、これにより少し少し議論が開かれ、私はこのすべての底に近づくことができます。

1
Trevor Hickey

Auto-Dependency Generation 記事の内容に基づいて作成した以前の投稿のコメントで参照されている annotated makefile project には generic Makefileが含まれています コメントで注釈が付けられ、3つの.cファイルと2つの.hファイルを持つ単純なプロジェクトに実装されます。以下のMakefileの全内容を参照してください。単純なプロジェクトはTODOセクションをカスタマイズするだけでよい

# See http://make.mad-scientist.net/papers/advanced-auto-dependency-generation/
# for the template used to start this file

# -- TODO: customize the list below for your project ---
# List of source .c files used with the project
SRCS := main.c file1.c file2.c

# The aplication generated 
APPNAME = depend-generation-test
# -- End of customization section ---

# Replace .c extension on SRCS to get objfiles using gnu make pattern rules and substitution references.
# See https://www.gnu.org/software/make/manual/html_node/Pattern-Intro.html#Pattern-Intro for pattern rules and 
# https://www.gnu.org/software/make/manual/html_node/Substitution-Refs.html#Substitution-Refs for substitution references overview
OBJFILES := $(SRCS:%.c=%.o)

# Build the app you've specified in APPNAME for the "all" or "default" target
all : $(APPNAME)
default : $(APPNAME)

# Remove all build intermediates and output file
clean : ; @rm -rf $(APPNAME) *.o

# Build the application by running the link step with all objfile inputs
$(APPNAME) : $(OBJFILES)
    $(CC) $(LDFLAGS) $^ -o $(APPNAME)

# Add all warnings/errors to cflags default.  This is not required but is a best practice
CFLAGS += -Wall -Werror

# The below content is from  http://make.mad-scientist.net/papers/advanced-auto-dependency-generation/
# with the following changes:
#   1) Added comments
#   2) Removed TARGET_Arch from COMPILE.c since it's no longer listed in the [default rules](https://www.gnu.org/software/make/manual/html_node/Catalogue-of-Rules.html#Catalogue-of-Rules) and [isn't documented](https://lists.gnu.org/archive/html/help-make/2010-06/msg00005.html)
# Original content below is:
# Copyright © 1997-2019 Paul D. Smith Verbatim copying and distribution is permitted in any medium, provided this notice is preserved.

# The directory (hidden) where dependency files will be stored
DEPDIR := .deps
# Flags passed to gcc to automatically build dependencies when compiling
# See https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html for detail about variable names
# $@ references the target file of the rule and will be "main.o" when compiling "main.c"
# $* references the stem of the rule, and will be "main" when target is "main.o"
DEPFLAGS = -MT $@ -MMD -MP -MF $(DEPDIR)/$*.d

# Rules for compiling a C file, including DEPFLAGS along with Implicit GCC variables.
# See https://www.gnu.org/software/make/manual/html_node/Implicit-Variables.html
# and see https://www.gnu.org/software/make/manual/html_node/Catalogue-of-Rules.html#Catalogue-of-Rules
# for the default c rule
COMPILE.c = $(CC) $(DEPFLAGS) $(CFLAGS) $(CPPFLAGS) -c

# Delete the built-in rules for building object files from .c files
%.o : %.c
# Define a rule to build object files based on .c or dependency files by making the associated dependency file
# a prerequisite of the target.  Make the DEPDIR an order only prerequisite of the target, so it will be created when needed, meaning
# the targets won't get rebuilt when the timestamp on DEPDIR changes
# See https://www.gnu.org/software/make/manual/html_node/Prerequisite-Types.html for order only prerequesites overview.
%.o : %.c $(DEPDIR)/%.d | $(DEPDIR)
    $(COMPILE.c) $(OUTPUT_OPTION) $<

# Create the DEPDIR when it doesn't exist
$(DEPDIR): ; @mkdir -p $@

# Use pattern rules to build a list of DEPFILES
DEPFILES := $(SRCS:%.c=$(DEPDIR)/%.d)
# Mention each of the dependency files as a target, so make won't fail if the file doesn't exist
$(DEPFILES):

# Include all dependency files which exist, to include the relevant targets.
# See https://www.gnu.org/software/make/manual/html_node/Wildcard-Function.html for wildcard function documentation
include $(wildcard $(DEPFILES))
0
dan