CMake 库的默认类型与不支持动态库警告
构建库的默认类
在 CMake 中,添加库目标时,如果不指定库的类型(STATIC
,SHARED
),则会根据变量 BUILD_SHARED_LIBS
的值来决定库的类型。
BUILD_SHARED_LIBS
为 ON
时默认构建动态库,OFF
时则默认构建静态库。
BUILD_SHARED_LIBS
自身的默认值通常为 ON
。
add_library(${LIB_NAME} ${LIB_SOURCE})
不支持动态库的警告
现象
我在构建 WebAssembly 应用时,经常在引入第三方库时看到这样的警告:
CMake Warning (dev) at 3rdparty/zlib/CMakeLists.txt:153 (add_library):
ADD_LIBRARY called with SHARED option but the target platform does not
support dynamic linking. Building a STATIC library instead. This may lead
to problems.
This warning is for project developers. Use -Wno-dev to suppress it.
这是因为 WebAssembly 不支持动态库,CMake 将所有构建动态库的目标都转换为了构建静态库。
遇到的问题
但是我自己添加的库目标却没有产生这样的警告,而是构建出了动态库。并且在之后构建可执行文件时产生错误。
CMake 是怎么判断当前平台不支持动态库的?
为什么第三方库产生了警告并自动转换为了构建静态库,而我添加的目标没有警告直接构建了动态库?
现象的原理
通过查阅资料,发现 CMake 是通过 TARGET_SUPPORTS_SHARED_LIBS
属性来判断是否支持动态库的。
emcmake 自动将该属性设为了 FALSE
,因此遇到构建静态库时发出了警告并转换为构建静态库。
而引入 Qt 时,Qt 将该值又改为了 TRUE
,导致我在这之后添加的目标没有产生警告。
问题的解决办法
解决办法是在引入 Qt 之前将 BUILD_SHARED_LIBS
的值设为和 TARGET_SUPPORTS_SHARED_LIBS
一样。
CMake 中
ON
/OFF
,YES
/NO
,TRUE
/FALSE
都是布尔值,可以等价。
get_property(BUILD_SHARED_LIBS GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS)
补充
由于设置了变量 BUILD_SHARED_LIBS
的值,如果引入的第三方库中将该变量设为了 option
会产生警告,此类问题的消除方法:
if (POLICY CMP0077)
cmake_policy(SET CMP0077 NEW)
endif (POLICY CMP0077)
旧版本 Qt 使用 qt_addlibrary
时,会忽略 BUILD_SHARED_LIBS
,采用和 Qt 库相同的构建方式。
在 Qt 6.7 版本新增了 QTP0003
来决定策略,为 NEW
时和 BUILD_SHARED_LIBS
一致,为 OLD
时采用和 Qt 库相同的构建方式。
qt_policy(SET QTP0003 NEW)
但是无法通过 if (POLICY QTP0003)
来判断 QTP0003
是否存在,在旧版本上会报错,可以通过判断 Qt 版本来决定是否设置 QTP0003
,或者使用 addlibrary
代替 qt_addlibrary
。
CMake 子目录依赖处理
最近开发的一个项目依赖 libgeotiff
,同时 libgeotiff
又依赖 libtiff
,我将他们都加入 thirdparty 中,通过 add_subdirectory
添加进项目中。
- thirdparty
- CMakeLists.txt
- libgeotiff
- libtiff
add_subdirectory(libtiff)
add_subdirectory(libgeotiff/libgeotiff)
这样 CMake 仍会报错:
CMake Error at /usr/local/lib/python3.10/dist-packages/cmake/data/share/cmake-3.25/Modules/FindPackageHandleStandardArgs.cmake:230 (message):
Could NOT find TIFF (missing: TIFF_LIBRARY TIFF_INCLUDE_DIR)
因为 find_package
无法查找项目内的目标。
可以通过 FetchContent
来解决这个问题:
include(FetchContent)
FetchContent_Declare(
TIFF
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libtiff"
OVERRIDE_FIND_PACKAGE
)
add_subdirectory(libgeotiff)
CMake 忽略第三方库的安装指令
通过 CMake 在项目中包含第三方库的源码时,如果直接包含,install 的时候会同时安装第三方库。如果不想安装第三方库,可以添加 EXCLUDE_FROM_ALL
add_subdirectory(${目录} EXCLUDE_FROM_ALL)