Makefile - 仅编译已修改的C ++文件

时间:2018-04-10 22:23:43

标签: makefile gnu-make

这是我当前的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,它会将目录.cpp中的所有sources/个文件编译为.o个文件,然后将这些文件移动到{{} 1}}。然后触发目标obj-tmp/,链接所有$(PROGRAM_NAME)文件并输出可执行文件。

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

抬头:我知道存在类似的问题,但我还没有看到与上述makefile一起使用的解决方案。

非常感谢任何输入。

3 个答案:

答案 0 :(得分:2)

make的重点是仅编译自上次构建以来已修改的文件。 makefile中的问题是您的compile配方将$(SOURCES)变量作为依赖项。如同所有源文件一样。

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

vpath %.cpp src
vpath %.h include 

这将告诉make查找./src中的c ++文件和./include中的头文件。然后,您可以简化单个文件的配方,如下所示:

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

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

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

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

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

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

VPATH = include src src/sub

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

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

我想指出thisthis,这两者都是关于makefile的非凡资源。

答案 1 :(得分:1)

  1. compile的依赖关系更改为目标文件。
  2. 为目标文件添加模式规则。
  3. compile: $(OFILES)
    
    $(OBJDIR)/%.o: sources/%.cpp
        mkdir -p $(OBJDIR)
        $(CC) $(CFLAGS) -c $< -o $@
    

答案 2 :(得分:0)

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

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)

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