CMake之编写属于自己的Findxxx.cmake文件:定义一个定制化的CMakeLists.txt文件

简介: CMake之编写属于自己的Findxxx.cmake文件:定义一个定制化的CMakeLists.txt文件

概述

CMake是一个跨平台的开源构建工具,可以帮助开发人员自动化构建、测试和打包软件。当构建项目时,CMake会按照CMakeLists.txt文件中的说明生成相应的项目文件或IDE。但是有时候,我们需要自己定义一些变量或命令等,这时候就需要编写自己的xxx.cmake文件来帮助我们管理我们的项目。
本文将介绍如何创建属于自己的xxx.cmake文件并编写CMake命令,以及如何在CMake中使用它。通过本文的介绍,读者将能够掌握如何利用xxx.cmake文件更有效地管理自己的CMake项目。


了解CMake模块

C在CMake中,模块是一种可重用的、可共享的代码单元,通常包含一组定义函数、宏和变量的CMake脚本。CMake自带一些标准模块,例如FindXXX.cmake系列模块,用于查找系统中已安装的库、头文件和程序等。
用户也可以编写自己的模块,并将其共享给其他项目使用。编写xxx.cmake文件是编写模块的一种方式。


xxx.cmake文件和find_package的关系

xxx.cmake文件和find_package之间存在密切的联系。
在使用第三方库时,通常会提供一个xxxConfig.cmake文件用于描述库的编译选项和链接选项。
该文件可以通过find_package命令引入,并将库的名称传递给该命令,例如:
find_package(MyLibrary REQUIRED)
如果库的CMake构建文件中定义了MYLIBRARY_ROOT变量,则find_package可以自动查找并引入MyLibraryConfig.cmake文件,并将库的编译选项和链接选项导入到CMake工程中,使得可以使用库的API进行开发。
而在某些情况下,库的CMake构建文件并不提供xxxConfig.cmake文件,但可以提供一个xxx.cmake文件来描述编译和链接的选项。此时,可以通过include命令引入该xxx.cmake文件,并将库的编译选项和链接选项导入到CMake工程中,例如:
include(path/to/my_library.cmake)
通过此方式引入的xxx.cmake文件可以包含定义变量、输出信息、定义函数和定义可执行文件的编译和链接选项等命令,用于实现更加灵活和可维护的CMake工程。
因此,xxx.cmake文件不仅可以用于自定义CMake脚本,而且也可以作为第三方库的配置文件来使用。同时,find_package命令可以与xxx.cmake文件配合使用,以便将库的编译选项和链接选项导入到CMake工程中。

创建xxx.cmake文件

在CMake项目中,我们可以将一些常用的命令和变量定义保存在一个独立的xxx.cmake文件中,便于在其他CMake脚本文件中引用。下面介绍如何创建xxx.cmake文件。

  • 首先在项目的某个目录下创建一个新的xxx.cmake文件,可以使用任何文本编辑器来编辑该文件。
  • 其次添加命令和变量定义
    在新建的xxx.cmake文件中添加需要定义的命令和变量,例如:
# 定义一个变量
set(MY_VAR "hello world")
# 定义一个函数
function(my_function)
  message("This is my function.")
endfunction()

可以根据具体的需求添加其他的命令和变量定义。
在其他CMake脚本文件中引用
在其他的CMake脚本文件中使用include命令引入定义好的xxx.cmake文件,然后就可以使用其中定义的命令和变量了,例如:

# 引入xxx.cmake文件
include(path/to/xxx.cmake)
# 使用在xxx.cmake文件中定义的变量
message(${MY_VAR})
# 调用在xxx.cmake文件中定义的函数
my_function()

通过将一些常用的命令和变量定义保存在xxx.cmake文件中,可以提高代码的重用性和可维护性,降低代码的复杂度,便于项目的开发和维护。


编写CMake命令

在CMake中编写自己的xxx.cmake文件时,常用到的命令包括以下几种:

set

set命令用于定义变量,语法为:
set(  [CACHE   [FORCE]])

其中,var为变量名,value为变量的值。CACHE可选,用于将变量缓存到CMake缓存中,用于记录用户的设置。type为变量的类型,docstring为变量的文档。FORCE可选,用于强制重新定义缓存变量。例如:

set(MY_VAR "hello world")
set(MY_VAR2 "world hello" CACHE STRING "My variable" FORCE)

message

message命令用于输出一些信息或变量的值,语法为:
message([] "message to display" ...)
其中,mode可选,可以是STATUS、WARNING、AUTHOR_WARNING、SEND_ERROR、FATAL_ERROR或DEPRECATION。默认情况下,输出的信息为普通信息。例如:

message("Hello, world!")
message(STATUS "MY_VAR: ${MY_VAR}")
message(WARNING "This is a warning")

include

include()命令用于包含其他CMake脚本文件。这对于模块化代码和重用脚本非常有用。语法如下:

include(<filename> [OPTIONAL] [RESULT_VARIABLE <var>] [NO_POLICY_SCOPE])
• 1
  • :要包含的文件名或路径。
  • OPTIONAL:如果找不到指定的文件,不会引发错误,而是设置的值为空。这是可选参数。
  • :用于存储包含文件结果的变量名。
  • NO_POLICY_SCOPE:指定不使用命名空间管理包含的文件,可选,用于避免将被引入文件的策略影响到引入文件本身的策略。
    例如:
include(path/to/xxx.cmake)
include(path/to/other.cmake OPTIONAL)

function

function命令用于定义函数,语法为:

function(<name> [<arg1> ...])
   <commands>
  endfunction()

其中,name为函数名,arg1为函数的参数。commands为函数的实现部分。例如:

function(add_numbers a b)
   message("a + b = ${a} + ${b} = $(expr ${a} + ${b})")
 endfunction()
 
 add_numbers(2 3)

条件判断

if(condition) - 该命令根据给定条件执行语句。条件可以是一个变量的值、一个文件是否存在等。你可以使用此命令来实现条件编译。
elseif(condition) - 如果前面的 if 或 elseif 语句的条件不成立,则执行该命令。你可以使用此命令来添加更多的条件。
else() - 如果前面的所有 if 和 elseif 语句的条件都不成立,则执行该命令。你可以使用此命令来供默认值。
endif() - 该命令结束一个 if 块。你必须使用此命令来结束 `if

查找外部库的命令

关于如何在cmake中链接外部库,可以参考我的这篇文章:CMake 链接外部库


在CMake中使用xxx.cmake文件

在CMake项目中,我们可以在主CMakeLists.txt文件中使用include命令来引入其他CMake脚本文件,如下所示:

# 引入子目录CMakeLists.txt文件
add_subdirectory(path/to/subdir)
# 引入其他CMake脚本文件
include(path/to/other_script.cmake)
# 使用xxx.cmake文件中定义的变量
message("变量A的值是:${VAR_A}")
# 使用xxx.cmake文件中定义的函数
my_function()
#需要注意的是,当xxx.cmake文件与主CMakeLists.txt文件中使用的变量、函数等命名冲突时,可能会出现不必要的错误。
#因此,在编写xxx.cmake文件时,需要避免与主CMakeLists.txt文件中使用的变量、函数等命名冲突。

在使用include命令引入其他CMake脚本文件时,需要注意以下几点:
引入的文件路径可以是相对路径或绝对路径。
引入的文件必须是合法的CMake脚本文件,否则会出现语法错误。
引入的文件中定义的变量、函数等可以在主CMakeLists.txt文件中使用,但需要注意命名冲突的问题。
通过使用include命令,我们可以更好地组织CMake项目的结构,将大型项目分为多个子目录,并在每个子目录中分别编写对应的CMakeLists.txt文件,便于项目的维护和管理。


CMake中使用xxx.cmake文件的场景

定义项目的通用变量和函数

可以将项目中经常使用的变量和函数定义在一个单独的xxx.cmake文件中,便于在其他CMake脚本文件中引用,提高代码的重用性和可维护性。例如:

# 定义项目名称
  set(PROJECT_NAME "MyProject")
  
  # 定义编译器和编译选项
  set(CMAKE_C_COMPILER "gcc")
  set(CMAKE_C_FLAGS "-Wall -Werror -O2")
  
  # 定义一个函数,用于打印版本信息
  function(print_version)
    message("Version 1.0.0")
  endfunction()

定义第三方库的编译选项和链接选项

在使用第三方库时,通常需要指定编译选项和链接选项,可以将其定义在一个单独的xxx.cmake文件中,便于在其他CMake脚本文件中引用。例如:

# 定义第三方库的编译选项
  set(THIRD_PARTY_C_FLAGS "-Ipath/to/include")
  
  # 定义第三方库的链接选项
  set(THIRD_PARTY_LINK_FLAGS "-Lpath/to/lib -lthird_party_lib")

定义不同平台的编译选项和链接选项

在不同的平台上,需要使用不同的编译选项和链接选项,可以将其定义在不同的xxx.cmake文件中,在不同的平台上引用不同的文件,以实现平台相关的编译和链接操作。例如:

# Windows平台的编译选项和链接选项
 if(WIN32)
   set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /DWIN32 /D_WINDOWS")
   set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS")
 endif()
 
 # Linux平台的编译选项和链接选项
 if(UNIX)
   set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D__LINUX__")
   set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,->>  rpath,./lib")
 endif()

通过将编译选项和链接选项定义在不同的xxx.cmake文件中,可以轻松地切换不同的平台,实现跨平台的应用开发和部署。


示例

# 指定查找的路径
SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} /home/usr)
# 查找库文件
FIND_LIBRARY(Xxx_LIBRARY
    NAMES xxx
    PATHS /home/usr/lib
)
# 查找头文件
FIND_PATH(Xxx_INCLUDE_DIR
    NAMES xxx.h
    PATHS /home/usr/include
)
# 输出查找结果
IF(Xxx_LIBRARY AND Xxx_INCLUDE_DIR)
    MESSAGE(STATUS "Found Xxx: ${Xxx_LIBRARY}")
    MESSAGE(STATUS "Found Xxx header: ${Xxx_INCLUDE_DIR}")
ELSE()
    MESSAGE(FATAL_ERROR "Could not find Xxx library and header files")
ENDIF()
# 设置变量
SET(Xxx_FOUND TRUE)
SET(Xxx_LIBRARIES ${Xxx_LIBRARY})
SET(Xxx_INCLUDE_DIRS ${Xxx_INCLUDE_DIR})

您可以将以上代码保存为 Findxxx.cmake 文件,并将其放置到您的项目中的 cmake/modules 目录中。在您的 CMakeLists.txt 文件中,通过 find_package() 命令引用该模块即可:

find_package(Xxx REQUIRED)
include_directories(${Xxx_INCLUDE_DIRS})
target_link_libraries(${PROJECT_NAME} ${Xxx_LIBRARIES})

请注意修改代码中的 xxx 为您实际的库名称。

总结

在CMake项目中,我们可以使用xxx.cmake文件来定义自己的变量或函数等。我们可以通过创建自己的xxx.cmake文件,编写CMake命令,并使用include命令将其引入主CMakeLists.txt文件中,从而更好地管理我们的项目。我们还可以在主CMakeLists.txt文件中使用xxx.cmake文件中定义的变量或函数等。在使用xxx.cmake文件时,需要注意命名冲突的问题,避免出现不必要的错误。通过本文的介绍,读者可以更好地掌握如何利用xxx.cmake文件来管理CMake项目。


目录
相关文章
|
2月前
|
C语言 Windows
使用CMake调用Makefile 项目
使用CMake调用Makefile 项目
48 0
|
2月前
|
测试技术
使用CLion创建Cmake项目,使用GoogleTest和GoogleMock对代码进行测试
使用CLion创建Cmake项目,使用GoogleTest和GoogleMock对代码进行测试
59 3
|
2月前
|
开发框架 前端开发 算法
【Qt App 编译 】Qt Cmake 资源文件的加载:如何使用 CMakeLists.txt 文件和资源文件
【Qt App 编译 】Qt Cmake 资源文件的加载:如何使用 CMakeLists.txt 文件和资源文件
82 0
|
2月前
|
编译器
【cmake】 --- 一个完整的cmake工程示例
【cmake】 --- 一个完整的cmake工程示例
58 0
|
2月前
好用的 自定义Makefile文件
好用的 自定义Makefile文件
17 0
|
11月前
|
C++
2022-9-28-CMAKE工程中Cmakelist文档编写的一些注意点
2022-9-28-CMAKE工程中Cmakelist文档编写的一些注意点
93 0
|
计算机视觉
CMakeLists用法
CMakeLists用法
133 0
|
C++
AndroidStudio中CMake多模块多目录编译
在学习过程接触到了CMake编译,它的好处就是可以跨平台。 使用AndroidStudio开发的工程中需要引入第三方库,加上自己的lib库,最后生成指定的SO库,这就涉及到了多模块编译。
477 0