Windows下CMake打包C/C++库供调用的错误排查与规范优化
背景
我用CMake在Windows平台给C/C++程序调用共享或静态库,总结了两步入门流程,但遇到了问题:
- 在Ubuntu(WSL)环境下用g++编译完全正常;
- 在Windows环境下用Visual Studio 2022打开项目,通过开发者PowerShell编译失败。
问题
- 执行测试步骤后出现链接错误,该怎么修改CMakeLists.txt解决这个Windows平台的错误?
错误信息:
\CreateSharedLibs\UseLib..\CreateLib\build\lib\MathFunctions.dll : fatal error LNK1107: invalid or corrupt file: cannot read at 0x340
- 有没有必要对CMakeLists.txt做额外修改来更符合规范、避免错误?比如要不要用
find_library,如果用的话该怎么操作?
测试步骤
先在CreateLib目录执行构建流程,再在UseLib目录执行相同操作:
- Linux环境下直接在终端执行;
- Windows环境下可以在Visual Studio中打开目录,用开发者PowerShell执行以下命令:
mkdir build cd build cmake .. cmake --build .
代码结构
CreateLib - CMakeLists.txt - src - MathFunctions.cpp - include - MathFunctions.h UseLib - CMakeLists.txt - src - main.cpp
CreateLib 代码文件
CMakeLists.txt
cmake_minimum_required(VERSION 3.10) project(MathFunctionsProject) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) # Set the output directory for the DLL set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # Add the include directory include_directories(include) # Define location of source files set(SOURCE_FILES src/MathFunctions.cpp) # Create the shared library add_library(MathFunctions SHARED ${SOURCE_FILES}) # Set the name and properties of the shared library set_target_properties(MathFunctions PROPERTIES OUTPUT_NAME "MathFunctions" PREFIX "") # Copy the DLL to the output directory add_custom_command(TARGET MathFunctions POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:MathFunctions> ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} )
MathFunctions.h
#ifndef MATHFUNCTIONS_H #define MATHFUNCTIONS_H double sqrt_custom(double x); #endif
MathFunctions.cpp
#include "MathFunctions.h" #include <cmath> double sqrt_custom(double x) { return std::sqrt(x); }
UseLib 代码文件
CMakeLists.txt
cmake_minimum_required(VERSION 3.10) project(SquareRootApp) # Set the output directory for the executable set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) # Create the executable and link against the DLL add_executable(SquareRootApp src/main.cpp) # Link the executable with the DLL IF (WIN32) target_link_libraries(SquareRootApp PRIVATE ${CMAKE_SOURCE_DIR}/../CreateLib/build/lib/MathFunctions.dll) ELSE() target_link_libraries(SquareRootApp PRIVATE ${CMAKE_SOURCE_DIR}/../CreateLib/build/lib/MathFunctions.so) ENDIF() # Include the directory containing the header files target_include_directories(SquareRootApp PRIVATE ${CMAKE_SOURCE_DIR}/../CreateLib/include)
main.cpp
#include <iostream> #include "MathFunctions.h" int main() { double x = 16.0; double result = sqrt_custom(x); std::cout << "Square root of " << x << " is: " << result << std::endl; std::cin.get(); return 0; }
解决方案
1. 解决Windows链接错误LNK1107
Windows平台下链接DLL时,不能直接链接.dll文件,必须链接对应的导入库(.lib文件)——Visual Studio编译DLL时会生成两个文件:.dll是运行时依赖,.lib是链接时需要的导入库。
修改UseLib/CMakeLists.txt中的链接逻辑:
IF (WIN32) # 链接导入库.lib,而非.dll文件 target_link_libraries(SquareRootApp PRIVATE ${CMAKE_SOURCE_DIR}/../CreateLib/build/lib/MathFunctions.lib) ELSE() target_link_libraries(SquareRootApp PRIVATE ${CMAKE_SOURCE_DIR}/../CreateLib/build/lib/MathFunctions.so) ENDIF()
另外,CreateLib的CMake配置里,CMAKE_LIBRARY_OUTPUT_DIRECTORY在Windows下默认输出DLL,而导入库.lib会生成在CMAKE_ARCHIVE_OUTPUT_DIRECTORY。可以统一输出目录避免找不到.lib:
在CreateLib/CMakeLists.txt中添加:
# 统一静态库(导入库)输出目录 set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
这样.lib和.dll都会输出到build/lib目录,确保UseLib能找到正确的导入库。
2. 遵循CMake规范的优化方案
直接硬编码库路径不是CMake的最佳实践,推荐以下两种更可靠的方式:
方式一:用find_library自动定位库
修改UseLib/CMakeLists.txt:
cmake_minimum_required(VERSION 3.10) project(SquareRootApp) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) add_executable(SquareRootApp src/main.cpp) # 自动查找MathFunctions库,跨平台适配 find_library(MATH_FUNCTIONS_LIB NAMES MathFunctions MathFunctions.lib MathFunctions.so PATHS ${CMAKE_SOURCE_DIR}/../CreateLib/build/lib ) # 链接找到的库 target_link_libraries(SquareRootApp PRIVATE ${MATH_FUNCTIONS_LIB}) # 包含头文件目录 target_include_directories(SquareRootApp PRIVATE ${CMAKE_SOURCE_DIR}/../CreateLib/include)
find_library会根据当前平台自动匹配对应的库文件(Windows找.lib,Linux找.so),无需手动判断平台。
方式二:将CreateLib作为子项目管理(推荐)
把CreateLib作为UseLib的子项目,CMake会自动构建依赖并管理路径:
在UseLib/CMakeLists.txt中添加:
# 添加子项目,指定构建目录 add_subdirectory(../CreateLib ${CMAKE_BINARY_DIR}/CreateLib)
之后直接用目标名链接,无需手动设置路径:
target_link_libraries(SquareRootApp PRIVATE MathFunctions) # 头文件目录可通过子项目目标自动继承,无需手动设置
此外,CreateLib中的全局include_directories(include)建议改成目标级别的包含,符合现代CMake规范:
target_include_directories(MathFunctions PUBLIC include)
这样其他目标链接MathFunctions时,会自动继承其头文件目录,无需在UseLib中重复设置。
内容的提问来源于stack exchange,提问作者Scott G

