【问题标题】:Makefile -- compile only modified C++ filesMakefile -- 只编译修改过的 C++ 文件
【发布时间】:2018-04-10 22:23:42
【问题描述】:

这是我当前的makefile

CFLAGS = -Iheaders/
CC = g++

PROGRAM_NAME = sportsmanager
rwildcard    = $(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2))
SOURCES      = $(call rwildcard,sources/,*.cpp)
OFILES       = $(call rwildcard,obj-tmp/,*.o)
OBJDIR       = obj-tmp/

compileAndRun: 
    make -s compile && make -s $(PROGRAM_NAME)
    ./$(PROGRAM_NAME)

compile: $(SOURCES)
    mkdir -p $(OBJDIR)
    $(CC) $(CFLAGS) -c $(SOURCES) && mv *.o $(OBJDIR)

$(PROGRAM_NAME): $(OFILES)
    $(CC) $(CFLAGS) $(OFILES) -o $(PROGRAM_NAME)

每当我运行$ make 时,都会触发目标compile,它将目录sources/ 中的所有.cpp 文件编译为.o 文件,然后将这些文件移动到obj-tmp/。然后触发目标$(PROGRAM_NAME),链接所有.o文件,输出可执行文件。

问题是每次我运行make 时都会编译所有 文件。如果我连续两次运行“make”,理想情况下应该发生的是,make 应该知道该程序第二次是最新的。如果我只修改一个文件,则只应编译该文件。

请注意:我知道存在与此相关的类似问题,但我还没有看到与上述 makefile 结合使用的解决方案。

非常感谢任何输入。

【问题讨论】:

  • 你的命令明确告诉它编译每个文件——这是不可能的。您需要告诉 make 如何 编译单个文件(使用 % 规则),设置依赖关系,它会神奇地做正确的事情。
  • 不要复制 .o 文件 - 将它们创建在您要放置它们的目录中。这是一个非常糟糕的 makefile。
  • 不要垃圾标签。并阅读make 的文档。很全面。

标签: makefile gnu-make


【解决方案1】:

make 的全部意义在于只编译那些自上次构建以来修改过的文件。您的 makefile 中的问题是您的 compile 配方将 $(SOURCES) 变量作为依赖项。如,所有源文件。

我会使用vpath 来组织项目文件夹,如下所示:

vpath %.cpp src
vpath %.h include 

这将告诉make./src 中查找c++ 文件并在./include 中查找头文件。然后,您可以像这样简化 individual 文件的配方:

%.o: %.cpp
    $(CC) $(CFLAGS) -c -o $@ $<

完成此操作后,您现在可以使用与 .o 文件匹配的通配符定义 $(OBJECTS) 变量并从那里继续。顺便说一句,将目标文件移动到单独的文件夹中被认为是不好的做法,我同意;它确实没有增加任何实质性的价值,但会使食谱复杂化。

请记住,目标文件代表$(PROGRAM) 配方的依赖项。所以很自然地,make 会寻找必要的目标文件来查看它们是否需要重建。如果它们已被移动,则会发生以下两种情况之一。 make 将确定它们不存在并从头开始重新重建 所有 目标文件,从而使我们首先使用 make 的原因无效,或者您必须定义一个文件夹目标文件所在的位置,以及每次处理通配符、搜索等(实际上是与目标文件有关的任何事情)时,您都必须考虑到这种增加的复杂性。

我同意在项目文件夹中有大量目标文件可能有点烦人,但这绝对比永远等待项目编译好。只需记住将*.o 添加到您的.gitignore 或您使用的任何源代码控制平台,它们只不过是令人眼花缭乱的,而make 会更容易使用。


要回答您有关处理源文件夹中子目录的问题,答案要复杂一些。

您可以直接使用VPATH 变量,而不是像上面那样使用特定的vpath &lt;pattern&gt; &lt;folder&gt; 指令:

VPATH = include src src/sub

这将处理这项工作,但通常首选第一种方法,因为当使用VPATH 时,在查找文件时每次搜索每个目录,而不是受文件的位置限制扩展名。

虽然可以使用 make 来方便地管理大型项目,但它涉及递归调用 make 本身,为构建过程中的每个模块编写 makefile。这个过程显然要复杂得多,我强烈建议考虑项目是否真的需要这样做,因为由于实施过程中涉及的复杂性,构建过程模块化的任何潜在收益都可能无法恢复。

我想向您指出thisthis,它们都是关于makefile 的非凡资源。

【讨论】:

  • 好的。谢谢。为什么移动 .o 文件被认为是一种不好的做法?当你在一个大目录中工作时,你的文件树的根目录中充斥着你从未接触过的文件,这是非常令人震惊的。
  • 我在解释中添加了,如果有帮助请告诉我
  • 我明白了,这很有帮助。最后一个问题,你能递归地应用'vpath'吗?例如考虑到 src/sub/file.cpp?
  • 感谢您提供的所有精彩答案和资源!
  • 我不认为这是个愚蠢的问题。不客气
【解决方案2】:
  1. compile的依赖改为目标文件。
  2. 为对象文件添加模式规则。

compile: $(OFILES)

$(OBJDIR)/%.o: sources/%.cpp
    mkdir -p $(OBJDIR)
    $(CC) $(CFLAGS) -c $< -o $@

【讨论】:

    【解决方案3】:

    好的,这个线程中有很多好的输入!这是一个跟进。我现在已将脚本更新为以下内容:

    CC           = g++
    CFLAGS       = -Iheaders/
    PROGRAM_NAME = sportsmanager
    OFILES       = $(patsubst %.cpp,%.o,$(wildcard sources/*.cpp))
    
    vpath %.cpp sources
    
    compileAndRun:
        @make -s $(PROGRAM_NAME)
        @./$(PROGRAM_NAME)
    
    $(PROGRAM_NAME): $(OFILES)
        $(CC) $(CFLAGS) -o $(PROGRAM_NAME) $(OFILES)
    
    %.o: %.cpp
        $(CC) $(CFLAGS) -c -o $@ $<
    
    clean:
        rm -rf $(PROGRAM_NAME) $(OFILES)
    

    非常欢迎任何进一步改进的建议!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-04-04
      • 1970-01-01
      • 1970-01-01
      • 2012-07-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多