我使用GNU makefile构建一个C项目。我想将所有构建工件保留在隔离的构建树上,以便最大限度地减少混乱。该项目看起来像这样:
$prefix/
include/$tree/program.h
source/$tree/program.c
build/
objects/$tree/program.o
dependencies/$tree/program.d
其中$prefix
表示项目目录,$tree
表示任意文件夹结构。
我想将source/
目录中的源文件与build/
树中的对象和依赖文件对应项进行匹配。所以,我写了以下规则:
# Remove the built-in rules
%.o : %.c
$(objects_directory)/%.o : $(source_directory)/%.c $(dependencies_directory)/%.d
$(compiler_command_line) $(compiler_option_output) $@ $<
$(build_directory)/$(target) : $(objects)
$(compiler_command_line) $(compiler_option_output) $@ $^
正确地计算出编译target
以及构建它所需的目标文件。但是,此时使用错误停止:
'build/objects/project/program.o'
所需的目标'build/program.dll'
没有规则。
为什么会发生这种情况,我该如何解决?
我通过运行make --print-data-base
来调查问题,其输出包括:
# Not a target:
build/objects/project/program.o:
# Implicit rule search has been done.
# File does not exist.
# File has not been updated.
这表明先决条件与预期的隐式规则不匹配。但是,当我尝试通过编写以下方式向后工作时,我确认它确实匹配:
object := build/objects/project/program.o
$(object:$(objects_directory)/%.o=$(source_directory)/%.c)
$(object:$(objects_directory)/%.o=%)
这些行会产生source/project/program.c
和project/program
,这意味着正确计算词干。
我研究过GNU make文档,我不记得读过任何暗示这种模式匹配不能在隐式规则定义中发生的事情。
以下是变量定义:
include_directory := include
source_directory := source
build_directory := build
objects_directory := $(build_directory)/objects
dependencies_directory := $(build_directory)/dependencies
sources := $(wildcard $(source_directory)/**/*.c)
objects := $(sources:$(source_directory)/%.c=$(objects_directory)/%.o)
# Take the name of the project's directory
target := $(notdir $(CURDIR)).dll
compiler_dependency_file = $(patsubst $(source_directory)/%.c,$(dependencies_directory)/%.d,$<)
compiler_options = -I $(include_directory) -MMD -MF $(compiler_dependency_file)
CC = gcc
compiler_command_line = $(CC) $(compiler_options) $(CFLAGS)
compiler_option_output = -o
答案 0 :(得分:1)
事实证明,这不是模式匹配。问题的根源在于隐式规则的依赖性先决条件。
依赖文件首先不应该是先决条件;它应该是与目标文件一起生成的目标之一。
当我再次阅读手册的4.14 Generating Prerequisites Automatically时,答案就跳出来了:
sed命令的目的是翻译(例如):
main.o : main.c defs.h
成:
main.o main.d : main.c defs.h
虽然我的构建系统不使用sed
,但main.d
位于示例规则左侧的事实感觉很奇怪。在我的代码中,它位于右侧。
当我把我的规则放在左侧时,它起作用并且问题解决了。错误的配方主要是将其中一种副产品作为先决条件。