【问题标题】:Makefiles with source files in different directories具有不同目录中的源文件的 Makefile
【发布时间】:2010-11-11 11:24:50
【问题描述】:

我有一个项目,目录结构是这样的:

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

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

我可以做类似的事情吗 -I$projectroot/part1/src -I$projectroot/part1/inc -I$projectroot/part2/src ...

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

【问题讨论】:

标签: linux makefile


【解决方案1】:

传统方式是在每个子目录(part1part2 等)中都有一个Makefile,以便您独立构建它们。此外,在构建所有内容的项目的根目录中有一个Makefile。 “根”Makefile 如下所示:

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

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

我建议看看GNU make manual section 5.7;很有帮助。

【讨论】:

  • 这应该是 +$(MAKE) -C part1 等等。这允许 Make 的作业控制进入子目录。
  • 这是一种经典的方法并且被广泛使用,但它在几个方面都不是最理想的,随着项目的发展会变得更糟。 Dave Hinton 有指针跟随。
【解决方案2】:

如果您的一个子目录中的代码依赖于另一个子目录中的代码,那么您最好在顶层使用一个 makefile。

请参阅Recursive Make Considered Harmful 了解完整原理,但基本上您希望 make 拥有决定是否需要重建文件所需的全部信息,如果您只告诉它一个您项目的三分之一。

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

【讨论】:

  • 谢谢,不知道这个。了解做事的“正确方式”而不是“正常工作”或被接受为标准的方式非常有用。
  • 递归 make 被认为是有害的,当它真的不能很好地工作时。现在人们认为它没有害处,事实上,这是 autotools / automake 管理大型项目的方式。
  • 截至 2020 年 9 月,所有这些链接似乎都无法访问。你可以在谷歌上找到它
【解决方案3】:

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 目录被分解为子目录时,这会更有用。正如其他人所说,对于您的描述,您可能最好为每个部分使用一个生成文件,特别是如果每​​个部分都可以独立。

【讨论】:

  • 我不敢相信这个简单完美的答案没有得到更多的选票。这是我的 +1。
  • 我在子文件夹中的几个不同项目的较高目录中有一些通用源文件,VPATH=.. 为我工作!
【解决方案4】:

您可以在根 Makefile 中添加规则,以便在其他目录中编译必要的 cpp 文件。下面的 Makefile 示例应该是一个良好的开端,可以帮助您到达您想去的地方。

抄送=g++ 目标=cppTest OTHERDIR=../../someotherpath/in/project/src 源 = cppTest.cpp 源 = $(OTHERDIR)/file.cpp ## 结束源定义 包含 = -I./ $(AN_INCLUDE_DIR) 包括 = -I.$(OTHERDIR)/../inc ##结束更多包括 VPATH=$(其他目录) OBJ=$(join $(addsuffix ../obj/, $(dir $(SOURCE))), $(notdir $(SOURCE:.cpp=.o))) ## 将依赖目标固定为 ../.dep 相对于 src 目录 DEPENDS=$(join $(addsuffix ../.dep/, $(dir $(SOURCE))), $(notdir $(SOURCE:.cpp=.d))) ## 执行默认规则 全部:$(目标) @真的 ## 清洁规则 干净的: @-rm -f $(TARGET) $(OBJ) $(DEPENDS) ## 制作实际目标的规则 $(目标):$(目标) @echo "============= @echo "链接目标 $@" @echo "============= @$(CC) $(CFLAGS) -o $@ $^ $(LIBS) @echo -- 链接完成 -- ## 通用编译规则 %.o : %.cpp @mkdir -p $(dir $@) @echo "============= @echo "编译 $ $@' ## “其他”目录的依赖规则 $(OTHERDIR)/../.dep/%.d: %.cpp @mkdir -p $(dir $@) @echo "============= @echo 为 $*.o 构建依赖文件 @$(SHELL) -ec '$(CC) -M $(CFLAGS) $ $@' ## 包含依赖文件 -包括$(取决于)

【讨论】:

  • 我知道这已经很老了,但是,SOURCE 和 INCLUDE 不是都被另一行之后的另一个赋值覆盖了吗?
  • @skelliam 是的,确实如此。
  • 这种做法违反了 DRY 原则。为每个“其他目录”复制代码是个坏主意
【解决方案5】:

如果源代码分布在许多文件夹中,并且按照前面的建议拥有单独的 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)))

【讨论】:

  • 这对于您只想添加一些自定义库并将它们放在单独的文件夹中的小型项目非常有用。非常感谢
【解决方案6】:

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

这是一个很棒的工具,但它的直接使用在 2010 年应该被认为已经过时了。

当然,除非您在特殊环境中工作,即处理遗留项目等。

使用 IDE,CMake,或者,如果您是硬核,则使用 Autotools

(因反对票而编辑,ty Honza 指出)

【讨论】:

  • 很高兴能解释反对意见。我以前也自己制作 Makefile。
  • 我赞成“不要那样做”的建议——KDevelop 有一个非常图形化的界面来配置 Make 和其他构建系统,虽然我自己编写 Makefiles,但 KDevelop 是我的给我的同事——但我认为 Autotools 链接没有帮助。要了解 Autotools,您需要了解 m4、libtool、automake、autoconf、shell、make 以及基本上整个堆栈。
  • 反对票可能是因为您正在回答自己的问题。问题不在于是否应该使用 Makefile。这是关于如何编写它们。
  • 如果你能用几句话解释一下现代工具如何让生活变得比编写 Makefile 更轻松,那就太好了。它们是否提供更高级别的抽象?还是什么?
  • xterm、vi、bash、makefile== 统治世界,cmake 是为不会破解代码的人准备的。
【解决方案7】:

我一直在寻找类似的东西,经过一些尝试和失败后,我创建了自己的 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!

我知道这很简单,对于某些人来说,我的标志是错误的,但正如我所说,这是我第一个在多个目录中编译我的项目并将所有然后链接在一起以创建我的 bin 的 Makefile。

我接受建议 :D

【讨论】:

    【解决方案8】:

    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”的实用程序似乎更直观地完成了这项任务,但为了便携性而不依赖于其他工具,我选择了这种方式。

    希望这会有所帮助!

    【讨论】:

      【解决方案9】:

      Recursive Use of Make

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

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

      【讨论】:

      • 这比make -j4 之类的操作更好吗?
      • @devin 在我看来,它并不是更好,它只是使 make 完全可以使用作业控制。单独运行make时,不受作业控制。
      【解决方案10】:

      我建议使用autotools:

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

      AUTOMAKE_OPTIONS = subdir-objects
      

      只需将它与其他非常简单的东西一起包含在 Makefile.am 中即可。

      这里是tutorial

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-05
        • 1970-01-01
        相关资源
        最近更新 更多