在不同目录中包含源文件的Makefile


135

我有一个目录结构如下的项目:

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

我应该如何编写一个可以在part / src中(或实际在任何地方)的makefile,该文件可以部分地在c / c ++源文件上完成/链接?/ src?

我可以做-I $ projectroot / part1 / src -I $ projectroot / part1 / inc -I $ projectroot / part2 / src ...

如果那行得通,有没有更简单的方法来做到这一点。我见过在每个相应部分都有一个makefile的项目吗?文件夹。[在这篇文章中,我在bash语法中使用了问号]



1
Phony Targets下的原始gnu手册(gnu.org/software/make/manual/html_node/Phony-Targets.html)中有一个示例recursive invocation,其含义非常优雅。
Frank Nocke 2014年

您使用什么工具来创建该文本图形?
哈立德·侯赛因

Answers:


114

传统的方法是有一个Makefile在每个子目录(中part1part2等),使您能够独立构建它们。此外,Makefile在项目的根目录中有一个用于构建所有内容的目录。“根” Makefile看起来类似于以下内容:

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

由于make目标中的每一行都在其自己的shell中运行,因此不必担心遍历目录树或其他目录。

我建议看看 GNU make手册5.7;这是非常有帮助的。


26
这应该是 +$(MAKE) -C part1等等。这允许Make的作业控制进入子目录。
短暂

26
这是一种经典的方法,已被广泛使用,但是随着项目的发展,它在几种方面都不理想。戴夫·欣顿(Dave Hinton)有待追随的指针。
dmckee ---前主持人小猫

89

如果一个子目录中有代码,而另一个子目录中有代码,则最好在顶层使用单个makefile。

请参阅递归使有害完整的原理,。但是,基本上,您希望使make具有决定是否需要重建文件所需的全部信息,并且如果仅告诉它三分之一的内容,就不会具有该信息。您的项目。

上面的链接似乎无法访问。此处可以访问同一文档:


3
谢谢,没有意识到这一点。了解做事的“正确方法”而不是“正常工作”或被接受为标准方法是非常有用的。
tjklemz13年

3
递归make被认为是有害的,早在它确实不能很好运行的时候。如今,人们认为这不是有害的,实际上,这就是自动工具/自动制作管理大型项目的方式。
埃德温·巴克

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

这将在任何VPATH指定的目录中自动找到匹配的partXapi.cpp文件并进行编译。但是,当您的src目录分为多个子目录时,此功能更为有用。正如您所描述的那样,就像其他人所说的那样,您最好为每个部分使用一个makefile,尤其是如果每个部分可以独立的话。


2
我不敢相信这个简单的完美答案还没有得到更多的选票。来自我的+1。
尼古拉斯·汉密尔顿2014年

2
我在较高的目录中有一些通用的源文件,可用于子文件夹中的多个不同项目,VPATH=..对我有用!
EkriirkE 16/12/2

23

您可以将规则添加到根Makefile中,以便在其他目录中编译必需的cpp文件。下面的Makefile示例应该是使您到达想要的位置的良好起点。

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

源= cppTest.cpp
源= $(OTHERDIR)/file.cpp

##最终来源定义
INCLUDE = -I./ $(AN_INCLUDE_DIR)  
包含= -I。$(OTHERDIR)/../ inc
##结束更多包括

VPATH = $(OTHERDIR)
OBJ = $(加入$ {addsuffix ../obj/,$ {dir $ {SOURCE))),$ {notdir $ {SOURCE:.cpp = .o)))) 

##将依赖目标固定为相对于src目录的../.dep
DEPENDS = $(加入$(addsuffix ../.dep/,$(dir $(SOURCE))),$(notdir $(SOURCE:.cpp = .d)))

##执行默认规则
全部:$(TARGET)
        @真正

##清洁规则
清洁:
        @ -rm -f $(目标)$(目标)$(取决于)


##制定实际目标的规则
$(目标):$(目标)
        @echo“ ==============”
        @echo“链接目标$ @”
        @echo“ ==============”
        @ $(CC)$(CFLAGS)-o $ @ $ ^ $(LIBS)
        @echo-链接完成-

##通用编译规则
%.o:%。cpp
        @mkdir -p $(目录$ @)
        @echo“ ==============”
        @echo“正在编译$ <”
        @ $(CC)$(CFLAGS)-c $ <-o $ @


## cpp文件中目标文件的规则
##每个文件的目标文件放在obj目录中
##从实际源目录上移一级。
../obj/%.o:%.cpp
        @mkdir -p $(目录$ @)
        @echo“ ==============”
        @echo“正在编译$ <”
        @ $(CC)$(CFLAGS)-c $ <-o $ @

#“其他目录”的规则每个“其他”目录都需要一个
$(OTHERDIR)/../ obj /%。o:%.cpp
        @mkdir -p $(目录$ @)
        @echo“ ==============”
        @echo“正在编译$ <”
        @ $(CC)$(CFLAGS)-c $ <-o $ @

##制定依赖规则
../.dep/%.d:%.cpp
        @mkdir -p $(目录$ @)
        @echo“ ==============”
        @echo为$ *。o建立依赖文件
        @ $(SHELL)-ec'$(CC)-M $(CFLAGS)$ <| sed“ s ^ $ *。o ^ .. / obj / $ *。o ^”> $ @'

##“其他”目录的依赖性规则
$(OTHERDIR)/../。dep /%。d:%.cpp
        @mkdir -p $(目录$ @)
        @echo“ ==============”
        @echo为$ *。o建立依赖文件
        @ $(SHELL)-ec'$(CC)-M $(CFLAGS)$ <| sed“ s ^ $ *。o ^ $(OTHERDIR)/../ obj / $ *。o ^”> $ @'

##包括依赖文件
-包括$(DEPENDS)


7
我知道现在这已经很老了,但是SOURCE和INCLUDE都不会在以后一行被另一个作业覆盖吗?
skelliam 2015年

@skelliam是的,确实如此。
nabroyan

1
这种方法违反了DRY原理。复制每个“其他目录”的代码是一个坏主意
Jesus 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 2012年

6

我认为最好指出,使用Make(无论是否递归)通常是您可能要避免的事情,因为与当今的工具相比,它很难学习,维护和扩展。

这是一个很棒的工具,但是直接使用应该在2010年以后被淘汰。

当然,除非您在特殊的环境中工作,即与遗留项目等一起工作。

使用IDE,CMake或使用Autotools(如果需要硬核)。

(由于拒绝投票而被编辑,ty Honza指出)


1
最好请大家解释一下。我曾经也自己制作过Makefile。
IlDan

2
我赞成“不要这样做”的建议-KDevelop具有非常图形化的界面来配置Make和其他构建系统,尽管我自己编写Makefile,但KDevelop是我给同事的东西-但是我认为Autotools链接没有帮助。要了解Autotools,您需要了解m4,libtool,automake,autoconf,shell,make以及基本上整个堆栈。
短暂

7
拒绝投票可能是因为您正在回答自己的问题。问题不在于是否应该使用Makefile。这是关于如何写它们。
Honza 2012年

8
如果您能用几句话解释一下现代工具如何使编写Makefile变得更轻松,那将是很好的。它们是否提供更高级别的抽象?或者是什么?
Abhishek Anand 2014年

1
xterm,vi,bash,makefile ==统治世界,cmake适用于无法破解代码的人。
μολὼν.λαβέ

2

RC的帖子非常有用。我从没考虑过使用$(dir $ @)函数,但是它确实满足了我的需要。

在parentDir中,有一堆目录中包含源文件:dirA,dirB,dirC。各种文件都依赖于其他目录中的目标文件,因此我希望能够从一个目录中创建一个文件,并通过调用与该依赖项关联的makefile使其具有该依赖项。

本质上,我在parentDir中创建了一个Makefile,该文件具有(除其他事项外)类似于RC的通用规则:

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

每个子目录都包含此上级makefile,以便继承此通用规则。在每个子目录的Makefile中,我为每个文件编写了一个自定义规则,以便可以跟踪每个文件所依赖的所有内容。

每当需要创建文件时,我(基本上)都使用此规则来递归创建任何/所有依赖项。完善!

注意:有一个名为“ makepp”的实用程序似乎可以更直观地完成此任务,但是出于可移植性的考虑,并且不依赖于其他工具,我选择采用这种方式。

希望这可以帮助!


2

递归使用Make

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

这允许make拆分为作业并使用多个内核


2
这比做类似的事情make -j4好吗?
devin

1
如我所见,@ devin并不是更好,它只允许 make使用作业控制。当运行单独的制造流程时,它不受工作控制。
Michael Pankov

1

我一直在寻找类似的东西,经过一番尝试之后,我创建了自己的makefile,我知道这不是“惯用的方式”,但这是一种开始理解make的方法,并且对我来说这是可行的,也许您可​​以在您的项目中尝试一下。

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,它将我的项目编译成多个目录,然后将所有这些链接在一起以创建我的bin。

我接受建议:D


-1

我建议使用autotools

//## 将生成的目标文件(.o)与源文件放在同一目录中,以避免在使用非递归make时发生冲突。

AUTOMAKE_OPTIONS = subdir-objects

只是将其Makefile.am与其他非常简单的内容包括在内。

这是本教程

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.