是否可以使用 Makefile "define" 来定义目标及其配方?

Is it possible to use Makefile "define" to define a target plus its recipes?

我有一个包含不同目录的 C/C++ 项目,每个目录包含一组对象可执行文件,可从 C/C++ 源代码构建。

为了启用自动依赖跟踪(每当我的 #include 头文件发生变化时生成 .d 依赖文件),我在一个公共 Makefile 中定义了以下变量:

# automatic prerequisite generation
# source: http://web.archive.org/web/20210820223028/http://make.mad-scientist.net/papers/advanced-auto-dependency-generation/
DEPFLAGS = -MT "$@" -MMD -MP -MF "$(@:.o=.d)"
CC_WRITE_DEP = $(CC) $(CFLAGS) -c "$<" -o "$@" $(DEPFLAGS)
CXX_WRITE_DEP = $(CXX) $(CXXFLAGS) -c "$<" -o "$@" $(DEPFLAGS)

这样当我编写特定于目录的 Makefile 时,我可以这样写:

# common compile options
common := common/Makefile
-include $(common)

# automatic dependency tracking
deps = $(objs:.o=.d)
-include $(deps)

# compile all .cpp source code files into .o object files
%.o: %.cpp
    $(CXX_WRITE_DEP)

# compile all .c source code files into .o object files
%.o: %.c
    $(CC_WRITE_DEP)

其中 objs 是指构建每个可执行文件所需的目标文件。

但我发现我在每个目录中用于构建可执行文件的每个 Makefile 都必须重复上面给出的行块,如果它们很多,这可能会很麻烦。

然后我尝试在通用 Makefile 中编写:

define CC_OBJ_COMPILE =
%.o: %.c
    $(CC_WRITE_DEP)
endef

define CXX_OBJ_COMPILE =
%.o: %.cpp
    $(CXX_WRITE_DEP)
endef

并将它们包含在构建可执行文件中:

common := common/Makefile
-include $(common)

$(CC_OBJ_COMPILE)
$(CXX_OBJ_COMPILE)

但这不起作用。 当我 运行 make -p --dry-run 在一个可执行文件的目录中查看这些变量如何扩展时,我看到了这些行:

# ...
# makefile (from 'common/Makefile', line 16)
define CC_OBJ_COMPILE
%.o: %.c
    $(CC_WRITE_DEP)
endef
# ...
# makefile (from 'common/Makefile', line 21)
define CXX_OBJ_COMPILE
%.o: %.cpp
    $(CXX_WRITE_DEP)
endef
# ...

这意味着文本变量已正确包含到我的可执行文件特定的 Makefile 中。

然而,隐式规则扩展为:

# Implicit Rules

%.o: %.c
 cc -Wall -Werror -c "" -o "" -MT "" -MMD -MP -MF ""

%.o: %.cpp
 g++ -Wall -Werror -c "" -o "" -MT "" -MMD -MP -MF ""

这意味着它们未能包含目标.

的自动$<$@变量

那么是否可以创建可重用的规则,这些规则可以 defined 作为变量并 -included 在其他使用变量引用的 Makefile 中?我在这里错过了什么吗?

Make是一位老爷爷——今年45岁。考虑转向更新的东西——CMake、Scons、Meson 等。这些工具将自动处理依赖关系,将是可移植的,将具有更多功能,并将使您免于无休止(且毫无意义)的重新发明轮子的时间。

Is it possible to use Makefile "define" to define a target plus its recipes?

您必须评估通话。

define CXX_OBJ_COMPILE =
%.o: %.cpp
    $(CXX_WRITE_DEP)
endef

$(eval $(CXX_OBJ_COMPILE))

Which means that they fail to include the automatic $< and $@ variables for targets.

当然可以 - $@ 就像“先展开”,需要留下来进行展开。

CC_WRITE_DEP = $(CC) $(CFLAGS) -c "$$<" -o "$$@" $(DEPFLAGS)

据我所知,您有三个选择。

您可以按照@KamilCuk 的指示使用正确的语法来使用“固定”代码。

或者您可以将规则放在公共文件中,而不是像@thebusybee 所建议的那样将它们分配给变量。

或者您可以完全省略规则,让 Make 使用带有额外标志的默认规则:

CPPFLAGS += $(DEPFLAGS)