【问题标题】:Makefile: automatically compile source for every included headerMakefile:为每个包含的头文件自动编译源代码
【发布时间】:2018-06-24 06:28:32
【问题描述】:

如果目标包含#include "foo/bar.hpp",我能否以自动编译和链接foo/bar.cpp 的方式配置我的makefile?

详细信息:我有一个结构如下的项目的生成文件:

src/
|-- program1/
|   |-- main.cpp
|   |-- makefile
|-- modules/
|   |-- module1/
|   |   |-- foo.cpp
|   |   |-- foo.hpp
|   |-- module1/
|   |   |-- bar.cpp
|   |   |-- bar.hpp

目前我的 program1 的 makefile 包含它使用的所有模块的所有 *.cpp 文件的列表,这很难维护并且容易出错并与我的包含保持同步。

但是,在我的代码中,遵循#include 命令将提供精确的依赖关系树。对于每个 *.hpp,我需要编译和链接一个对应的 *.cpp。

这个编译过程可以通过 makefile 自动化吗?自动依赖可以帮助我吗?

有问题的makefile:

# compiler settings
CXX = g++
CXXFLAGS = -std=c++14
# object file generation path
tmpDir = .objs
# modules path
modPath = ../modules

# Names of modules and files to be compiled
names := \
    main.o \
    module1/foo.o \
    module2/bar.o

# prepend tmpDir
names := $(addprefix $(tmpDir)/, $(names))

# Linking
main: $(names)
    $(CXX) $(CXXFLAGS) -o main $^

# Rule for main file
$(tmpDir)/main.o: main.cpp
    @mkdir -p $(tmpDir)
    $(CXX) $(CXXFLAGS) -c main.cpp -o $@ -I "$(modPath)"

# rules for module files
$(tmpDir)/%.o: $(modPath)/%.cpp
    mkdir -p $(dir $@)
    $(CXX) $(CXXFLAGS) -c $< -o $@

clean:
    rm -rf *.o main $(tmpDir)

我想避免手动设置names。

【问题讨论】:

  • 这里有哪些模块?只是源代码目录,还是每个模块都编译到特定的库?
  • 啊,它们是纯源代码目录。没有单独的编译意图。此外,它们可能相互包含。
  • 你会考虑使用像 CMake 这样的构建生成工具吗?它具有强大的抽象(函数、宏...),可以帮助您避免为这样的用例重复自己。
  • GCC 可以以Makefile 可以读取的格式输出所有标头依赖项。请参阅:标志-MMD -MP。如果您发布示例 Makefile 我可以尝试发布一个答案,说明它是如何工作的。 (假设你使用GCC)
  • 就我个人而言,每当源文件或头文件发生更改时,我会更关心自动重新编译所有更改的编译单元。完全忘记在 Makefile 中列出 .cpp 文件很快就会通过未解决的符号错误引起您的注意

标签: c++ c makefile automation


【解决方案1】:

自动生成文件名的常用方法是使用$(wildcard ...) 或一些$(shell ...) 命令扫描目录。

根据您链接的Makefile,我认为您可以使用带有-MMD -MP 标志的GCC 跟踪依赖关系,如下所示:

# compiler settings
CXX = g++
# use flags to generate dependency files
CXXFLAGS = -std=c++14 -MMD -MP
# object file generation path
tmpDir = .objs
# modules path
modPath = ../modules

# Names of modules and files to be compiled
names := main.o
names += $(patsubst $(modPath)/%.cpp,%.o,$(shell find $(modPath) -iname "*.cpp"))

# prepend tmpDir
names := $(addprefix $(tmpDir)/, $(names))

# there should be a dep file for every object file
deps  := $(patsubst %.o,%.d,$(names))

all: main

# Linking
main: $(names)
    $(CXX) $(CXXFLAGS) -o main $^

# Rule for main file
$(tmpDir)/main.o: main.cpp
    @mkdir -p $(tmpDir)
    $(CXX) $(CXXFLAGS) -c main.cpp -o $@ -I "$(modPath)"

# rules for module files
$(tmpDir)/%.o: $(modPath)/%.cpp
    mkdir -p $(dir $@)
    $(CXX) $(CXXFLAGS) -c $< -o $@

# include the dependencies if they exist    
-include $(deps)

clean:
    rm -rf *.o main $(tmpDir) $(deps)

每个使用-MMD -MP 标志的编译命令都会生成一个对应于输出文件的依赖文件(.d 扩展名除外)。

【讨论】:

  • 好的,现在,names 的列表是我想要规避的。我可以添加一个规则,为依赖项中的每个 *.hpp 文件编译相应的 *.cpp 文件吗?
  • 关于:$(tmpDir)/%.o: $(modPath)/%.cpp 1) 这缺少 %.d 依赖项。 2)这不会递归到源文件所在的目录。
  • @Simon 我添加了一种使用$(shell ...) 命令生成names 的方法。
  • 关于:mkdir -p $(dir $@) 这将尝试为每个源文件创建相同的目录。这将是一个问题,因为在第一个源文件之后,该目录已经存在。
  • @user3629249 我只是在展示上面 cmets 中链接的 Makefile OP。最好在那里提出改进建议。
【解决方案2】:

关于:*目前我的 program1 的 makefile 包含它使用的所有模块的所有 *.cpp 文件的列表,这很难维护并且容易出错,并与我的包含保持同步。

但是,在我的代码中,遵循 #include 命令将提供精确的依赖关系树。对于每个 *.hpp,我需要编译和链接一个对应的 *.cpp。

这个编译过程可以通过 makefile 自动化吗?自动依赖可以帮助我吗?*

在 makefile 中,您可以使用调用find 命令的输出来获取modules 目录下目录中所有(例如)*.cpp 文件的列表,而不是硬编码列表。

【讨论】:

    【解决方案3】:

    我稍微完善了 Galik 的答案。现在它的行为很像我正在搜索的内容。

    我没有手动定义names,而是调用文件搜索。然后过滤结果以仅匹配具有同名 *.hpp 文件的 *.cpp 文件。这会忽略所有包含测试的文件:

    # find *.hpp files
    names := $(shell find $(modPath) -iname "*.hpp")
    # replace extension .hpp -> .cpp
    names := $(patsubst %,%.cpp,$(basename $(names)))
    # filter nonexistent *.cpp files
    names := $(wildcard $(names))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-08-08
      • 2022-01-04
      • 2016-04-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多