
find_package 用于查找包(通常是使用三方库),并返回关于包的细节(使用包所依赖的头文件、库文件、编译选项、链接选项等)
与 find_libaray 直接在指定搜索目录下搜索库不同, find_package 命令可以获取更多的信息,那么它的搜索方式也是与 find_libaray 不一样,它有两种不同的搜索方式,因此在介绍这个命令的细节之前,先简单介绍一下 find_package 命令的两种搜索模式: 模块模式 ( Module mode )和 配置模式 ( Config mode )。
在该模式下, Cmake 会搜索一个名为 Find<PackageName>.cmake 的文件,其中 <PackageName>为待搜索包的名称。
搜索路径的顺序依次是:
如果找到文件 Find<PackageName>.cmake , Cmake 会读取并处理该文件,简而言之,它负责检查一些条件(如版本号是否满足等)是否满足,并在找到包后,返回给调用者一些变量,用以获取包的详细信息。
一般来说, Find<PackageName>.cmake 文件不是随包本身一起提供的,更多的是外部针对已有包的重新包装,例如 *** 作系统、 Cmake 程序、甚至是调用 find_package 命令的工程针对已有的包提供针对该包的 .cmake 文件。
该模式下, CMake 会搜索 <lowercasePackageName>-config.cmake 文件或 <PackageName>Config.cmake 文件。如果 find_package 命令中指定了具体的版本,也会搜索 <lowercasePackageName>-config-version.cmake 或 <PackageName>ConfigVersion.cmake 文件,因此配置模式下通常会提供配置文件和版本文件(注意形式上要保持一致),并且作为包的一部分一起提供给使用者。
该模式下对 .cmake 文件的搜索路径的顺序比较复杂,具体见本文的 4.1 节。
find_package 命令有两种格式, 基本命令格式 和 完整命令格式 。
几个重要的参数介绍:
这里介绍一下与基本命令有差异的地方:
<PackageName>_FOUND 变量用来表示包是否找到, True 表示包找到了, False 表示未找到满足条件的包。如果包被找到,那么还会提供其他与这个包相关的变量供调用者使用,例如包的头文件、库文件等。这些变量都是以 <PackageName>_ 开头的,具体的命名格式请参考 Cmake中find_package命令的搜索模式之模块模式(Module mode) 的 四、对标准变量名称的更多说明 章节。
搜索模式有两种:模块模式和配置模式。命令有两种形式:基本命令和完整命令。他们之间的关系是:
我们将以两个例子分别展示两种搜索模式。本例中会利用我自己系统( macOS )已经安装的库 LibLZMA (如何编写自己的库并让 find_package 两种模式能搜索到,请参考另外两篇文章看 Cmake中find_package命令的搜索模式之模块模式(Module mode) 和 Cmake中find_package命令的搜索模式之配置模式(Config mode) ,尝试搜索这个库,并利用这个库提供的接口 lzma_version_string (在头文件 lzma.h 中提供)来获取它的版本号,并打印出来,测试程序如下:
模块模式的 CMakeLists.txt 内容如下:
由于 lzma 库本身未提供 lzmaConfig.cmake ,我们简单的编写一个,内容就是为 find_package 提供 lzma 库所在的头文件和库文件,并在 find_package 中指定查找该 .cmake 所在的路径:
配置模式的 CMakeLists.txt 内容如下:
CMake 会从如下从几个目录中取搜索配置文件,下面列出了将会搜索的目录,每一个目录后面通过字母来标记不同的 *** 作系统( W 表示 Windows , U 表示 UNIX , A 表示 Apple ),目录中的 <prefix>是目录的前缀,将在 4.1.2 介绍是怎么生成的:
在支持 macOS 的 FRAMEWORK 和 BUNDLE 系统中,会搜索如下框架和应用程序包目录是否包含配置文件:
上面列举的目录中, <name>是大小写不敏感的,并且会跟 <PackageName>或者 NAMES 指定的名字进行匹配。
CMAKE_LIBRARY_ARCHITECTURE 变量指定的时候,也会搜索 lib/<arch>相关的路径,会按照如下顺序搜索:
可以通过 PATH_SUFFIXES 变量指定搜索路径的后缀,会在上述的每一个路径中都添加后缀路径进行查找。
如果 NO_DEFAULT_PATH 选项指定了的话,那么所有以 NO_* 开头的命令都会使能, <prefix>的查找顺序依次如下:
CMAKE_FIND_ROOT_PATH 用于指定搜索的根路径。
在 find_package 命令调用之前设置 CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS 为 TRUE ,这样如果查找到的路径是一个符号链接,会将符号链接对应的真实路径存起来。
当指定 version 参数,配置模式将仅会查找能兼容指定版本的包,如果指定了 EXACT ,则只会查找精确匹配指定版本的包。 CMake 本身不会对版本号做任何转换,而是通过查找到包的版本校验文件(包自身提供的) <PackageName>ConfigVersion.cmake (或 <PackageName>-config-version.cmake ),调用版本配置文件做校验,版本配置文件可以通过 CMakePackageConfigHelpers 模块来辅助创建。可以参考 Cmake中find_package命令的搜索模式之配置模式(Config mode) 中的例子。
当 find_package 命令中指定 version 参数后,会把 version 参数分解出来,赋值到 PACKAGE_FIND_XXX 中,供版本配置文件校验版本号使用,具体赋值的变量如下:
当指定的版本是一个范围时,上述变量会存放范围中较小的那个版本号,这个主要是为了保证对没有实现版本范围的兼容,此外,也会赋值如下变量:
当版本配置文件完成版本校验后,会设置如下 PACKAGE_VERSION_XXX 变量供 find_package 使用,具体的变量如下:
上面的 PACKAGE_VERSION_XXX 几个变量仅用于 find_package 命令检查配置文件是否提供了一个可接受的版本,一旦 find_package 命令返回后,这些变量就失效了。如果版本校验通过,那么如下 <PackageName>_VERSION_XXX 变量会被设置,供 find_package 调用者使用:
首先说加载一个\//\模块。
1.这个函数\//\首先查找 package.loaded 表, 检测 modname 是否被加载过。 如果被加载过,require 返回 package.loaded[modname] 中保存的值。 否则,它试着为模块寻找 加载器 。
requi\//\re 遵循 package.searchers 序列的指引来查找加载器。 如果改变这个序列,我们可以改变 require 如何查找一个模块。 下列说明基于 package.searchers 的默认配置。
首先 re\//\quire 查找 package.pre\//\load[modname] 。 如果这里有一个值,这个值(必须是一个函数)就是那个加载器。 否则 require 使\//\用 Lua 加载器去查找 package.path 的路径。 如果查找失败,接着使用 C 加载\//\器去查找 package.cpath 的路径。 如果都失败了,再尝试 一体化 加载器 ()。
2.每次找到一个加载器,require 都用两个参数调用加载器: modname 和一个在获取加载器过程中得到的参数。 (如果通过查找文件得到的加\//\载器,这个额外参数是文件名。) 如果加载器返\//\回非空值, require 将这个值赋\//\给 package.loaded[modname]。 如果加载器没能返回一个非空值用\//\于赋给 package.loaded[modname], require 会在那里设入 true 。 无论是什么情况,require 都会返回 package.loaded[modname] 的最终值。
3.如果在加载或运行模块时有错误, 或是无法为模块找到加载器, require 都会抛出错误。
查找器查找模块 fo\//\o 会依次尝试打开文件 ./foo.so,./foo.dll, 以及 /usr/local/foo/init.so。 一旦它找到一个 C 库, 查\//\找器首先使用动态链接机制连接该库。 然后尝试在该库中找到可以用作加载器的 C 函数。 这个 C 函数的名字是 "luaopen_" 紧接模块名的字符串, 其\//\中字符串中所有的下划线都会被替换成点。 此外,如\//\果模块名中有横线, 横线后面的部分(包括\//\横线)都被去掉。 例如,如果\//\模块名为 a.b.c-v2.1, 函数名就是 luaopen_a_b_c。
4.第四个搜索器是 一体化加载器。 它从 C \//\路径中查找指定模块的根名字。 例如,当请求 a.b.c 时, \//\它将查找 a 这个 C 库。 如果找得到,它\//\会在里面找子模块的加载函数。 在我们的例子中,就是找 luaopen_\//\a_b_c。 利用这个机制,可以把若干 \//\C 子模块打包进单个库。 每个子模块都可以有原本的加载函数名。
除了第一个\//\(预加载)搜索器外,每\//\个搜索器都会返回 它\//\找到的模\//\块的文件名。 这和 package.sear\//\chpath 的返回值一样。 第一个搜索器没有返回值。
5.这张表内的每一项\//\都是一个 查找器函数。 当查找一个模块时, require 按次序调用这些查找器, 并传\//\入模块名(requi\//\re 的参数)作为唯一的一个参数\//\。 此函数可以返回另一个函数(模块的 加载器)加上另一个将传\//\递给这个加载器的参数。 \//\或是返回一个描述为何没有找到这个模\//\块的字符串 (或是返回 nil 什么也不想说)。
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)