我正在尝试再次学习使用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 $@