ソースファイルが異なるディレクトリにあるメイクファイル


135

ディレクトリ構造が次のようなプロジェクトがあります:

                         $projectroot
                              |
              +---------------+----------------+
              |               |                |
            part1/          part2/           part3/
              |               |                |
       +------+-----+     +---+----+       +---+-----+
       |      |     |     |        |       |         |
     data/   src/  inc/  src/     inc/   src/       inc/

c / c ++ソースファイルの一部を補完/リンクできる部分/ src(または実際にはどこにでもある)のmakefileをどのように記述すればよいですか?/ src?

-I $ projectroot / part1 / src -I $ projectroot / part1 / inc -I $ projectroot / part2 / src ...

それがうまくいくなら、それを行う簡単な方法はありますか?対応する各部分にメイクファイルがあるプロジェクトを見たことがありますか?フォルダ。[この投稿では、bash構文のように疑問符を使用しました]



1
オリジナルのgnuマニュアル(gnu.org/software/make/manual/html_node/Phony-Targets.html)のPhony Targetsには、のサンプルがありrecursive invocation、非常にエレガントです。
Frank Nocke 14

そのテキストグラフィックを作成するためにどのツールを使用しましたか?
Khalid Hussain 2017

回答:


114

従来の方法はMakefile、サブディレクトリ(、、など)のそれぞれにを置きpart1part2それらを個別に構築できるようにすることです。さらに、Makefileすべてをビルドするプロジェクトのルートディレクトリにを置きます。「ルート」Makefileは次のようになります。

all:
    +$(MAKE) -C part1
    +$(MAKE) -C part2
    +$(MAKE) -C part3

makeターゲットの各行は独自のシェルで実行されるため、ディレクトリツリーをさかのぼって他のディレクトリに移動することを心配する必要はありません。

GNU makeマニュアルのセクション5.7をご覧になることをお勧めします。とても助かります。


26
これは+$(MAKE) -C part1etcである必要があります。これにより、Makeのジョブ制御がサブディレクトリで機能できるようになります。
ephemient

26
これは古典的なアプローチであり、広く使用されていますが、プロジェクトが成長するにつれて悪化するいくつかの点で最適ではありません。デイブヒントンには、従うべき指針があります。
dmckee ---元モデレーターの子猫

89

あるサブディレクトリのコードが別のサブディレクトリのコードに依存している場合は、トップレベルの単一のmakefileを使用する方がよいでしょう。

有害と見なされる再帰的作成を参照完全な根拠については、と。ただし、基本的には、ファイルを再構築する必要があるかどうかを判断するために必要な完全な情報を作成する必要があります。あなたのプロジェクト。

上記のリンクにアクセスできないようです。同じドキュメントにアクセスできます:


3
ありがとう、これに気づかなかった。「正しく動作する」方法や標準として受け入れられている方法ではなく、物事を行う「正しい方法」を知ることは非常に役立ちます。
tjklemz 2013年

3
再帰的なmakeは、実際にはうまくいかなかったため、有害であると見なされていました。最近では有害とは見なされていません。実際、autotools / automakeが大規模なプロジェクトを管理する方法です。
エドウィンバック

36

VPATHオプションが役立つ場合があります。これは、ソースコードを探すディレクトリをmakeに指示します。ただし、インクルードパスごとに-Iオプションが必要です。例:

CXXFLAGS=-Ipart1/inc -Ipart2/inc -Ipart3/inc
VPATH=part1/src:part2/src:part3/src

OutputExecutable: part1api.o part2api.o part3api.o

これにより、一致するpartXapi.cppファイルがVPATHで指定されたディレクトリのいずれかで自動的に検索され、コンパイルされます。ただし、これは、srcディレクトリがサブディレクトリに分割されている場合により便利です。あなたが説明することについては、他の人が言ったように、特に各部分が独立できる場合は、おそらく各部分のメイクファイルを用意するほうがよいでしょう。


2
この単純な完璧な答えがこれ以上票を獲得していないとは信じられません。私からの+1。
ニコラスハミルトン

2
私はサブフォルダー内のいくつかの異なるプロジェクトの上位ディレクトリにいくつかの共通のソースファイルを持っていてVPATH=..、私のために働いた!
EkriirkE 2016

23

必要なcppファイルを他のディレクトリにコンパイルするために、ルートMakefileにルールを追加できます。以下のMakefileの例は、目的の場所に移動するための良いスタートになるはずです。

CC = g ++
TARGET = cppTest
OTHERDIR = .. / .. / someotherpath / in / project / src

ソース= cppTest.cpp
ソース= $(OTHERDIR)/file.cpp

##ソース定義の終了
INCLUDE = -I./ $(AN_INCLUDE_DIR)  
INCLUDE = -I。$(OTHERDIR)/../ inc
##追加のインクルード

VPATH = $(OTHERDIR)
OBJ = $(join $(addsuffix ../obj/、$(dir $(SOURCE)))、$(notdir $(SOURCE:.cpp = .o))) 

##依存関係の宛先をsrcディレクトリに相対的な../.depに修正
DEPENDS = $(join $(addsuffix ../.dep/、$(dir $(SOURCE)))、$(notdir $(SOURCE:.cpp = .d))))

##デフォルトのルールが実行された
すべて:$(TARGET)
        @true

##クリーンルール
掃除:
        @ -rm -f $(TARGET)$(OBJ)$(DEPENDS)


##実際のターゲットを作るためのルール
$(TARGET):$(OBJ)
        @echo "============="
        @echo "ターゲット$ @をリンク"
        @echo "============="
        @ $(CC)$(CFLAGS)-o $ @ $ ^ $(LIBS)
        @echo-リンク終了-

##一般的なコンパイル規則
%.o:%.cpp
        @mkdir -p $(dir $ @)
        @echo "============="
        @echo "$ <をコンパイルしています"
        @ $(CC)$(CFLAGS)-c $ <-o $ @


## cppファイルからのオブジェクトファイルのルール
##各ファイルのオブジェクトファイルはobjディレクトリに配置されます
##実際のソースディレクトリから1レベル上。
../obj/%.o:%.cpp
        @mkdir -p $(dir $ @)
        @echo "============="
        @echo "$ <をコンパイルしています"
        @ $(CC)$(CFLAGS)-c $ <-o $ @

#「その他のディレクトリ」のルール「その他」のディレクトリごとに1つ必要です
$(OTHERDIR)/../ obj /%。o:%.cpp
        @mkdir -p $(dir $ @)
        @echo "============="
        @echo "$ <をコンパイルしています"
        @ $(CC)$(CFLAGS)-c $ <-o $ @

##依存関係ルールを作成する
../.dep/%.d:%.cpp
        @mkdir -p $(dir $ @)
        @echo "============="
        $ *。oの@echoビルド依存ファイル
        @ $(シェル)-ec '$(CC)-M $(CFLAGS)$ <| sed "s ^ $ *。o ^ .. / obj / $ *。o ^"> $ @ '

##「その他」のディレクトリの依存ルール
$(OTHERDIR)/../。dep /%。d:%.cpp
        @mkdir -p $(dir $ @)
        @echo "============="
        $ *。oの@echoビルド依存ファイル
        @ $(シェル)-ec '$(CC)-M $(CFLAGS)$ <| sed "s ^ $ *。o ^ $(OTHERDIR)/../ obj / $ *。o ^"> $ @ '

##依存関係ファイルを含める
-include $(DEPENDS)


7
私はこれがかなり古いことを知っていますが、SOURCEとINCLUDEの両方が1行後に別の割り当てによって上書きされていませんか?
skelliam 2015年

@skelliamはい、そうです。
nabroyan 16

1
このアプローチは、DRYの原則に違反しています。各「その他のディレクトリ」のコードを複製することは悪い考えです
ジーザスH

20

ソースが多くのフォルダーに分散していて、前述のように個々のMakefileがあることが理にかなっている場合は、再帰的なmakeが適切なアプローチですが、小規模なプロジェクトの場合、Makefile内のすべてのソースファイルを相対パスでリストする方が簡単です。このようにMakefileに

# common sources
COMMON_SRC := ./main.cpp \
              ../src1/somefile.cpp \
              ../src1/somefile2.cpp \
              ../src2/somefile3.cpp \

次に、VPATHこのように設定できます。

VPATH := ../src1:../src2

次に、オブジェクトを構築します。

COMMON_OBJS := $(patsubst %.cpp, $(ObjDir)/%$(ARCH)$(DEBUG).o, $(notdir $(COMMON_SRC)))

ルールは簡単です:

# the "common" object files
$(ObjDir)/%$(ARCH)$(DEBUG).o : %.cpp Makefile
    @echo creating $@ ...
    $(CXX) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<

そして、出力の構築はさらに簡単です:

# This will make the cbsdk shared library
$(BinDir)/$(OUTPUTBIN): $(COMMON_OBJS)
    @echo building output ...
    $(CXX) -o $(BinDir)/$(OUTPUTBIN) $(COMMON_OBJS) $(LFLAGS)

次の方法でVPATH生成を自動化することもできます。

VPATH := $(dir $(COMMON_SRC))

または、sort重複を削除するという事実を使用します(重要ではありませんが)。

VPATH := $(sort  $(dir $(COMMON_SRC)))

これは、いくつかのカスタムライブラリを追加し、それらを別のフォルダに置きたい小さなプロジェクトに最適です。どうもありがとうございました
zitroneneis

6

Make(再帰的かどうか)を使用することは、通常は避けたいと思うかもしれないことを指摘したほうがいいと思います。なぜなら、今日のツールと比較すると、学習、維持、スケーリングが難しいからです。

これはすばらしいツールですが、直接使用することは2010年以降は廃止されると考えるべきです。

もちろん、レガシープロジェクトなどの特別な環境で作業している場合を除きます。

IDE、CMake、またはハードコアの場合はAutotoolsを使用してください。

(反対投票のために編集され、ty Honzaが指摘した)


1
反対票が説明されているといいですね。私も自分でMakefileを作成していました。
IlDan 2009

2
私は「それをしないでください」という提案に賛成しています。KDevelopにはMakeやその他のビルドシステムを構成するための非常にグラフィカルなインターフェースがあり、私がMakefileを自分で作成している間、KDevelopは私の同僚に提供するものですが、私はAutotoolsリンクが役立つとは思わないでください。Autotoolsを理解するには、m4、libtool、automake、autoconf、shell、make、および基本的にスタック全体を理解する必要があります。
ephemient 2009

7
反対票はおそらくあなたがあなた自身の質問に答えているからです。問題は、Makefileを使用する必要があるかどうかではありませんでした。それらの書き方についてでした。
Honza 2012

8
できれば、数文で、Makefileを書くよりも現代のツールがどのように生活を楽にするのかを説明できればすばらしいでしょう。それらはより高いレベルの抽象化を提供しますか?または何?
Abhishek Anand

1
xterm、vi、bash、makefile ==は世界を支配し、cmakeはコードをハックできない人のためのものです。
μολὼν.λαβέ

2

RCの投稿はSUPERに役立ちました。$(dir $ @)関数を使用することを考えたことはありませんでしたが、それはまさに私が必要とするものでした。

parentDirには、ソースファイルが含まれる多数のディレクトリ(dirA、dirB、dirC)があります。さまざまなファイルが他のディレクトリのオブジェクトファイルに依存しているので、1つのディレクトリ内から1つのファイルを作成し、その依存関係に関連付けられたmakefileを呼び出すことによってその依存関係を作成できるようにしたいと考えました。

基本的に、RCに類似した一般的なルール(他のものもある)を備えた1つのMakefileをparentDirに作成しました。

%.o : %.cpp
        @mkdir -p $(dir $@)
        @echo "============="
        @echo "Compiling $<"
        @$(CC) $(CFLAGS) -c $< -o $@

この一般的なルールを継承するために、各サブディレクトリにはこの上位レベルのメイクファイルが含まれていました。各サブディレクトリのMakefileで、各ファイルが依存するすべてのものを追跡できるように、各ファイルのカスタムルールを記述しました。

ファイルを作成する必要があるときはいつでも、(本質的に)このルールを使用して再帰的に依存関係を作成しました。パーフェクト!

注:「makepp」と呼ばれるユーティリティがあり、このタスクをより直感的に実行しているように見えますが、移植性を考慮し、他のツールに依存しないため、この方法を選択しました。

お役に立てれば!


2

Makeの再帰的な使用

all:
    +$(MAKE) -C part1
    +$(MAKE) -C part2
    +$(MAKE) -C part3

これにより、makeジョブに分割して複数のコアを使用できます


2
これはどのようなことをするよりも優れていmake -j4ますか?
デビン

1
@devinは、私が見ているように、それは良くはありません。ジョブ制御を使用できるよう makeにするだけです。makeの別のプロセスを実行する場合、ジョブ制御の対象にはなりません。
Michael Pankov

1

私はこのようなものを探していましたが、試行錯誤して自分のメイクファイルを作成しましたが、それが「慣用的な方法」ではないことはわかっていますが、それはメイクを理解するための始まりであり、これは私にとってはうまくいきます。プロジェクトで試すことができます。

PROJ_NAME=mono

CPP_FILES=$(shell find . -name "*.cpp")

S_OBJ=$(patsubst %.cpp, %.o, $(CPP_FILES))

CXXFLAGS=-c \
         -g \
        -Wall

all: $(PROJ_NAME)
    @echo Running application
    @echo
    @./$(PROJ_NAME)

$(PROJ_NAME): $(S_OBJ)
    @echo Linking objects...
    @g++ -o $@ $^

%.o: %.cpp %.h
    @echo Compiling and generating object $@ ...
    @g++ $< $(CXXFLAGS) -o $@

main.o: main.cpp
    @echo Compiling and generating object $@ ...
    @g++ $< $(CXXFLAGS)

clean:
    @echo Removing secondary things
    @rm -r -f objects $(S_OBJ) $(PROJ_NAME)
    @echo Done!

私はそれが単純で、一部の人にとって私のフラグが間違っていることを知っていますが、私が言ったように、これは私のプロジェクトを複数のディレクトリでコンパイルし、すべてをリンクして私のビンを作成する最初のMakefileです。

私は提案を受け入れています:D


-1

私は使用することをお勧めしますautotools

//## 再帰的でないmakeが使用されている場合の衝突を回避するために、生成されたオブジェクトファイル(.o)をソースファイルと同じディレクトリに配置します。

AUTOMAKE_OPTIONS = subdir-objects

それをMakefile.am他の非常に単純なものに含めるだけです。

こちらがチュートリアルです。

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