首先为什么要写 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