Makefile中的模式目标无法识别?

时间:2017-06-01 17:03:39

标签: makefile

我不明白为什么下面的Makefile无法识别模式目标%.o: %.cpp。第42行应该在第58行调用规则。

目的是创建一个Makefile,用相同的代码生成不同的可执行文件。

此外,如果更改了头文件(依赖项),Makefile将(将来)考虑重建代码。但目前这不是问题。

.SECONDEXPANSION:

SUFFIXES += .d

MD := mkdir
MD_FLAGS := -p

RM := rm
RM_FLAGS := -rf

EC := echo

SRC_DIR := src
# SRC_FILES - target specific

OBJ_DIR := obj
# OBJ_FILES - target specific

DEP_DIR := dep
# DEP_FILES - target specific

BUILD_DIR := build

CC := clang++
CC_FLAGS := -c -std=c++11 -I $(SRC_DIR) -O3

LD := clang++
# LD_FLAGS - target specific

.PHONY: all
all: first

.PHONY: first
first: LD_FLAGS :=

first: SRC_FILES :=\
        $(SRC_DIR)/executables/first.cpp\
        $(SRC_DIR)/factory/milk.cpp

first: obj

first: $(OBJ_FILES)
        @echo $(OBJ_FILES)
        @echo making $@

first: dep

first: lnk

.PHONY: obj
obj:
        $(eval OBJ_FILES := $(patsubst $(SRC_DIR)/%.cpp, $(OBJ_DIR)/%.o, $(SRC_FILES)))

.PHONY: dep
dep:
        $(eval DEP_FILES := $(patsubst $(SRC_DIR)/%.cpp, $(DEP_DIR)/%.dep, $(SRC_FILES)))

%.o: %.cpp
        @$(EC) "Begin compiling "$@
        @$(MD) $(MD_FLAGS) $(dir $@)
        @$(CC) $(CC_FLAGS) $< -o $@
        @$(EC) "Done compiling "$@

.PHONY: lnk
lnk: $(DEP_FILES) $(OBJ_FILES) $(SRC_FILES)
        @$(EC) $(SRC_FILES)
        @$(EC) $(OBJ_FILES)
        @$(EC) $(DEP_FILES)
        @$(EC) "Begin linking "$@
        @$(MD) $(MD_FLAGS) $(DEP_DIR) $(BUILD_DIR)
        @$(LD) -o $(BUILD_DIR)/$@ $(OBJ_FILES) $(LD_FLAGS)
        @$(EC) "Done linking "$@

.PHONY: clean
clean:
        @$(EC) "Begin cleaning"
        @$(RM) $(RM_FLAGS) $(DEP_DIR) $(OBJ_DIR) $(BUILD_DIR)
        @$(EC) "Done cleaning"

调试输出的片段(make -d)对我来说不是一个很大的帮助,但是为了完成......

Updating goal targets....
Considering target file 'all'.
 File 'all' does not exist.
  Considering target file 'first'.
   File 'first' does not exist.
    Considering target file 'obj'.
     File 'obj' does not exist.
     Finished prerequisites of target file 'obj'.
    Must remake target 'obj'.
    Successfully remade target file 'obj'.
    Considering target file 'dep'.
     File 'dep' does not exist.
     Finished prerequisites of target file 'dep'.
    Must remake target 'dep'.
    Successfully remade target file 'dep'.
    Considering target file 'lnk'.
     File 'lnk' does not exist.
     Finished prerequisites of target file 'lnk'.
    Must remake target 'lnk'.

更新1

我已更新代码,因为它现在使用MAKECMDGOALS从biger集的子集构建多个可执行文件(例如firstsecond)。

没有按预期工作,但目标全部被打破。我很清楚为什么,但我现在没有解决方案。

.SECONDEXPANSION:

SUFFIXES += .d

MD := mkdir
MD_FLAGS := -p

RM := rm
RM_FLAGS := -rf

EC := echo

SRC_DIR := src

ifeq ($(filter first, $(MAKECMDGOALS)), first)
        SRC_FILES :=\
        $(SRC_DIR)/executables/first.cpp\
        $(SRC_DIR)/factory/milk.cpp
endif

ifeq ($(filter second, $(MAKECMDGOALS)), second)
        SRC_FILES :=\
        $(SRC_DIR)/executables/second.cpp\
        $(SRC_DIR)/factory/milk.cpp\
        $(SRC_DIR)/factory/cream.cpp
endif

OBJ_DIR := obj

OBJ_FILES := $(patsubst $(SRC_DIR)/%.cpp, $(OBJ_DIR)/%.o, $(SRC_FILES))

DEP_DIR := dep

DEP_FILES := $(patsubst $(SRC_DIR)/%.cpp, $(DEP_DIR)/%.dep, $(SRC_FILES))

BUILD_DIR := build

CC := clang++

CC_FLAGS := -c -std=c++11 -I $(SRC_DIR) -O3

LD := clang++

ifeq ($(filter first, $(MAKECMDGOALS)), first)
        LD_FLAGS :=
endif

ifeq ($(filter second, $(MAKECMDGOALS)), second)
        LD_FLAGS :=
endif

.PHONY: all
all: first second

.PHONY: first
first: $(OBJ_FILES) lnk

.PHONY: second
second: $(OBJ_FILES) lnk

%.o: %.cpp
        @$(EC) "Begin compiling "$@
        @$(MD) $(MD_FLAGS) $(dir $@)
        @$(CC) $(CC_FLAGS) $< -o $@
        @$(EC) "Done compiling "$@

.PHONY: lnk
lnk: $(OBJ_FILES)
        @$(EC) "Begin linking "$@
        @$(MD) $(MD_FLAGS) $(DEP_DIR) $(BUILD_DIR)
        @$(LD) -o $(BUILD_DIR)/$@ $(OBJ_FILES) $(LD_FLAGS)
        @$(EC) "Done linking "$@

.PHONY: clean
clean:
        @$(EC) "Begin cleaning"
        @$(RM) $(RM_FLAGS) $(DEP_DIR) $(OBJ_DIR) $(BUILD_DIR)
        @$(EC) "Done cleaning"

更新2

我必须更改目标firstsecond,这是一个复制粘贴错误。这是工作示例:

MD := mkdir
MD_FLAGS := -p

RM := rm
RM_FLAGS := -rf

EC := echo

SRC_DIR := src

ifeq ($(filter first, $(MAKECMDGOALS)), first)
        SRC_FILES :=\
        $(SRC_DIR)/executables/first.cpp\
        $(SRC_DIR)/factories/milk.cpp
endif

ifeq ($(filter second, $(MAKECMDGOALS)), second)
        SRC_FILES :=\
        $(SRC_DIR)/executables/second.cpp\
        $(SRC_DIR)/factories/milk.cpp\
        $(SRC_DIR)/factories/cream.cpp
endif

OBJ_DIR := obj

OBJ_FILES := $(patsubst $(SRC_DIR)/%.cpp, $(OBJ_DIR)/%.o, $(SRC_FILES))

DEP_DIR := dep

DEP_FILES := $(patsubst $(SRC_DIR)/%.cpp, $(DEP_DIR)/%.dep, $(SRC_FILES))

BUILD_DIR := build

CC := clang++

CC_FLAGS := -c -std=c++11 -I $(SRC_DIR) -O3

LD := clang++

ifeq ($(filter first, $(MAKECMDGOALS)), first)
        LD_FLAGS :=
        LD_FILE := first
endif

ifeq ($(filter second, $(MAKECMDGOALS)), second)
        LD_FLAGS :=
        LD_FILE := second
endif

.PHONY: all
all:
        @$(MAKE) $(MAKEFLAGS) first
        @$(MAKE) $(MAKEFLAGS) second

.PHONY: first
first: $(OBJ_FILES) lnk

.PHONY: second
second: $(OBJ_FILES) lnk

$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp
        @$(EC) "Begin compiling "$@
        @$(MD) $(MD_FLAGS) $(dir $@)
        @$(CC) $(CC_FLAGS) $< -o $@
        @$(EC) "Done compiling "$@

.PHONY: lnk
lnk: $(OBJ_FILES)
        @$(EC) "Begin linking "$(LD_FILE)
        @$(MD) $(MD_FLAGS) $(DEP_DIR) $(BUILD_DIR)
        @$(LD) -o $(BUILD_DIR)/$(LD_FILE) $(OBJ_FILES) $(LD_FLAGS)
        @$(EC) "Done linking "$(LD_FILE)

.PHONY: clean
clean:
        @$(EC) "Begin cleaning"
        @$(RM) $(RM_FLAGS) $(DEP_DIR) $(OBJ_DIR) $(BUILD_DIR)
        @$(EC) "Done cleaning"

1 个答案:

答案 0 :(得分:2)

您必须更清楚地了解变量扩展时的规则。有关完整详细信息,请参阅GNU make手册部分How Make Reads a Makefile

在那里,您将发现当解析makefile时,在规则定义的目标或先决条件部分中出现的变量和函数会立即扩展。扩展显示在规则的配方中的变量和函数 deferred ,直到调用该配方为止。

让我们看一下makefile的有趣部分:

all: first
first: obj
first: $(OBJ_FILES)
        @echo $(OBJ_FILES)
        @echo making $@
obj:
        $(eval OBJ_FILES := $(patsubst $(SRC_DIR)/%.cpp, $(OBJ_DIR)/%.o, $(SRC_FILES)))

这里发生了什么? Make读取第一行并知道first是默认目标的先决条件(因为它是makefile中的第一个目标)all。然后,它会发现first取决于目标obj。然后它看到first取决于变量$(OBJ_FILES)的值,但此时OBJ_FILES未设置,因此它会扩展为空字符串并且实际上并不依赖于任何目标文件。

现在当make完成解析并希望运行构建时,它会看到第一个目标是all,这取决于first,这取决于obj,而不是#{1}} 39; t依赖于任何东西,所以它运行配方来构建obj目标。这使用eval来设置make变量OBJ_FILES,所以现在它有一个值,但它已经太晚了,无法帮助first目标的先决条件列表,这已经是计算。所以现在make运行first的配方,打印OBJ_FILES的值。

此处还有其他各种问题,例如构建非lnk目标的规则(例如$@)。

但是,基本上你需要重新考虑在配方中设置变量的方法。这仅在罕见/模糊的情况下有用。我不清楚为什么你在eval的配方中这样做,而不是直接分配OBJ_FILES,所以我不能具体告诉你如何重写你的makefile

如果要查看为您解析的规则集,可以使用-p标志运行make,它将打印出用于构建项目的规则数据库。< / p>