如何为临时目录中生成的obj文件创建makefile目标?



我正在尝试再次学习使用makefiles。我的目录结构是这样的:

build
|- Makefile
|- Project files, like visual studio project
tmp
|- $(os name)_$(compiler)
|- *.o and any other intermediate files
bin
|- $(os name)_$(compiler)
|- the compiled binary executable
|- debug info, if any
src
|- *.cpp, *.h

所以我做了这个Makefile经过一点谷歌搜索和实验:

CXX      := g++
CXXFLAGS := -std=c++17 -Wall
LDFLAGS  := -lpthread
ASSUMED_OS := unknownOS
SRC_DIR := ../src
EXTENSION :=
# OS detection and extension assignment for windows redacted for brevity
# ... detect OS, set extension to .exe if windows
COMPILE_PATH := $(ASSUMED_OS)_gcc
TMP_DIR := ../tmp/$(COMPILE_PATH)
TARGET_DIR := ../bin/$(COMPILE_PATH)
TARGET := $(TARGET_DIR)/MyProject$(EXTENSION)
# $(wildcard *.cpp /xxx/xxx/*.cpp): get all .cpp files from the current directory and dir "/xxx/xxx/"
SRCS := $(wildcard $(SRC_DIR)/*.cpp)
# $(patsubst %.cpp,%.o,$(SRCS)): substitute all ".cpp" file name strings to ".o" file name strings
OBJS := $(patsubst %.cpp,%.o,$(SRCS))
all: $(TARGET)
# Compile all obj files into an executable
$(TARGET): $(OBJS)
@mkdir -p $(@D)
$(CXX) -o $@ $^ $(LDFLAGS)
# Compile all CPP files in separate obj files
%.o: %.cpp
@mkdir -p $(@D)
$(CXX) $(CXXFLAGS) -c $< -o $@
clean:
rm -rf $(TARGET) ../*.o
.PHONY: all clean

这个可以工作,但是它会在源代码旁边创建.o文件。

所以我试图改变OBJS变量并替换路径:

OBJS := $(patsubst %.cpp,%.o,$(SRCS))
# Substitute src path for tmp directory path
OBJS := $(patsubst $(SRC_DIR)/%,$(TMP_DIR)/%,$(OBJS))

但是我得到一个错误:

make: *** No rule to make target '../tmp/linux_gcc/Job.o', needed by '../bin/linux_gcc/MyProject'.  Stop.

所以我尝试创建一个不同的目标,我认为它会将../tmp/linux_gcc中的目标文件名映射到../src中的源文件名:

# $(wildcard *.cpp /xxx/xxx/*.cpp): get all .cpp files from the current directory and dir "/xxx/xxx/"
SRCS := $(wildcard $(SRC_DIR)/*.cpp)
# $(patsubst %.cpp,%.o,$(SRCS)): substitute all ".cpp" file name strings to ".o" file name strings
OBJS := $(patsubst %.cpp,%.o,$(SRCS))
# Substitute src path for tmp directory path
OBJS := $(patsubst $(SRC_DIR)/%,$(TMP_DIR)/%,$(OBJS))
all: $(TARGET)
# Compile all obj files into an executable
$(TARGET): $(OBJS)
@mkdir -p $(@D)
$(CXX) -o $@ $^ $(LDFLAGS)
# Compile all CPP files in separate obj files
$(OBJS): $(SRCS)
@mkdir -p $(@D)
$(CXX) $(CXXFLAGS) -c $< -o $@

但是当我这样做时,make总是为每个.o文件编译相同的cpp文件:

g++ -std=c++17 -Wall -c ../src/Job.cpp -o ../tmp/linux_gcc/Job.o
g++ -std=c++17 -Wall -c ../src/Job.cpp -o ../tmp/linux_gcc/Worker.o
g++ -std=c++17 -Wall -c ../src/Job.cpp -o ../tmp/linux_gcc/WorkManager.o

我想我已经很接近了,但是我怎么才能让它工作呢?我认为在编译命令$(CXX) $(CXXFLAGS) -c $< -o $@中,我需要输入文件的$<以外的东西,但我不确定是什么。

在你的第一个makefile中:

SRC_DIR := ../src
...
SRCS := $(wildcard $(SRC_DIR)/*.cpp)
...
OBJS := $(patsubst %.cpp,%.o,$(SRCS))
...
%.o: %.cpp
@mkdir -p $(@D)
$(CXX) $(CXXFLAGS) -c $< -o $@

是的,这个"在源代码"旁边创建。o文件。如果您有一个源foo.cpp,则变量OBJS将包含../src/foo.o,并且规则将构建它。

在您的第二次尝试中,您已经更改了对象,但您没有更改规则,因此Make知道构建../tmp/linux_gcc/Job.o的唯一方法是从不存在的../tmp/linux_gcc/Job.cpp

第三次尝试时,你的想法是正确的,但你的规则有一个缺陷:

$(OBJS): $(SRCS)
@mkdir -p $(@D)
$(CXX) $(CXXFLAGS) -c $< -o $@

变量$(OBJS)是一个对象列表,$(SRCS)是一个源列表,因此每个对象依赖于所有源。自动变量$<扩展为先决条件列表中的第一项,在本例中是../src/Job.cpp,因此无论您选择哪个对象,Make都将尝试通过编译该源来构建它。

这是一个静态模式规则的作业:

$(OBJS): $(TMP_DIR)/%.o: $(SRC_DIR)/%.cpp
@mkdir -p $(@D)
$(CXX) $(CXXFLAGS) -c $< -o $@

最新更新