CMAKE 编写 FindXXX.cmake

首先为什么要写 FindXXX.cmake

对于一个大型项目,作为一个合格的开发人员来说,特别是C/C++开源人员。会一门编译工具非常重要。在一个大型项目中,使用过Makefile 的肯定感觉到问题就是,当项目庞大的时候,越到后面的库或者进程,依赖的头文件,库将会越来越多。在写makefile也是一件非常痛苦的一件事。相信这个问题,不只是在Makefile 中存在,cmake 中也会存在。
那么如果,可以让编译过后的库自己解决,自己的依赖。是不是就可以解决这个问题呢?

cmake提供这样的方法:
find_package.

find_package 将会根据开发人员提前写好的库或者进程的依赖,自己去加载依赖项,可以大大减少程序员工作量。
但是程序自己是不知道,库的依赖关系。这块信息需要开发人员提前写好,告诉程序。
大家可以写 FindXXX.cmake 或者 XXXXConfig.cmake XXXX-config.cmake

至于他们的先后顺序如下
下面这张图片来自网上。
在这里插入图片描述

由上图

find_package(<package> [version] [EXACT] [QUIET]
             [REQUIRED] [[COMPONENTS] [components...]]
             [CONFIG|NO_MODULE] # 这里就是关于配置是否使用 XXXConfig.cmake 的配置
             [NO_POLICY_SCOPE]
             [NAMES name1 [name2 ...]]
             [CONFIGS config1 [config2 ...]]
             [HINTS path1 [path2 ... ]]
             [PATHS path1 [path2 ... ]]
             [PATH_SUFFIXES suffix1 [suffix2 ...]]
             [NO_DEFAULT_PATH]
             [NO_CMAKE_ENVIRONMENT_PATH]
             [NO_CMAKE_PATH]
             [NO_SYSTEM_ENVIRONMENT_PATH]
             [NO_CMAKE_PACKAGE_REGISTRY]
             [NO_CMAKE_BUILDS_PATH]
             [NO_CMAKE_SYSTEM_PATH]
             [NO_CMAKE_SYSTEM_PACKAGE_REGISTRY]
             [CMAKE_FIND_ROOT_PATH_BOTH |
              ONLY_CMAKE_FIND_ROOT_PATH |
              NO_CMAKE_FIND_ROOT_PATH])

所以由上图可以看出来,直接使用 find_package的时候,先是尝试去找 FindXXX.cmake 文件。

那么这个文件一般在哪里呢?
一般存在
CMAKE_MODULE_PATH变量中.

下面是个人理解的编写过程,中间可能有错误,仅作参考。
先给大家看我的文件结构:

├── hello
│   ├── build
│   │   ├── libhello.so
│   │   └── Makefile
│   ├── CMakeLists.txt
│   ├── Findhello.cmake
│   ├── Findworld.cmake
│   ├── hello.cpp
│   └── hello.h
├── main
│   ├── CMakeLists.txt
│   └── main.cpp
└── world
    ├── build
    │   ├── libworld.so
    │   └── Makefile
    ├── CMakeLists.txt
    ├── Findworld.cmake
    ├── worldConfig.cmake
    ├── world.cpp
    └── world.h

简单介绍一下关系:
world 文件夹里面生成 libworld.so 仅仅包含

// 打印 world
void world(){
std::cout << "world" <<std::endl;
}

hello 文件里面生成 libhello.so

// 打印 hello
void hello(){
std::cout << "hello" << std::endl;
wolrd();
}

main 文件里面 生成 main 进程

int main(){
	hello();
}

我这里编写 Findhello.cmake Findworld.cmake去包含 world 和 hello 的文件依赖。
Findhello.cmake

set(hello_FOUND TRUE) # auto 如果成功 find 到,hello_FOUND =true。一般用法会判断 hello_FOUND 
set(hello_ROOT_DIR /home/lhd/work/testCmake/hello/)

find_path(hello_INCLUDE_DIR NAMES hello.h PATHS "${hello_ROOT_DIR}") 
#mark_as_advanced(hello_INCLUDE_DIR) # show entry in cmake-gui

find_library(hello_LIBRARY NAMES libhello.so PATHS "${hello_ROOT_DIR}/build") 
#mark_as_advanced(hello_LIBRARY) # show entry in cmake-gui

# use xxx_INCLUDE_DIRS and xxx_LIBRARIES in CMakeLists.txt

find_package(world )



set(hello_INCLUDE_DIRS ${hello_INCLUDE_DIR} ${world_INCLUDE_DIRS})
message(WARNING "======================hello_INCLUDE_DIRS=${hello_INCLUDE_DIRS}")
set(hello_LIBRARIES ${hello_LIBRARY} ${world_LIBRARIES})
message(WARNING "======================hello_LIBRARIES=${hello_LIBRARIES}")
#set(hello_INCLUDE_DIRS ${hello_INCLUDE_DIR} )
#set(hello_LIBRARIES ${hello_LIBRARY} )
message( "hello-config.cmake " ${hello_ROOT_DIR})

Findworld.cmake

set(world_FOUND TRUE) # auto 
set(world_ROOT_DIR /home/lhd/work/testCmake/world/)

find_path(world_INCLUDE_DIR NAMES world.h PATHS "${world_ROOT_DIR}") 
#mark_as_advanced(world_INCLUDE_DIR) # show entry in cmake-gui

find_library(world_LIBRARY NAMES libworld.so PATHS "${world_ROOT_DIR}/build") 
#mark_as_advanced(world_LIBRARY) # show entry in cmake-gui

# use xxx_INCLUDE_DIRS and xxx_LIBRARIES in CMakeLists.txt
set(world_INCLUDE_DIRS ${world_INCLUDE_DIR} )
set(world_LIBRARIES ${world_LIBRARY} )

message( "world-config.cmake " ${world_ROOT_DIR})

上面2个FindXXX.cmake 看出来
Findhello.cmake 依赖了 Findworld.cmake

所以main在find_package 的时候只需要,find hello 就可以了。
但是有一个地方要注意 Findhello.cmake Findworld.cmake 最好要在同一个目录。
如果不在同一个目录在cmake 的时候就需要
-DCMAKE_MODULE_PATH=“路径1;路径2”

cmake_minimum_required(VERSION 3.10)

project(main VERSION 1.0.0)

find_package(hello REQUIRED)
#find_package(world REQUIRED)

include_directories(${hello_INCLUDE_DIRS} )


set(CMAKE_CXX_STANDARD 11)

add_executable(${PROJECT_NAME}  main.cpp)

target_link_libraries(${PROJECT_NAME} ${hello_LIBRARIES} )

编译过程 :

cmake … -DCMAKE_MODULE_PATH=“path1”

如果某些同学,就是想使用 XXXConfig.cmake

也可以。按找上面的理论。如果你想使用 XXXConfig.cmake
你有2个选择,第一个只提供 XXXConfig.cmake.
因为 find_package 首先会去找 FindXXX.cmake . 如果找不到将会查找 XXXConfig.cmake 或者 XXX-config.cmake

第二个方法,直接指定使用的就是 XXXConfig.cmake
find_package(XXX CONFIG)
或者 find_package(XXX NO_MODULE)
find_package 将不会去查找 FindXXX.cmake,直接找 XXXConfig.cmake

但是对于 XXXConfig.cmake 需要注意使用另一个变量来指定路径
-DCMAKE_PREFIX_PATH=”path1;path2“

下面看一下修改后的
helloConfig.cmake

set(hello_FOUND TRUE) # auto 
set(hello_ROOT_DIR /home/lhd/work/testCmake/hello/)

find_path(hello_INCLUDE_DIR NAMES hello.h PATHS "${hello_ROOT_DIR}") 
#mark_as_advanced(hello_INCLUDE_DIR) # show entry in cmake-gui

find_library(hello_LIBRARY NAMES libhello.so PATHS "${hello_ROOT_DIR}/build") 
#mark_as_advanced(hello_LIBRARY) # show entry in cmake-gui

# use xxx_INCLUDE_DIRS and xxx_LIBRARIES in CMakeLists.txt

find_package(world CONFIG)


if (world_FOUND)
set(hello_INCLUDE_DIRS ${hello_INCLUDE_DIR} ${world_INCLUDE_DIRS})
message(WARNING "======================hello_INCLUDE_DIRS=${hello_INCLUDE_DIRS}")
set(hello_LIBRARIES ${hello_LIBRARY} ${world_LIBRARIES})
message(WARNING "======================hello_LIBRARIES=${hello_LIBRARIES}")
#set(hello_INCLUDE_DIRS ${hello_INCLUDE_DIR} )
#set(hello_LIBRARIES ${hello_LIBRARY} )
message( "hello-config.cmake " ${hello_ROOT_DIR})
endif()

worldConfig.cmake

set(world_FOUND TRUE) # auto 
set(world_ROOT_DIR /home/lhd/work/testCmake/world/)

find_path(world_INCLUDE_DIR NAMES world.h PATHS "${world_ROOT_DIR}") 
#mark_as_advanced(world_INCLUDE_DIR) # show entry in cmake-gui

find_library(world_LIBRARY NAMES libworld.so PATHS "${world_ROOT_DIR}/build") 
#mark_as_advanced(world_LIBRARY) # show entry in cmake-gui

# use xxx_INCLUDE_DIRS and xxx_LIBRARIES in CMakeLists.txt
set(world_INCLUDE_DIRS ${world_INCLUDE_DIR} )
set(world_LIBRARIES ${world_LIBRARY} )

message( "world-config.cmake " ${world_ROOT_DIR})

main的 CMakeLists.txt

cmake_minimum_required(VERSION 3.10)

project(main VERSION 1.0.0)

find_package(hello REQUIRED CONFIG)
#find_package(world REQUIRED)

include_directories(${hello_INCLUDE_DIRS} )

message(WARNING "======================hello_INCLUDE_DIRS=${hello_INCLUDE_DIRS}")
message(WARNING "======================hello_LIBRARIES=${hello_LIBRARIES}")
set(CMAKE_CXX_STANDARD 11)

add_executable(${PROJECT_NAME}  main.cpp)

target_link_libraries(${PROJECT_NAME} ${hello_LIBRARIES} )

使用:
cmake … -DCMAKE_PREFIX_PATH=”path1;path2“ 就可以了。
当然这个 path1 path2 就是 helloConfig.cmake 和 worldConfig.cmake 所在的路径。

最后附上源码:https://gitee.com/lu_han_dong/camke-find_package

<think>嗯,用户问的是xxx-config.cmake和xxx.cmake之间的区别。我需要先回想一下之前提供的关于.cmake文件的信息,再结合用户提供的引用内容来解答这个问题。 首先,用户之前的问题是关于.cmake文件的用途和使用,我在回答中提到了find_package会查找两种文件:<Package>Config.cmakeFind<Package>.cmake。现在用户明确询问这两者的区别,需要详细解释它们的用途和适用场景。 根据引用中的内容,比如引用[3]提到了OpenCV_DIR变量会影响查找,而引用[4]中的例子使用了-P参数执行脚本。这可能涉及到包配置文件和模块文件的调用方式不同。 首先,xxx-config.cmake通常是由项目自身提供的,安装时生成,包含包的路径、库、依赖等信息。而Findxxx.cmake则是用户或CMake社区编写的,用于查找系统上的包。比如FindOpenCV.cmake可能需要用户自己写,但如果是OpenCV提供的OpenCVConfig.cmake,就属于前者。 另外,引用[3]中提到OpenCV_DIR变量有效,说明当使用Config模式时,可以通过设置xxx_DIR变量来指定路径。而Find模块可能依赖CMAKE_PREFIX_PATH等变量。 还需要注意,find_package有两种模式:Module模式和Config模式。Module模式查找Findxxx.cmake,而Config模式查找xxx-config.cmake。优先级可能不同,通常Module先,但用户可以通过指定参数改变。 用户可能的应用场景是,当使用第三方库时,如果该库支持CMake并安装了Config文件,那么优先使用Config模式;否则需要自己编写或使用现有的Find模块。 此外,引用[2]中的CMakeLists.txt示例展示了如何添加子目录,可能和包管理有关联,但当前问题更关注配置文件类型。 需要确保回答结构清晰,区分两者的定义、生成方式、使用场景以及调用方式。同时,结合用户提供的引用,比如-P参数处理脚本,但这个问题可能更多涉及包查找机制。 最后,检查是否有遗漏点,比如变量作用域、CMAKE_MODULE_PATH的设置,或者调试方法,但用户的问题主要集中在区别和用途,所以可能需要集中在这些方面。</think>### xxx-config.cmake 与 xxx.cmake 文件的区别及用途 #### 一、核心区别 1. **设计目的** - **`<Package>Config.cmake`**:由库的开发者提供,随库**安装时自动生成**,用于声明库的安装路径、编译选项、依赖关系等元信息(称为**Config模式**)[^3]。 - **`Find<Package>.cmake`**:由用户或CMake社区编写,用于**手动查找系统环境中的库**(称为**Module模式**),例如`FindOpenCV.cmake`[^4]。 2. **文件生成方式** ```cmake # 库的CMakeLists.txt中生成Config文件 install(EXPORT MyLibTargets FILE MyLibConfig.cmake DESTINATION lib/cmake/MyLib) ``` 3. **搜索优先级** - `find_package(Package)`默认**优先查找`FindPackage.cmake`**(Module模式) - 若指定`CONFIG`参数(如`find_package(Package CONFIG)`),则**仅查找`PackageConfig.cmake`** --- #### 二、典型使用场景对比 | **场景** | **Config文件** | **Find模块** | |-------------------------|----------------------------------------|-----------------------------------| | **库的开发者** | 必须提供 | 不需要提供 | | **库的安装路径** | 通过`<Package>_DIR`指定(如OpenCV_DIR)[^3] | 通过`CMAKE_PREFIX_PATH`指定[^3] | | **跨平台兼容性** | 依赖库的安装规范 | 需手动处理路径差异 | | **依赖传递** | 自动解析目标依赖(通过`target_link_libraries`) | 需手动设置变量(如`XXX_INCLUDE_DIR`) | --- #### 三、具体调用示例 1. **Config模式** 假设已安装OpenCV并设置`OpenCV_DIR`环境变量: ```cmake find_package(OpenCV REQUIRED CONFIG) # 强制使用Config模式 target_link_libraries(myapp PRIVATE OpenCV::opencv_core) ``` 2. **Module模式** 手动编写`FindMyLib.cmake`并设置搜索路径: ```cmake list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") find_package(MyLib REQUIRED) # 自动调用FindMyLib.cmake[^4] include_directories(${MYLIB_INCLUDE_DIRS}) ``` --- #### 四、关键技术细节 1. **变量定义差异** - Config文件通过`@PACKAGE_INIT@`宏定义变量(如`${Package}_LIBRARIES`) - Find模块需手动定义变量(如`FREETYPE_INCLUDE_DIRS`)[^3] 2. **目标导出机制** Config文件可使用**导入目标**(IMPORTED Targets),实现更安全的依赖管理: ```cmake # MyLibConfig.cmake add_library(MyLib::core STATIC IMPORTED) set_target_properties(MyLib::core PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include" IMPORTED_LOCATION "${_IMPORT_PREFIX}/lib/libmylib.a" ) ``` 3. **调试方法** 通过`--trace`参数可追踪文件加载过程: ```bash cmake --trace-expand .. # 显示Config/Find模块的加载细节[^4] ``` --- #### 五、选择建议 1. **优先使用Config模式** 若第三方库已提供`<Package>Config.cmake`(如Boost、Qt),直接通过`find_package()`调用。 2. **Fallback到Find模块** 对未提供Config文件的库(如旧版OpenCV),使用Find模块或自行编写。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值