Makefile链接步骤无法正确链接全部obj文件的LNK1181报错解决
Make递归构建子项目时MSVC链接器路径识别错误问题
目录结构
- root
- bin
- include
- project1 ... projectN
- Makefile
- root/bin
- project1 ... projectN
- bin
- obj
- project1 ... projectN
- 单项目(以Project1为例)结构
- include
- src
- Makefile
构建逻辑说明
同路径下存放多个子项目,通过递归调用make构建每个项目:首先收集指定项目的所有源文件与头文件完成编译,生成的obj文件存储在root/bin/project/obj路径下,再将所有obj文件链接生成可执行文件,存储到root/bin/project/bin目录。
问题表现
使用现有Makefile配置时,链接器(MSVC link.exe)仅能识别obj目录下的第一个obj文件,其余obj文件会被默认定位到Makefile所在目录,触发报错。
原有错误配置
# expands to root/bin/projname/bin TARGET_DIR := $(BINARY_DIR)/$(notdir $(patsubst %/,%,$(CURDIR)))/bin #expands to root/bin/projname/obj OBJ_DIR := $(BINARY_DIR)/$(notdir $(patsubst %/,%,$(CURDIR)))/obj # expands to a list of all the obj files created from the source files (a.obj, b.obj..) OBJECTS := \ $(notdir $(patsubst %.cpp,%.obj,$(patsubst %.c,%.obj,$(SOURCES)))) SOURCES := $(wildcard src/*.c) $(wildcard src/*.cpp) # default target all: dirs $(TARGET).exe # Link $(TARGET).exe: $(OBJ_DIR)/$(OBJECTS) $(LNK) $(LIB_PATH) $(LIBS) /OUT:$(TARGET_DIR)/$@ $(OBJ_DIR)/$(OBJECTS) # Compile $(OBJ_DIR)/$(OBJECTS): $(SOURCES) $(CC) /c $(CFLAGS) $(INCLUDES) /Fo$(OBJ_DIR)/ /EHsc $(SOURCES) .PHONY: build dirs: $(MKDIR) $(TARGET_DIR) $(MKDIR) $(OBJ_DIR) $(MKDIR) $(TEST_DIR)
报错信息
链接阶段执行的命令如下:
"C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\Tools\\..\..\VC\vcvarsall.bat" x86_amd64 > NUL && link /NOLOGO /MACHINE:X64 /LIBPATH:"C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Lib\x64" /DEFAULTLIB:shell32.lib /OUT:../bin/project1/bin/project1.exe ../bin/project1/obj/main.obj sort.obj
可见仅第一个obj文件拼接了正确的obj目录路径,第二个obj文件sort.obj被当作当前Makefile目录下的文件处理,最终抛出错误:LINK : fatal error LNK1181: cannot open input file 'sort.obj'
问题根因
Make的原生变量展开逻辑中,$(OBJ_DIR)/$(OBJECTS)只会给OBJECTS列表的第一个元素添加OBJ_DIR前缀,后续元素不会自动拼接路径,导致链接器在当前工作目录查找后续obj文件失败。
修复方案
使用Make内置的addprefix函数给所有obj文件统一添加路径前缀,同时链接命令使用自动变量$^代替手写的obj列表,避免重复逻辑出错,修复后配置如下:
# expands to root/bin/projname/bin TARGET_DIR := $(BINARY_DIR)/$(notdir $(patsubst %/,%,$(CURDIR)))/bin #expands to root/bin/projname/obj OBJ_DIR := $(BINARY_DIR)/$(notdir $(patsubst %/,%,$(CURDIR)))/obj # expands to a list of all the obj files created from the source files (a.obj, b.obj..) OBJECTS := \ $(notdir $(patsubst %.cpp,%.obj,$(patsubst %.c,%.obj,$(SOURCES)))) SOURCES := $(wildcard src/*.c) $(wildcard src/*.cpp) # default target all: dirs $(TARGET).exe # Link $(TARGET).exe: $(addprefix $(OBJ_DIR)/,$(OBJECTS)) $(LNK) $(LIB_PATH) $(LIBS) /OUT:$(TARGET_DIR)/$@ $^ # Compile $(OBJ_DIR)/$(OBJECTS): $(SOURCES) $(CC) /c $(CFLAGS) $(INCLUDES) /Fo$(OBJ_DIR)/ /EHsc $(SOURCES) .PHONY: build dirs: $(MKDIR) $(TARGET_DIR) $(MKDIR) $(OBJ_DIR) $(MKDIR) $(TEST_DIR)
内容的提问来源于stack exchange,提问作者Adrian Costin
相关产品推荐
相关产品推荐

