无法正常使用Makefile隐式/模式规则,编译链接报错求助
你的问题根源其实是一个低级拼写错误,加上几个小的规则细节问题,导致Makefile根本没生成目标文件,最终链接阶段找不到main函数。让我一步步拆解:
1. 致命错误:函数名拼写错误
你定义OBJECTS时用了pathsubst,但GNU Make的正确函数名是**patsubst**(pattern substitute的缩写)。这个错误直接导致OBJECTS变量为空——Make找不到任何要编译的.o文件,所以链接命令变成了g++ -lm -o bin/a.out(没有输入的目标文件),自然会报undefined reference to main。
修正这一行:
OBJECTS := $(patsubst src/%.cpp, obj/%.o, $(SOURCES))
2. 次要问题:VPATH路径多余斜杠
你的vpath定义多了个尾部斜杠:
vpath %.cpp $(SRC_DIR)/
改成:
vpath %.cpp $(SRC_DIR)
虽然这个错误不会直接导致失败,但会让Make查找源文件时使用src//*.cpp的路径,不符合规范,容易引发其他潜在问题。
3. 模式规则的小优化
你的make-goal定义里,$1/%.o: %.cpp其实可以简化,因为你已经用vpath指定了源文件目录,而且OBJ_DIR是单个目录(不是多个源目录对应多个obj目录),所以可以直接写模式规则,不用foreach+eval的复杂写法——当然原写法修正后也能工作,但简化后更易读:
替换原有的define make-goal和foreach部分为:
$(OBJ_DIR)/%.o: %.cpp $(CXX) $(INCLUDE) $(CXXFLAGS) -c $< -o $@
修正后的完整Makefile
CXX := g++ LXX := g++ CXXFLAGS := -m64 -std=gnu++14 -fno-enforce-eh-specs -fno-rtti -fpermissive -O3 -funswitch-loops -w CXXDEBUG := -ggdb3 -pg LDFLAGS := -lm EXE := a.out SRC_DIR := src BIN_DIR := bin OBJ_DIR := obj INCLUDE := $(addprefix -I, include) SOURCES := $(foreach sdir, $(SRC_DIR), $(wildcard $(sdir)/*.cpp)) OBJECTS := $(patsubst src/%.cpp, obj/%.o, $(SOURCES)) vpath %.cpp $(SRC_DIR) $(OBJ_DIR)/%.o: %.cpp $(CXX) $(INCLUDE) $(CXXFLAGS) -c $< -o $@ .PHONY: clean all checkdirs default .SECONDARY: default: all all: checkdirs $(BIN_DIR)/$(EXE) $(BIN_DIR)/$(EXE): $(OBJECTS) $(LXX) $(LDFLAGS) $^ -o $@ checkdirs: $(BIN_DIR) $(OBJ_DIR) $(BIN_DIR): @mkdir -p $@ $(OBJ_DIR): @mkdir -p $@ clean: rm -rf $(EXE) $(OBJECTS) $(BIN_DIR) @find . -name "*~" -exec rm {} \; @find . -name "*.o" -exec rm {} \; rm -rf $(OBJ_DIR)
为什么复制的Stack Overflow例子也失败?
你提到复制的示例也报错no input files,大概率是同样的问题:要么是源文件路径和Makefile里的SRC_DIR不匹配,要么是变量替换函数写错(比如和你一样的patsubst拼写错误),或者源文件目录里没有.cpp文件——Make找不到源文件,自然没有目标文件,链接时就会空输入。
验证你的场景
假设你的目录结构是:
├── bin ├── makefile ├── obj └── src └── world.cpp
world.cpp里有标准的main函数,用修正后的Makefile执行make clean all,应该会正常编译src/world.cpp到obj/world.o,然后链接成bin/a.out,完全符合你的预期。
内容的提问来源于stack exchange,提问作者user2717116

