202604140951clickhouse-cmakelists

我们从一个简单的CMakeLists.txt开始:

cmake_minimum_required(VERSION 3.20)
project(GREDialing VERSION 0.1)
 
message(STATUS "CMake version: " ${CMAKE_VERSION})
if(NOT ${CMAKE_VERSION} VERSION_LESS "3.2")
    set(CMAKE_CXX_STANDARD 17)
    set(CMAKE_CXX_STANDARD_REQUIRED ON)
else()
    message(STATUS "Checking compiler flags for C++11 support.")
    # Set C++11 support flags for various compilers
    include(CheckCXXCompilerFlag)
    check_cxx_compiler_flag("-std=c++11" COMPILER_SUPPORTS_CXX11)
    check_cxx_compiler_flag("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
    if(COMPILER_SUPPORTS_CXX11)
        message(STATUS "C++11 is supported.")
        if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
            set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++")
        else()
            set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
        endif()
    elseif(COMPILER_SUPPORTS_CXX0X)
        message(STATUS "C++0x is supported.")
        if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
            set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -stdlib=libc++")
        else()
            set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
        endif()
    else()
        message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
    endif()
endif()
 
 
 
# 禁止 C++ assert terminate 程序
# add_definitions(-DNDEBUG)
 
# ASIO 不使用 Boost 库
#add_definitions(-DASIO_STANDALONE)
 
# Spdlog 使用外部 Fmt 库
#add_definitions(-DSPDLOG_FMT_EXTERNAL)
 
# Jwt-cpp 使用外部 Json 库
# ADD_DEFINITIONS(-DJWT_CPP_JSON_EXTERNAL)
 
# add_definitions(-DGLOG_ON)
 
# 生成编译命令文件,给 YCM/Clangd 使用
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
 
ADD_COMPILE_OPTIONS(-g)
 
#设置输出目录
# set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
# set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
# set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin)
 
 
#加入包含目录
# include_directories(${CMAKE_CURRENT_SOURCE_DIR}/service/src/main/cpp/decryptSDK/include) 
include_directories(decryptSDK/include) 
include_directories(/home/hubingbing/rapidjson/include)
include_directories(/home/hubingbing/concurrentqueue)
# include_directories(/opt/Euler_compile_env/usr/src/kernels/4.19.90-vhulk2107.1.0.h699.eulerosv2r10.aarch64/include)
 
 
#静态库目录
#link_directories(/usr/lib64/mysql)
 
#logger lib 
#add_subdirectory(decryptSDK)
 
 
# aux_source_directory(decryptSDK SRCS) #加入目录下所有源码
# add_executable(demo ${SRCS}) #生成可执行文件
# target_link_libraries(demo logger)  #链接logger库
 
set(EXECUTABLE_OUTPUT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/build")
message(STATUS "binary dir is ${EXECUTABLE_OUTPUT_PATH}")
 
set(BUILD_SCRATCH_TARGET 0)
 
if(BUILD_SCRATCH_TARGET)
    file(GLOB_RECURSE scratch_srcs scratch/*.cpp)
    # message(STATUS "all scratchs: ${scratch_srcs}")
    foreach(srcfile IN LISTS scratch_srcs)
        # Get file name without directory
        get_filename_component(elfname ${srcfile} NAME_WE)
        message(STATUS "${elfname} .. ${srcfile}")
        add_executable(${elfname} ${srcfile})
        target_link_libraries(${elfname} pthread)
    endforeach()
endif()
 
 
# add_executable(main ../main.cpp)
# add_executable(server udp_server.cpp) 
# add_executable(ConfigReader ConfigReader.cpp) 
# add_executable(packet_handler packet_handler.cpp)
 
set(BUILD_MAIN_TARGET 1)
 
if(BUILD_MAIN_TARGET)
    set(target main)
 
    set(src_lst main.cpp ConfigReader.cpp packet_handler.cpp SslClient.cpp packet.cpp)
    add_executable(${target} ${src_lst})
 
    aux_source_directory(decryptSDK KMC_DIR)
    target_sources(${target} PRIVATE ${KMC_DIR})
    target_include_directories(${target} PRIVATE /home/hubingbing/cloud-frame-kmc/include/)
    target_link_directories(${target} PRIVATE /home/hubingbing/cloud-frame-kmc/lib_so/)
    target_link_libraries(${target} kmcjni)
 
    target_link_libraries(${target} ssl crypto pthread)
endif()
# target_link_libraries(debug pthread)
# target_link_libraries(server pthread)

上面的cmake也不算太简单,我们再简化一下:

cmake_minimum_required(VERSION 3.20)
project(GREDialing VERSION 0.1)
 
message(STATUS "CMake version: " ${CMAKE_VERSION})
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
# 生成编译命令文件,给 YCM/Clangd 使用
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
 
# 附加调试信息
ADD_COMPILE_OPTIONS(-g)
 
#加入包含目录
include_directories(decryptSDK/include) 
include_directories(/home/yychi/rapidjson/include)
include_directories(/home/yychi/concurrentqueue)
 
# 加入./decryptSDK目录下所有源码并赋到SRCS变量中
# 后续可用$SRCS使用该变量
aux_source_directory(decryptSDK SRCS) 
add_executable(demo ${SRCS}) #生成可执行文件
target_link_libraries(demo logger)  #链接logger库
 
set(EXECUTABLE_OUTPUT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/build")
message(STATUS "binary dir is ${EXECUTABLE_OUTPUT_PATH}")
 
set(BUILD_SCRATCH_TARGET 0)
if(BUILD_SCRATCH_TARGET)
    # 递归找到./scratch目录下所有cpp文件,赋值给scratch_srcs变量
    file(GLOB_RECURSE scratch_srcs scratch/*.cpp)
    # message(STATUS "all scratchs: ${scratch_srcs}")
    foreach(srcfile IN LISTS scratch_srcs)
        # Get file name without directory
        get_filename_component(elfname ${srcfile} NAME_WE)
        message(STATUS "${elfname} .. ${srcfile}")
        add_executable(${elfname} ${srcfile})
        target_link_libraries(${elfname} pthread)
    endforeach()
endif()
 
 
set(BUILD_MAIN_TARGET 1)
if(BUILD_MAIN_TARGET)
    set(target main)
 
    set(src_lst main.cpp a.cpp b.cpp c.cpp d.cpp)
    add_executable(${target} ${src_lst})
 
    aux_source_directory(decryptSDK descrypt_srcs)
    # 为target增加要编译的源文件
    target_sources(${target} PRIVATE ${descrypt_srcs})
    # 为target增加include目录
    target_include_directories(${target} PRIVATE /home/yychi/cloud/include/)
    # 为target增加动态库/静态库搜索路径
    target_link_directories(${target} PRIVATE /home/yychi/frame/lib_so/)
    # 为target链接库文件
    target_link_libraries(${target} ssl crypto pthread)
endif()

通常的,一个CMakeLists.txt中必须包含至少一个target,target可以是动态库/静态库/可执行文件。当定义了target之后,推荐使用

  • target_sources
  • target_include_directories
  • target_link_directories
  • target_link_options
  • target_link_libraries
  • target_compile_options
  • target_compile_definitions
  • target_compile_features

而非

  • include_directories
  • link_directories
  • add_compile_options
  • add_link_options

等全局函数。

target权限

target系列函数有时需要传递权限参数:PRIVATE/PUBLIC/INTERFACE,其含义分别是

  • PRIVATE:只参与当前目标自身编译。
  • PUBLIC:既参与当前目标编译,也会传递给依赖它的目标。
  • INTERFACE:不参与当前目标编译,只传递给依赖它的目标

target_sources

target_source可以给目标追加源文件,像下面这样:

add_executable(app) # 先建空目标
target_sources(app a.cpp b.cpp)
 
# equivalent to
add_executable(app a.cpp b.cpp)

最方便的一个用法是根据不同平台,添加不同源文件,就像这样

add_executable(app main.cpp)
target_sources(app PRIVATE
    $<$<PLATFORM_ID:Windows>:windows.cpp>
    $<$<PLATFORM_ID:Linux>:linux.cpp>
)

这里用到了cmake的生成器表达式,我目前也不甚了解。PLATFORM_ID参考,

平台PLATFORM_ID
WindowsWindows
LinuxLinux
macOSDarwin
FreeBSDFreeBSD
iOSiOS

CMake命令行

# 当前目录有CMakeLists.txt,指定在build目录生成构建项目(会自动创建目录)
cmake -B build
 
# 已经在build目录生成项目,直接让cmake调用响应的构建工具(make/ninja/msvc等)执行构建
cmake --build build
 
# 指定生成ninja项目,cmake -h最后一节会列出所有支持的构建工具集
cmake -G "Ninja"

变量

set(<variable> <value>) # set value to variable
${variable} # take the value of variable

Note

CMake语言不区分大小写,但是参数区分大小写。

References

  1. CMake 完整使用教程 之二 从可执行文件到库