2017-08-12 6 views
2

makefileが作成された後でアイデアを作成するという、トップレベルの再帰的なmakefileを1つ作成しようとしています。プロジェクト。GNU同じ名前の他のファイルを見たときにソースファイルを再利用するようにする

同じ名前のファイルがありますが、別のディレクトリにある場合、GNU makeは最初のディレクトリの.cppファイルを使用し、残りのビルド全体でリンカが実行される生成される.oファイルはすべて同じソースファイルに基づいているため、関数の複数の定義について不平を言います。

私が参照しているコードは私の実際のプロジェクトのコードではありませんが、私の主なプロジェクトをビルドしようとしているのと同じ問題があります。ソースコードがサブディレクトリにあるトップレベルディレクトリのレイアウトを次に示します。 "こんにちは" ハロー/ HELLO.CPPプリントと世界/ HELLO.CPP版画 "世界!!" -

[email protected]:~/work/temp/maketest$ tree 
. 
├── exec 
│   └── main.cpp 
├── hello 
│   ├── hello.cpp 
│   └── hello.h 
├── makefile 
└── world 
    ├── hello.cpp 
    └── hello.h 

この例は非常に簡単です。 main.cppは "HelloWorld !!"を印刷するために各関数を呼び出します。

ここに各ファイルがあります。

のexec/main.cppに

#include <iostream> 
#include "../hello/hello.h" 
#include "../world/hello.h" 

int main() 
{ 
    print_hello(); 
    print_world(); 
    return 0; 
} 

ハロー/ HELLO.CPP

#include "hello.h" 

void print_hello() 
{ 
    std::cout << "Hello"; 
} 

世界/ HELLO.CPPここ

#include "hello.h" 

void print_world() 
{ 
    std::cout << "World!!\n"; 
} 

は私のメイクです

#Specify modules to include in the build - corresponds to source code locations. 
MODULES := exec \ 
world \ 
hello 

CXX := g++ 
RM := rm -rf 

#Create a list of the source directories 
SRC_DIR  := $(addprefix ./,$(MODULES)) 

#Create a variable for the build output directory 
BUILD_DIR := $(addprefix ./build/,$(MODULES)) 

#C++ Compiler flags 
CPPFLAGS := -std=c++0x -O0 -g3 -Wall -fmessage-length=0 -c 

#Flags for generating dependency files. 
DEPFLAGS := -MMD -MP -MT "[email protected]" -MF "$$(@:%.o=%.d)" 

#Creates a list of all the source files that we wish to include in the build 
CPP_SRCS := $(foreach sdir, $(SRC_DIR), $(wildcard $(sdir)/*.cpp)) 

#Creates a list of all the object files that we need to build based off source files 
OBJ_TARGET := $(foreach sdir, $(MODULES), $(wildcard $(sdir)/*.cpp)) 
OBJS  := $(patsubst %.cpp, ./build/%.o, $(OBJ_TARGET)) 

#Specify directories to search 
vpath %.cpp $(SRC_DIR) $(BUILD_DIR) 

#"function" that contains the rule to make the .o files that exist within a source director and sub-directory 
define make-goal 
$1/%.o: %.cpp 
    @echo 'Building file: $$<' 
    @echo 'Invoking: Linux G++ Compiler' 
    $(CXX) $(CPPFLAGS) "$$<" -o "[email protected]" $(DEPFLAGS) 
    @echo 'Finished building: $$<' 
    @echo ' ' 
endef 

.PHONY: all checkdirs clean build/HelloWorld 

all: checkdirs build/HelloWorld 

build/HelloWorld: $(OBJS) 
    @echo 'Building Target: [email protected]' 
    @echo 'Invoking: G++ Linker' 
    $(CXX) -L/usr/local/lib $^ -o [email protected] 
    @echo 'Finished building target: [email protected]' 
    @echo ' ' 

clean: 
    -$(RM) $(BUILD_DIR) 

#Makes sure that the output directory exists 
checkdirs: $(BUILD_DIR) 

#Creates the output directory, build, if it doesn't exist 
$(BUILD_DIR): 
    @mkdir -p [email protected] 

#This is the important "recursive" part - this will loop through all source directories and call 'make-goal', which contains the rule to make the associated .o file. 
$(foreach bdir,$(BUILD_DIR),$(eval $(call make-goal,$(bdir)))) 

この小さなプロジェクトを作成しようとすると、次のようになります。注意./world/hello.cppは.oファイルをビルドするときに2回使用されます。最初にbuild/world/hello.oに出力され、2回目に/build/hello/hello.oに出力され、リンカは失敗します。 .oファイルは同じです。 .oファイルを構築するとき./world/hello.cppが2回使用されている理由

Building file: ./exec/main.cpp 
Invoking: Linux G++ Compiler 
g++ -std=c++0x -O0 -g3 -Wall -fmessage-length=0 -c "./exec/main.cpp" -o "build/exec/main.o" -MMD -MP -MT "build/exec/main.o" -MF "build/exec/main.d" 
Finished building: ./exec/main.cpp 

Building file: ./world/hello.cpp 
Invoking: Linux G++ Compiler 
g++ -std=c++0x -O0 -g3 -Wall -fmessage-length=0 -c "./world/hello.cpp" -o "build/world/hello.o" -MMD -MP -MT "build/world/hello.o" -MF "build/world/hello.d" 
Finished building: ./world/hello.cpp 

Building file: ./world/hello.cpp 
Invoking: Linux G++ Compiler 
g++ -std=c++0x -O0 -g3 -Wall -fmessage-length=0 -c "./world/hello.cpp" -o "build/hello/hello.o" -MMD -MP -MT "build/hello/hello.o" -MF "build/hello/hello.d" 
Finished building: ./world/hello.cpp 

Building Target: build/HelloWorld 
Invoking: G++ Linker 

CPP_SRCS: ./exec/main.cpp ./world/hello.cpp ./hello/hello.cpp 

g++ -L/usr/local/lib build/exec/main.o build/world/hello.o build/hello/hello.o -o build/HelloWorld 
build/hello/hello.o: In function `print_world()': 
/home/otter/work/temp/maketest/./world/hello.cpp:4: multiple definition of `print_world()' 
build/world/hello.o:/home/otter/work/temp/maketest/./world/hello.cpp:4: first defined here 
build/exec/main.o: In function `main': 
/home/otter/work/temp/maketest/./exec/main.cpp:7: undefined reference to `print_hello()' 
collect2: error: ld returned 1 exit status 
makefile:46: recipe for target 'build/HelloWorld' failed 
make: *** [build/HelloWorld] Error 1 

だから誰もが知っているのですか? .cppファイルは同じ名前を持っていても、別のディレクトリにあり、makeは同じ名前のファイルが見つかるたびに、最初のファイルを別のファイルとして扱い、最初のファイルを再利用しないほどスマートになると思います。私は時間があればそれ以降の解決しようとするでしょう - -

+0

'$ 1 /%。o:%.cpp' - " $ 1 "部分が間違っています。 –

+1

あなたの 'vpath'ディレクトリは' SRC_DIR'と 'BUILD_DIR'で検索するようにmakeに指示します。ビルドディレクトリをソース 'vpath'ディレクトリにすることは意味がありませんが、問題の根本原因は' vpath'があなたが提供するディレクトリを順番に検索することです。 'world'は' hello'の前に 'モジュール。 – user657267

答えて

1

私はあなたのアプローチが機能しない理由はわからないが、はるかに簡単なアプローチがあります:

build/HelloWorld: $(OBJS) 
    @echo 'Building Target: [email protected]' 
    @echo 'Invoking: G++ Linker' 
    $(CXX) -L/usr/local/lib $^ -o [email protected] 
    @echo 'Finished building target: [email protected]' 
    @echo ' ' 


$(OBJS): build/%.o: %.cpp 
    @echo building [email protected] from $< 
    mkdir -p $(dir [email protected]) 
    $(CXX) $(CPPFLAGS) $< -o [email protected] 

EDIT:あなたのアプローチがうまくいかなかった理由をuser657267が発見しました。

関連する問題