cmake选择性编译模块

cmake如何在一个项目中选择部分模块进行编译?
有几十个静态库,我需要根据系统的不同以及设备的不同选择不同的静态库进行编译,BUILD.gn中可以使用projects进行选择,但cmake中该如何去做?

在使用 CMake 构建项目时,可以通过条件判断和变量控制来选择部分模块进行编译。以下是一种常见的方法:

1.定义变量:在 CMakeLists.txt 文件中,定义一个变量来表示系统和设备的选择。例如,可以定义一个名为 "SYSTEM" 的变量来表示系统选择,以及一个名为 "DEVICE" 的变量来表示设备选择。

set(SYSTEM "system1" CACHE STRING "System selection")
set(DEVICE "device1" CACHE STRING "Device selection")

2.使用条件判断:在 CMakeLists.txt 文件中,使用条件判断来根据变量的值选择需要编译的模块。例如,可以使用 if 语句和等于(EQUAL)运算符来判断变量的值,并在满足条件时添加需要编译的模块。

if(SYSTEM STREQUAL "system1")
    if(DEVICE STREQUAL "device1")
        add_subdirectory(module1)
    elseif(DEVICE STREQUAL "device2")
        add_subdirectory(module2)
    endif()
elseif(SYSTEM STREQUAL "system2")
    add_subdirectory(module3)
endif()

在上述示例中,根据系统和设备的选择,选择不同的模块进行编译。您可以根据实际情况,根据需要添加或修改条件判断部分的代码。

3.运行 CMake:运行 cmake 命令来生成构建系统所需的 Makefile 或其他构建文件。

4.构建项目:运行构建系统的命令(如 make 或 ninja),编译选择的模块。

请注意,上述示例中的代码只是一种常见的方法,具体的实现方式可能因项目的结构和需求而有所不同。您可以根据您的项目需求,灵活调整和扩展条件判断部分的代码,以实现您的选择需求。

  • 这篇博客: cmake的安装、使用、常用命令介绍以及一些常用配置中的 10. 部分常用命令 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
    • PROJECT:指定工程名称,还可指定工程支持的语言(可忽略)
    • SET:定义变量,可定义多个变量,如下:
    SET(SRC_LIST main.c util.c reactor.c))
    
    • MESSAGE:向终端输出用户定义的信息或变量的值
    MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] “message to display” …)
    
    • SEND_ERROR, 产生错误,生成过程被跳过
    • STATUS, 输出前缀为—的信息
    • FATAL_ERROR, 立即终止所有cmake过程
    • ADD_EXECUTABLE:生成可执行文件
    • ADD_LIBRARY:生成动态库或静态库
    ADD_LIBRARY(libname [SHARED | STATIC | MODULE] [EXCLUDE_FROM_ALL] SRC_LIST) #生成动态、静态库,module等同于shared
    # EXCLUDE_FROM_ALL表示该库不会被默认构建
    
    • SET_TARGET_PROPERTIES:设置动态库的版本和API版本
    SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")#生成的库名字
    # 设置动态库的版本号,这里设置了两个版本号
    SET_TARGET_PROPERTIES(hello_shared PROPERTIES VERSION 1.2 SOVERSION 1)
    
    • CMAKE_MINIMUM_REQUIRED:声明CMake的版本要求
    CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
    
    • ADD_SUBDIRECTORY:向当前工程添加存放源文件的子目录

    • SUBDIRS:deprecated,不再推荐使用

    • INCLUDE_DIRECTORIES:向工程添加多个特定的头文件搜索路径

    • LINK_DIRECTORIES:添加非标准的共享库搜索路径

    • TARGET_LINK_LIBRARIES:为target添加需要链接的共享库

    • ADD_DEFINITIONS:向C/C++编译器添加-D定义

    • ADD_DEPENDENCIES:定义target依赖的其他target

    • AUX_SOURCE_DIRECTORY:发现一个目录下所有的源代码文件并将列表存储在一个变量中

    • EXEC_PROGRAM:用于在指定目录运行某个程序(默认为当前CMakeLists.txt所在目录)

    • INCLUDE:载入CmakeList.txt文件或者预定义的cmake模块

    INCLUDE(file [OPTIONAL]) #用来载入CMakeLists.txt文件
    INCLUDE(module [OPTIONAL])#用来载入预定义的cmake模块
    
    • FIND_
    • FIND_FILE( name path1 path2 …)

    VAR变量代表找到的文件全路径,包含文件名

    • FIND_LIBRARY( name path1 path2 …)

    VAR变量代表找到的库全路径,包含库文件名

    FIND_LIBRARY(libX X11 /usr/lib)
    IF (NOT libx)
        MESSAGE(FATAL_ERROR "libX not found")
    ENDIF(NOT libX)
    
    • FIND_PATH( name path1 path2 …)

    VAR变量代表包含这个文件的路径

    • FIND_PROGRAM( name path1 path2 …)

    VAR变量代表包含这个程序的全路径

    • FIND_PACKAGE( [major.minor] [QUIET] [NO_MODULE] [[REQUIRED | COMPONENTS] [componets …]])

    用来调用预定义在CMAKE_MODULE_PATH下的Find.cmake模块,你也可以自己定义Find
    模块,通过SET(CMAKE_MODULE_PATH dir)将其放入工程的某个目录供工程使用

    • IF语法:
    IF (expression)
        COMMAND1(ARGS ...)
        COMMAND2(ARGS ...)
        ...
    ELSE (expression)
        COMMAND1(ARGS ...)
        COMMAND2(ARGS ...)
        ...
    ENDIF (expression) # 一定要有ENDIF与IF对应
    
    IF (expression), #expression不为:空,0,N,NO,OFF,FALSE,NOTFOUND或<var>_NOTFOUND,为真
    IF (not exp), #与上面相反
    IF (var1 AND var2)
    IF (var1 OR var2)
    IF (COMMAND cmd) #如果cmd确实是命令并可调用,为真
    IF (EXISTS dir) #如果目录,为真
    IF (EXISTS file) #如果文件存在,为真
    IF (file1 IS_NEWER_THAN file2),#当file1比file2新,或file1/file2中有一个不存在时为真,文件名需使用全路径
    IF (IS_DIRECTORY dir) #当dir是目录时,为真
    IF (DEFINED var) #如果变量被定义,为真
    IF (var MATCHES regex) #此处var可以用var名,也可以用${var}
    IF (string MATCHES regex)
    
    #当给定的变量或者字符串能够匹配正则表达式regex时为真。比如:
    IF ("hello" MATCHES "ell")
        MESSAGE("true")
    ENDIF ("hello" MATCHES "ell")
    

    数字比较表达式:

    IF (variable LESS number)
    IF (string LESS number)
    IF (variable GREATER number)
    IF (string GREATER number)
    IF (variable EQUAL number)
    IF (string EQUAL number)
    

    按照字母表顺序进行比较:

    IF (variable STRLESS string)
    IF (string STRLESS string)
    IF (variable STRGREATER string)
    IF (string STRGREATER string)
    IF (variable STREQUAL string)
    IF (string STREQUAL string)
    
    #一个小例子,用来判断平台差异:
    IF (WIN32)
        MESSAGE(STATUS “This is windows.”)
    ELSE (WIN32)
        MESSAGE(STATUS “This is not windows”)
    ENDIF (WIN32)
    #上述代码用来控制在不同的平台进行不同的控制,但是,阅读起来却并不是那么舒服,ELSE(WIN32)之类的语句很容易引起歧义。
    #可以SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON)
    #这时候就可以写成:
    IF (WIN32)
    ELSE ()
    ENDIF ()
    #配合ELSEIF使用,可能的写法是这样:
    IF (WIN32)
        #do something related to WIN32
    ELSEIF (UNIX)
        #do something related to UNIX
    ELSEIF(APPLE)
        #do something related to APPLE
    ENDIF (WIN32)
    
    • WHILE语法:其真假判断条件可以参考IF指令
    WHILE(condition)
        COMMAND1(ARGS ...)
        COMMAND2(ARGS ...)
        ...
    ENDWHILE(condition)
    
    • FOREACH:FOREACH指令的使用方法有三种形式

    形式一:列表

    FOREACH(loop_var arg1 arg2 ...)
         COMMAND1(ARGS ...)
         COMMAND2(ARGS ...)
     ...
    ENDFOREACH(loop_var)
    

    示例:

    AUX_SOURCE_DIRECTORY(. SRC_LIST)
    FOREACH(F ${SRC_LIST})
         MESSAGE(${F})
    ENDFOREACH(F)
    

    形式二:范围

    FOREACH(loop_var RANGE total)
        COMMAND1(ARGS ...)
        COMMAND2(ARGS ...)
        ...
    ENDFOREACH(loop_var)
    
    #从0到total以1为步进
    FOREACH(VAR RANGE 10)
       MESSAGE(${VAR})
    ENDFOREACH(VAR)
    #输出:
    012345678910
    

    形式三:范围和步进

    FOREACH(loop_var RANGE start stop [step])
        COMMAND1(ARGS ...)
        COMMAND2(ARGS ...)
        ...
    ENDFOREACH(loop_var)
    

    从start开始到stop结束,以step为步进。**注意:**直到遇到ENDFOREACH指令,整个语句块才会得到真正的执行。

    FOREACH(A RANGE 5 15 3)
        MESSAGE(${A})
    ENDFOREACH(A)
    输出:
    581114
    
  • 您还可以看一下 肖朝军老师的CMake 最佳实践课程中的 CMake 如何控制共享库的符号可见性小节, 巩固相关知识点