CMake(Cross-Platform Make)是一种跨平台的构建系统生成器,用于管理和自动化软件的构建过程。它通过编写配置文件(通常是 CMakeLists.txt
)来定义项目的构建规则,支持多种编译器和操作系统,能够生成本地化的构建文件(如 Makefile、Visual Studio 解决方案等)。
CMake 广泛应用于以下场景:
跨平台项目:需要在多种操作系统上构建和部署的项目。
大型项目:包含多个模块和依赖的复杂项目。
团队开发:需要统一构建流程和配置的团队开发环境。
本文将对 duckdb 项目 src 目录下的 CMakeLists.txt 进行详细解析,代码内容基于最新 2025.01.20 日的提交版本( commit id:8e68a3e34aa526a342ae91e1b14b764bb3075a12)
add_definitions(-DDUCKDB)
if(${DISABLE_THREADS})
add_definitions(-DDUCKDB_NO_THREADS)
endif()
add_extension_definitions()
if(NOT MSVC)
set(CMAKE_CXX_FLAGS_DEBUG
"${CMAKE_CXX_FLAGS_DEBUG} -Wextra -Wno-unused-parameter -Wno-redundant-move"
)
if(CMAKE_COMPILER_IS_GNUCC)
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 6)
set(CMAKE_CXX_FLAGS_DEBUG
"${CMAKE_CXX_FLAGS_DEBUG} -Wimplicit-fallthrough")
endif()
else()
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wimplicit-fallthrough")
endif()
endif()
set(EXIT_TIME_DESTRUCTORS_WARNING FALSE)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}"
STREQUAL "AppleClang")
set(EXIT_TIME_DESTRUCTORS_WARNING TRUE)
set(CMAKE_CXX_FLAGS_DEBUG
"${CMAKE_CXX_FLAGS_DEBUG} -Wexit-time-destructors -Wimplicit-int-conversion -Wshorten-64-to-32 -Wnarrowing -Wsign-conversion -Wsign-compare -Wconversion"
)
endif()
set(DUCKDB_SYSTEM_LIBS ${CMAKE_DL_LIBS})
if(MSVC OR MINGW)
set(DUCKDB_SYSTEM_LIBS ${DUCKDB_SYSTEM_LIBS} ws2_32 rstrtmgr)
endif()
if(MSVC)
set(DUCKDB_SYSTEM_LIBS ${DUCKDB_SYSTEM_LIBS} bcrypt)
endif()
if(MSVC)
add_compile_options("/bigobj")
endif()
function(ensure_variable_is_number INPUT_VERSION OUT_RESULT)
if(NOT "${${INPUT_VERSION}}" MATCHES "^[0-9]+$")
message(
WARNING
"VERSION PARAMETER ${INPUT_VERSION} \"${${INPUT_VERSION}}\" IS NOT A NUMBER - SETTING TO 0"
)
set(${OUT_RESULT}
0
PARENT_SCOPE)
else()
set(${OUT_RESULT}
${${INPUT_VERSION}}
PARENT_SCOPE)
endif()
endfunction()
if(AMALGAMATION_BUILD)
add_library(duckdb SHARED "${PROJECT_SOURCE_DIR}/src/amalgamation/duckdb.cpp")
target_link_libraries(duckdb ${DUCKDB_SYSTEM_LIBS})
link_threads(duckdb)
link_extension_libraries(duckdb)
add_library(duckdb_static STATIC
"${PROJECT_SOURCE_DIR}/src/amalgamation/duckdb.cpp")
target_link_libraries(duckdb_static ${DUCKDB_SYSTEM_LIBS})
link_threads(duckdb_static)
link_extension_libraries(duckdb_static)
install(FILES "${PROJECT_SOURCE_DIR}/src/amalgamation/duckdb.hpp"
"${PROJECT_SOURCE_DIR}/src/include/duckdb.h"
DESTINATION "${INSTALL_INCLUDE_DIR}")
install(FILES "${PROJECT_SOURCE_DIR}/src/include/duckdb/common/winapi.hpp"
DESTINATION "${INSTALL_INCLUDE_DIR}/duckdb/common")
else()
add_definitions(-DDUCKDB_MAIN_LIBRARY)
add_subdirectory(optimizer)
add_subdirectory(planner)
add_subdirectory(parser)
add_subdirectory(function)
add_subdirectory(catalog)
add_subdirectory(common)
add_subdirectory(logging)
add_subdirectory(execution)
add_subdirectory(main)
add_subdirectory(parallel)
add_subdirectory(storage)
add_subdirectory(transaction)
add_subdirectory(verification)
set(DUCKDB_LINK_LIBS
${DUCKDB_SYSTEM_LIBS}
duckdb_fsst
duckdb_fmt
duckdb_pg_query
duckdb_re2
duckdb_miniz
duckdb_utf8proc
duckdb_hyperloglog
duckdb_fastpforlib
duckdb_skiplistlib
duckdb_mbedtls
duckdb_yyjson
duckdb_zstd)
add_library(duckdb SHARED ${ALL_OBJECT_FILES})
if(WIN32 AND NOT MINGW)
ensure_variable_is_number(DUCKDB_MAJOR_VERSION RC_MAJOR_VERSION)
ensure_variable_is_number(DUCKDB_MINOR_VERSION RC_MINOR_VERSION)
ensure_variable_is_number(DUCKDB_PATCH_VERSION RC_PATCH_VERSION)
ensure_variable_is_number(DUCKDB_DEV_ITERATION RC_DEV_ITERATION)
set(CMAKE_RC_FLAGS
"${CMAKE_RC_FLAGS} -D DUCKDB_VERSION=\"${DUCKDB_VERSION}\"")
set(CMAKE_RC_FLAGS
"${CMAKE_RC_FLAGS} -D DUCKDB_MAJOR_VERSION=\"${RC_MAJOR_VERSION}\"")
set(CMAKE_RC_FLAGS
"${CMAKE_RC_FLAGS} -D DUCKDB_MINOR_VERSION=\"${RC_MINOR_VERSION}\"")
set(CMAKE_RC_FLAGS
"${CMAKE_RC_FLAGS} -D DUCKDB_PATCH_VERSION=\"${RC_PATCH_VERSION}\"")
set(CMAKE_RC_FLAGS
"${CMAKE_RC_FLAGS} -D DUCKDB_DEV_ITERATION=\"${RC_DEV_ITERATION}\"")
target_sources(duckdb PRIVATE version.rc)
endif()
target_link_libraries(duckdb ${DUCKDB_LINK_LIBS})
link_threads(duckdb)
link_extension_libraries(duckdb)
add_library(duckdb_static STATIC ${ALL_OBJECT_FILES})
target_link_libraries(duckdb_static ${DUCKDB_LINK_LIBS})
link_threads(duckdb_static)
link_extension_libraries(duckdb_static)
target_include_directories(
duckdb PUBLIC $
$)
target_include_directories(
duckdb_static PUBLIC $
$)
install(
DIRECTORY "${PROJECT_SOURCE_DIR}/src/include/duckdb"
DESTINATION "${INSTALL_INCLUDE_DIR}"
FILES_MATCHING
PATTERN "*.hpp"
PATTERN "*.ipp")
install(FILES "${PROJECT_SOURCE_DIR}/src/include/duckdb.hpp"
"${PROJECT_SOURCE_DIR}/src/include/duckdb.h"
DESTINATION "${INSTALL_INCLUDE_DIR}")
endif()
install(
TARGETS duckdb duckdb_static
EXPORT "${DUCKDB_EXPORT_SET}"
LIBRARY DESTINATION "${INSTALL_LIB_DIR}"
ARCHIVE DESTINATION "${INSTALL_LIB_DIR}"
RUNTIME DESTINATION "${INSTALL_BIN_DIR}")
前文 CMakeLists.txt 涉及的常用语法整理如下:
CMake 命令 | 语法 | 作用 | 示例 |
---|---|---|---|
add_definitions |
add_definitions(-D<宏名>=<值>) |
添加预处理器定义(宏)。 |
|
if 和 else |
if( |
条件判断,根据条件执行不同的命令。 | if(${DISABLE_THREADS})... endif() |
set |
set(<变量名> <值>) |
设置变量的值。 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wextra") |
function 和 endfunction |
function(<函数名> <参数列表>)... endfunction() |
定义自定义函数。 | function(ensure_variable_is_number INPUT_VERSION OUT_RESULT)... |
add_library |
add_library(<目标名> <类型> <源文件列表>) |
定义动态库或静态库。 | add_library(duckdb SHARED "source.cpp") |
target_link_libraries |
target_link_libraries(<目标名> <链接的库列表>) |
为目标指定链接的库。 | target_link_libraries(duckdb ${DUCKDB_SYSTEM_LIBS}) |
add_subdirectory |
add_subdirectory(<目录路径>) |
将子目录中的 CMakeLists.txt 包含到项目中。 |
add_subdirectory(optimizer) |
install |
|
定义安装时需要复制的文件或目录。 | install(FILES "file.hpp" DESTINATION "include") |
target_include_directories |
target_include_directories(<目标名> [INTERFACE|PUBLIC|PRIVATE] [items1...]) | 为目标设置包含路径。 | target_include_directories(duckdb PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include") |
add_compile_options |
add_compile_options(<编译选项>) |
为当前目录及其子目录的目标添加编译选项。 | add_compile_options("/bigobj") |
set (条件变量) |
set(<变量名> <值> CACHE INTERNAL "描述信息") |
设置变量的值,并将其存储在缓存中。 | set(EXIT_TIME_DESTRUCTORS_WARNING TRUE CACHE INTERNAL "Enable warning") |
语法补充说明
add_definitions
:用于定义宏,常用于条件编译。
if
和 else
:用于根据条件执行不同的逻辑。
set
:用于设置变量值,可以用于控制编译器标志或其他配置。
function
和 endfunction
:用于定义可复用的逻辑。
add_library
:用于定义库目标(动态库或静态库)。
target_link_libraries
:用于指定库的依赖关系。
add_subdirectory
:用于包含子目录中的 CMakeLists.txt
文件。
install
:用于定义安装规则,指定哪些文件或目录需要安装到目标路径。
target_include_directories
:用于为目标设置包含路径。
add_compile_options
:用于添加编译选项。
set
(条件变量):用于设置条件变量,常用于控制编译器警告或功能。
这个 CMake 文件是 DuckDB 项目的构建配置文件,用于定义如何编译和链接 DuckDB 的动态库和静态库。以下是对文件内容的详细解析:
add_definitions(-DDUCKDB)
定义了一个宏 DUCKDB
,用于在代码中标识当前项目是 DuckDB。
if(${DISABLE_THREADS})
add_definitions(-DDUCKDB_NO_THREADS)
endif()
如果变量 DISABLE_THREADS
被设置为 ON
,则定义宏 DUCKDB_NO_THREADS
,表示禁用多线程功能。
add_extension_definitions()
这是一个自定义函数,用来添加扩展模块的定义。具体实现需要查看外层 CMakeLists.List 中 add_extension_definitions
function的定义。
function(add_extension_definitions)
include_directories(${PROJECT_SOURCE_DIR}/extension)
if(NOT "${TEST_WITH_LOADABLE_EXTENSION}" STREQUAL "")
string(REPLACE ";" "," COMMA_SEPARATED_EXTENSIONS "${TEST_WITH_LOADABLE_EXTENSION}")
# Note: weird commas are for easy substring matching in c++
add_definitions(-DDUCKDB_EXTENSIONS_TEST_WITH_LOADABLE=\",${COMMA_SEPARATED_EXTENSIONS},\")
add_definitions(-DDUCKDB_EXTENSIONS_BUILD_PATH="${CMAKE_BINARY_DIR}/extension")
endif()
if(NOT("${TEST_REMOTE_INSTALL}" STREQUAL "OFF"))
add_definitions(-DDUCKDB_TEST_REMOTE_INSTALL="${TEST_REMOTE_INSTALL}")
endif()
if(${DISABLE_BUILTIN_EXTENSIONS})
add_definitions(-DDISABLE_BUILTIN_EXTENSIONS=${DISABLE_BUILTIN_EXTENSIONS})
endif()
# Include paths for any registered out-of-tree extensions
foreach(EXT_NAME IN LISTS DUCKDB_EXTENSION_NAMES)
string(TOUPPER ${EXT_NAME} EXT_NAME_UPPERCASE)
if(${DUCKDB_EXTENSION_${EXT_NAME_UPPERCASE}_SHOULD_LINK})
add_definitions(-DDUCKDB_EXTENSION_${EXT_NAME_UPPERCASE}_LINKED=1)
if (DEFINED DUCKDB_EXTENSION_${EXT_NAME_UPPERCASE}_INCLUDE_PATH)
include_directories("${DUCKDB_EXTENSION_${EXT_NAME_UPPERCASE}_INCLUDE_PATH}")
else()
# We try the default locations for headers
include_directories("${PROJECT_SOURCE_DIR}/extension_external/${EXT_NAME}/src/include")
include_directories("${PROJECT_SOURCE_DIR}/extension_external/${EXT_NAME}/include")
endif()
endif()
endforeach()
endfunction()
这个 add_extension_definitions
函数是一个自定义的 CMake 函数,用于配置和管理 DuckDB 项目中扩展模块的编译选项和包含路径。详细作用是:
配置扩展模块的编译选项:
添加编译定义,标识是否启用可加载扩展、远程安装测试、禁用内置扩展等。
为每个需要链接的扩展模块定义宏,标识其已链接。
设置扩展模块的包含路径:
将扩展模块的头文件路径添加到编译器的包含路径中,确保编译时能够找到扩展模块的头文件。
支持灵活的扩展模块管理:
支持通过变量控制扩展模块的启用和链接。
提供默认路径,同时允许用户自定义扩展模块的头文件路径。
这个函数是 DuckDB 项目中用于动态管理扩展模块的关键部分,使得项目能够灵活地支持多种扩展模块的编译和链接。
if(NOT MSVC)
set(CMAKE_CXX_FLAGS_DEBUG ...)
endif()
如果不是使用 MSVC(Microsoft Visual C++)编译器,则为调试模式添加额外的编译器警告标志,例如 -Wextra
、-Wno-unused-parameter
等,用于增强代码的编译检查。
if(CMAKE_COMPILER_IS_GNUCC)
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 6)
set(CMAKE_CXX_FLAGS_DEBUG ...)
endif()
如果使用的是 GCC 编译器且版本大于 6,则添加 -Wimplicit-fallthrough
警告标志。
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang")
set(EXIT_TIME_DESTRUCTORS_WARNING TRUE)
set(CMAKE_CXX_FLAGS_DEBUG ...)
endif()
如果使用的是 Clang 或 AppleClang 编译器,则启用更多警告标志,例如 -Wexit-time-destructors
、-Wimplicit-int-conversion
等。
set(DUCKDB_SYSTEM_LIBS ${CMAKE_DL_LIBS})
初始化系统库依赖列表,CMAKE_DL_LIBS
是 CMake 提供的动态加载库依赖。
if(MSVC OR MINGW)
set(DUCKDB_SYSTEM_LIBS ${DUCKDB_SYSTEM_LIBS} ws2_32 rstrtmgr)
endif()
如果是 Windows 平台(MSVC 或 MinGW),则添加额外的系统库 ws2_32
和 rstrtmgr
。
if(MSVC)
set(DUCKDB_SYSTEM_LIBS ${DUCKDB_SYSTEM_LIBS} bcrypt)
endif()
如果是 MSVC 编译器,添加 bcrypt
库。
if(MSVC)
add_compile_options("/bigobj")
endif()
如果是 MSVC 编译器,添加编译选项 /bigobj
,用于支持更大的对象文件。
function(ensure_variable_is_number INPUT_VERSION OUT_RESULT)
if(NOT "${${INPUT_VERSION}}" MATCHES "^[0-9]+$")
message(WARNING ...)
set(${OUT_RESULT} 0 PARENT_SCOPE)
else()
set(${OUT_RESULT} ${${INPUT_VERSION}} PARENT_SCOPE)
endif()
endfunction()
定义一个函数,用于校验输入变量是否为数字。如果不是数字,则输出警告并将其设置为 0。
if(AMALGAMATION_BUILD)
add_library(duckdb SHARED "${PROJECT_SOURCE_DIR}/src/amalgamation/duckdb.cpp")
add_library(duckdb_static STATIC "${PROJECT_SOURCE_DIR}/src/amalgamation/duckdb.cpp")
...
endif()
如果启用 AMALGAMATION_BUILD
(单文件构建模式),则从 amalgamation
目录中构建动态库和静态库。
安装相关的头文件到指定目录。
else()
add_definitions(-DDUCKDB_MAIN_LIBRARY)
add_subdirectory(...)
...
endif()
如果未启用 AMALGAMATION_BUILD
,则从多个子目录(如 optimizer
、planner
、parser
等)中构建项目。
定义宏 DUCKDB_MAIN_LIBRARY
。
添加多个子目录,这些目录可能包含 DuckDB 的不同模块代码。
构建动态库和静态库,并链接多个依赖库(如 duckdb_fsst
、duckdb_fmt
等)。
if(WIN32 AND NOT MINGW)
ensure_variable_is_number(DUCKDB_MAJOR_VERSION RC_MAJOR_VERSION)
...
target_sources(duckdb PRIVATE version.rc)
endif()
如果是 Windows 平台且不是 MinGW,从变量中提取版本号,并将其传递给资源文件 version.rc
。
install(TARGETS duckdb duckdb_static ...)
定义动态库和静态库的安装路径,包括库文件和头文件的安装目录。
这个 CMake 文件的主要作用是:
配置编译选项和编译器标志。
根据平台和编译器选择系统库依赖。
支持两种构建模式:
Amalgamation 构建:从单文件中构建库。
非 Amalgamation 构建:从多个模块目录中构建库。
安装构建生成的库和头文件到指定目录。
通过这种方式,DuckDB 项目能够灵活地支持不同平台和构建模式,同时保持代码的可维护性和可扩展性。