我是 C++ 新手,很难理解为什么我的 Makefile 没有达到预期效果。
这是我的项目的(所需的)结构:
MyProject
├── build
│ ├── debug
│ │ ├── main
│ │ └── src
│ │ └── call .o and .d files compiled with debug flag
│ └── release
│ ├── main
│ └── src
│ └── all .o and .d files compiled without additional flags
├── include
│ └── all .hpp files
├── lib
│ └── currently empty
├── src
│ └── all .cpp files
└── Makefile
构建文件夹中的文件夹结构是所需的部分 - 这是不起作用的部分。 我的意图是:每当我调用
make
时,它都会按照 src 文件夹结构将 .o
和 .d
文件构建到 release
文件夹中,并且主要可执行文件直接放在 /release
中。
同样,每当我调用 make debug
时,它都会构建到 debug
文件夹中(并传递附加调试标志)。
ATM 调用
make
会产生以下结构:
MyProject
├── build
│ ├── main
│ └── src
│ └── all .o and .d files compiled without additional flags
...
因此,即使
make
应该是默认目标,release
也不会导致使用 release
文件夹。
调用
make debug
(如果相应替换,则调用 make release
)会产生以下结构:
MyProject
├── build
│ ├── debug
│ │ └── main
│ └── src
│ └── all .o and .d files (not compiled anew! And hence without the debug flags)
...
只有
main
可执行文件位于 debug
文件夹中。其余部分位于 /build
下,如果我之前调用过 make
,则不会再次编译。因此,调试标志丢失,我没有得到调试输出。
所以构建总体上是有效的,文件只是不是我想要的位置,在调试的情况下,我必须每次运行 make clean
才能获得正确的编译和调试输出。
我知道这些块称为配方,并且用制表符缩进的所有内容都会传递到 shell。不过,我认为我不理解 Makefile 的一般执行流程。 这部分
$(BUILD)/%.o: %.cpp
$(MD) $(@D)
$(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -MMD -o $@
似乎是在其他事情之前执行的。我什至看不出它在哪里被称为。尤其是这是失败的部分,因为它没有更新的
BUILD
路径。
这是我的完整 Makefile:
CXX = g++
CXXFLAGS = -std=c++17 -Wall -Wextra -g
debug: CXXFLAGS += -DDEBUG -g
LFLAGS =
SRC := src
BUILD = build
release: BUILD := $(BUILD)/release
debug: BUILD := $(BUILD)/debug
INCLUDE := include
LIB := lib
ifeq ($(OS),Windows_NT)
MAIN := main.exe
SOURCEDIRS := $(SRC)
INCLUDEDIRS := $(INCLUDE)
LIBDIRS := $(LIB)
FIXPATH = $(subst /,\,$1)
RM := del /q /f
MD := mkdir
else
MAIN := main
SOURCEDIRS := $(shell find $(SRC) -type d)
INCLUDEDIRS := $(shell find $(INCLUDE) -type d)
LIBDIRS := $(shell find $(LIB) -type d)
FIXPATH = $1
RM := rm -f
MD := mkdir -p
endif
INCLUDES := $(patsubst %,-I%, $(INCLUDEDIRS:%/=%))
LIBS := $(patsubst %,-L%, $(LIBDIRS:%/=%))
SOURCES := $(wildcard $(patsubst %,%/*.cpp, $(SOURCEDIRS)))
OBJECTS := $(SOURCES:%.cpp=$(BUILD)/%.o)
DEPS := $(OBJECTS:.o=.d)
all: $(OBJECTS)
$(MD) $(BUILD)
$(CXX) $(CXXFLAGS) $(INCLUDES) -o $(BUILD)/$(MAIN) $(OBJECTS) $(LDFLAGS) $(LIBS)
# this seems to be executed whenever calling any "make" command
# this command does not have the updated BUILD path (aka build/release or build/debug)
# Is this a recipe as well? When is it called?
$(BUILD)/%.o: %.cpp
$(MD) $(@D)
$(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -MMD -o $@
-include $(DEPS)
# I would like for release to be called when simply calling "make" (doesn't happen)
.PHONY: default_target
default_target: release
.PHONY: clean
clean:
$(RM) $(call FIXPATH,$(BUILD)/$(MAIN))
$(RM) $(call FIXPATH,$(OBJECTS))
$(RM) $(call FIXPATH,$(DEPS))
@echo Cleanup complete!
.PHONY: debug
debug: all
.PHONY: release
release: all
.PHONY: debugrun
debugrun: all
./$(call FIXPATH,$(BUILD)/$(MAIN))
.PHONY: run
run: all
./$(call FIXPATH,$(BUILD)/$(MAIN))
当然这个Makefile不是我自己想出来的。我从两个模板复制。我的第一个模板将所有
.o
和 .d
文件构建到 src
目录中。 :/ 并查阅了其他各种 stackoverflow 帖子,但无法破解最后一点。这个 Makefile 经历了更糟糕的阶段。
这不能按你想要的方式工作:
release: BUILD := $(BUILD)/release
debug: BUILD := $(BUILD)/debug
$(BUILD)/%.o: %.cpp
从GNU Make手册你会看到:
与自动变量一样,这些值仅在目标配方的上下文中可用(以及其他特定于目标的分配)
这意味着特定于目标的变量不能在规则的目标或先决条件中使用,因为它们仅在配方中设置。