Cmake命令之find_file

2023-08-25  本文已影响0人  Domibaba

find_file命令按照全路径查找指定的文件,并将查找的结果存储在<VAR>中,如果指定了NO_CACHE选项,那么<VAR>是一个普通变量,否则<var>是一个缓存条目(会存储在CMakeCache.txt文件中)。
  如果找到了指定的文件,那么结果存入<VAR>中,除非<VAR>被清空,否则后续继续调用find_file也不会再继续查找;如果没有找到文件,那么<VAR>的值为<var>_NOTFOUND

一、命令格式

find_file命令有两种格式,速记的格式为:

find_file (<VAR> name1 [path1 path2 ...])

通用的格式为:

find_file (
          <VAR>
          name | NAMES name1 [name2 ...]
          [HINTS [path | ENV var]... ]
          [PATHS [path | ENV var]... ]
          [PATH_SUFFIXES suffix1 [suffix2 ...]]
          [DOC "cache documentation string"]
          [NO_CACHE]
          [REQUIRED]
          [NO_DEFAULT_PATH]
          [NO_PACKAGE_ROOT_PATH]
          [NO_CMAKE_PATH]
          [NO_CMAKE_ENVIRONMENT_PATH]
          [NO_SYSTEM_ENVIRONMENT_PATH]
          [NO_CMAKE_SYSTEM_PATH]
          [CMAKE_FIND_ROOT_PATH_BOTH |
           ONLY_CMAKE_FIND_ROOT_PATH |
           NO_CMAKE_FIND_ROOT_PATH]
         )

二、命令使用举例

未指定NO_DEFAULT_PATH选项的情况下,find_file会以CMAKE_FIND_ROOT_PATHCMAKE_SYSROOTCMake变量(默认值为空)指定的路径为根路径,与PATHSHINTS指定路径拼接成全路径的进行查找;若指定了NO_DEFAULT_PATH选项,查找路径会稍微复杂一些,具体查找过程会在四、查找过程进行详细介绍。

下面来看几个例子(所有例子的运行环境为macOS),例子中使用的文件或目录树结构为:

├── CMakeLists.txt
├── myfile1
└── path
    └── myfile2

  1. 使用速记命令格式查找文件,注意指定查找路径和未指定查找路径的区别。
# CMakeLists.txt
find_file(result myfile1) # 未指定查找路径,因为默认根路径变量为空,因此无法找到
message("myfile1 find result: ${result}")
find_file(result2 myfile2) # 未指定查找路径,因为默认根路径变量为空,因此无法找到
message("myfile2 find result: ${result2}") 
find_file(result3 myfile2 ./path) # 指定查找路径,能查找到
message("myfile3 find result in ./path: ${result3}")

执行cmake .运行结果:
myfile1 find result: result-NOTFOUND
myfile2 find result: result2-NOTFOUND
myfile3 find result in ./path: /XXX/YYY/ZZZ/path/myfile2

  1. 使用通用命令格式查找文件,可以指定多个查找目标。当指定查找多个目标时,查找结果<VAR>存储的是第一个找到的文件,因此,只有当所有指定的文档都无法找到,<VAR>的值才会赋值为<VAR>_NOTFOUND
# CMakeLists.txt
find_file(find_result1 NAMES myfile1 myfile2)
message("find result: ${find_result1}")
find_file(find_result2 NAMES myfile1 myfile2 PATHS ./path NO_CACHE)
message("find result: ${find_result2}")
find_file(find_result3 NAMES myfile1 myfile2 PATHS . ./path NO_CACHE)
message("find result: ${find_result3}")

执行cmake .运行结果为:
find result: find_result1-NOTFOUND
find result: /XXX/YYY/ZZZ/path/myfile2
find result: /XXX/YYY/ZZZ/myfile1

三、更多命令参数详解

# CMakeLists.txt
find_file(find_from_env NAMES myfile2 PATHS ENV MY_TEST_PATH)
message("find file from $ENV{MY_TEST_PATH}: ${find_from_env}")

在运行cmake .命令之前,我们先使用export MY_TEST_PATH=/XXX/YYY/ZZZ/path来添加一个临时的环境变量,注意/XXX/YYY/ZZZ是我本地运行的路径,需要根据实际情况指定。
export MY_TEST_PATH=/XXX/YYY/ZZZ/path
cmake .

运行结果:
find file from /XXX/YYY/ZZZ/path: /XXX/YYY/ZZZ/path/myfile2

# CMakeLists.txt
find_file(find_result_suffix NAMES myfile2 PATHS . PATH_SUFFIXES path) # 本例中,如果不指定PATH_SUFFIXES,myfile2无法搜索到
message("find result: ${find_result_suffix}")

执行cmake .运行结果:
find result: /XXX/YYY/ZZZ/path/myfile2

# CMakeLists.txt
find_file(find_result_doc NAMES myfile2 PATHS ./path DOC "myfile2 is a test file for find_file command")
message("find result: ${find_result_doc}")

执行cmake .后输出结果为:
find result: /XXX/YYY/ZZZ/path/myfile2

那么指定的文档字符串描述在哪儿呢?结合<VAR>必须是缓存条目才有效的信息,那么可以在执行cmake .后,在CMakeCache.txt文件中找到它,我本地执行cat CMakeCache.txt | grep "myfile2”的结果为:
//myfile2 is a test file for find_file command
find_result_doc:FILEPATH=/Users/shengyi/mycode/leetcode/mytest/find_file/path/myfile2

# CMakeLists.txt
# NO_CACHE的使用
find_file(result_no_cache NAMES myfile1 PATHS ./ NO_CACHE) # 首次查找myfile1
message("first find result: ${result_no_cache}")
find_file(result_no_cache NAMES myfile2 PATHS ./path NO_CACHE) # 在前面已经查找到myfile1的情况下,再次调用,实际上不会执行查找过程了
message("second find result: ${result_no_cache}")

执行cmake .的结果:
first find result: /XXX/YYY/ZZZ/myfile1
second find result: /XXX/YYY/ZZZ/myfile1

说明:只要找到了指定文件,后续调用find_file不会再执行。
此时我们删除myfile1文件,再次执行cmake .:
rm myfile1
cmake .
运行结果为:
first find result: result_no_cache-NOTFOUND
second find result: /XXX/YYY/ZZZ/myfile2

说明:重新运行cmake .进行构建,普通变量不会保存在CMakeCache.txt中,因此会重新执行查找过程

# CMakeLists.txt
# 未使用NO_CACHE
find_file(result_cache NAMES myfile1 PATHS ./) # 首次查找myfile1
message("first find result: ${result_cache}")
find_file(result_cache NAMES myfile2 PATHS ./path) # 在前面已经查找到myfile1的情况下,再次调用,实际上不会执行查找过程了
message("second find result: ${result_cache}")

执行cmake .的结果:
first find result: /XXX/YYY/ZZZ/myfile1
second find result: /XXX/YYY/ZZZ/myfile1

说明:只要找到了指定文件,后续调用find_file不会再执行。
此时我们删除myfile1文件,再次执行cmake .:
rm myfile1
cmake .
运行结果为:
****first find result: /XXX/YYY/ZZZ/myfile1
second find result: /XXX/YYY/ZZZ/myfile1***
说明:与第一次执行结果一致,说明缓存条目会存储在CMakeCache.txt中,即使重复执行构建过程,也不会再次执行查找,除非删除CMakeCache.txt文件,我们可以通过查看CMakeCache.txt文件,看到result_cache变量:
cat CMakeCache.txt | grep "result_cache"
result_cache:FILEPATH=/XXX/YYY/ZZZ/myfile1

# CMakeLists.txt
find_file(result NAMES noexistfile PATHS ./ REQUIRED)
message("result: ${result}")

执行cmake .运行结果:
CMake Error at CMakeLists.txt:6 (find_file):
Could not find result using the following files: noexistfile

四、查找过程

当没有指定NO_DEFAULT选项时,搜索过程会按照如下顺序进行:

  1. 如果是从find模块或者通过find_package<PackageName>命令调用进来,那么首先会在CMake变量<PackageName>_ROOT和环境变量<PackageName>_ROOT进行查找。如果find模块的调用是嵌套的,那么会先在当前的模块中的<PackageName>_ROOTENV{<PackageName>_ROOT}进行查找,然后在父模块的<PackageName>_ROOTENV{<PackageName>_ROOT}进行查找。这个是随着3.12版本中引入。可以通过NO_PACKAGE_ROOT_PATH选项或者将CMAKE_FIND_USE_PACKAGE_ROOT_PATH变量设置为FALSE来跳过这以查找过程。
    对于每一个查找路径<prefix>,还会添加include子目录进行查找(也就是查找<prefix>/include)。此外如果设置了CMAKE_LIBRARY_ARCHITECTURE变量值为<arch>,也会将include/<arch>子目录添加到查找路径,也就是对<prefix>/include/<arch>进行查找。

  2. 通过命令行传入CMake变量指定的路径,一般是cmake . -DVAR=value这种形式传入,如果有多个路径,使用分号进行分隔。可以通过设置NO_CMAKE_PATH选项,或者将CMAKE_FIND_USE_CMAKE_PATH变量设置为FALSE来跳过这一查找过程。
    VAR有如下几个取值:
    1)CMAKE_PREFIX_PATH:以CMAKE_PREFIX_PATH指定的路径作为前缀<prefix>include子目录也会被添加到查找路径中,也就是<prefix>/include;特别的,如果设置了CMAKE_LIBRARY_ARCHITECTURE变量<arch>,那么<prefix>/include/<arch>也会被添加到查找路径中;
    2)CMAKE_INCLUDE_PATH
    3)CMAKE_FRAMEWORK_PATH
    我们以CMAKE_PREFIX_PATH举例说明,在CMakeLists.txt所在的目录,新建文件myfile1,新建目录include和目录include/x64,并在这两个目录下分别建立文件include/myfile3include/x64/myfile4,目录结构如下:

├── CMakeLists.txt
├── Makefile
├── include
│   ├── myfile3
│   └── x64
│       └── myfile4
├── myfile1

# CMakeLists.txt
find_file(result NAMES myfile1)
message("result: ${result}")

find_file(result2 NAMES myfile3)
message("result2: ${result2}")

find_file(result3 NAMES myfile4)
message("result3: ${result3}")

执行cmake . -DCMAKE_PREFIX_PATH=./结果如下,可以看到include子目录也会被添加到查找路径中:
result: /XXX/YYY/ZZZ/myfile1
result2: /XXX/YYY/ZZZ/include/myfile3
result3: result3-NOTFOUND

执行cmake . -DCMAKE_PREFIX_PATH=./ -DCMAKE_LIBRARY_ARCHITECTURE=x64,本次同时也传递了CMAKE_LIBRARY_ARCHITECTURE变量,结果如下,可以看到include子目录和include/x64子目录都被添加到了查找路径:
result: /XXX/YYY/ZZZ/myfile1
result2: /XXX/YYY/ZZZ/include/myfile3
result3: /XXX/YYY/ZZZ/include/x64/myfile4

3. 查找CMake环境变量指定的路径,如果要指定多个路径,可以通过分号分隔。具体的环境变量取值和搜索方式与步骤2中一致,此处不在赘述,以CMAKE_INCLUDE_PATH包含当前目录作为搜索目录进行说明。

# CMakeLists.txt
find_file(result NAMES myfile1)
message("result: ${result}")

执行如下命令:
export CMAKE_INCLUDE_PATH=./ # 设置环境变量
cmake .

执行结果如下:
result: /XXX/YYY/ZZZ/myfile1

4. 搜索HINTS选项指定的路径。

5. 搜索系统的标准环境变量。可以通过设置NO_SYSTEM_ENVIRONMENT_PATH选项或者将CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH变量设置为FALSE来跳过该搜索步骤。例如PATH环境变量,在我的macOS系统中,echo ${PATH}可以得到如下输出:

/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin

6. 搜索当前系统的平台文件定义的CMake变量。包含CMAKE_SYSTEM_PREFIX_PATHCMAKE_SYSTEM_INCLUDE_PATHCMAKE_SYSTEM_FRAMEWORK_PATH三个。可以通过设置NO_CMAKE_SYSTEM_PATH选项或者将CMAKE_FIND_USE_CMAKE_SYSTEM_PATH变量设置为FALSE来跳过这一查找过程。在我的macOS系统中,CMake中打印出CMAKE_SYSTEM_FRAMEWORK_PATH的输出如下:

# CMakeLists.txt
message("system: ${CMAKE_SYSTEM_FRAMEWORK_PATH}")

输出:
system: ~/Library/Frameworks;/Applications/Xcode.app/Contents/Developer/Library/Frameworks;/Library/Frameworks;/Network/Library/Frameworks;/System/Library/Frameworks

7. 搜索PATHS选项指定的路径。

最后对CMAKE_FIND_ROOT_PATHCMAKE_SYSROOT两个CMake变量做一个简短的说明。这两个变量都可以将指定的变量作为待搜索路径的根目录前缀,默认情况下CMAKE_FIND_ROOT_PATH为空。此外使用的默认顺序为:优先使用CMAKE_FIND_ROOT_PATH,然后使用CMAKE_SYSROOT。可以通过CMAKE_FIND_ROOT_PATH_MODE_INCLUDE变量进行调整:值为ONLY表明只使用CMAKE_FIND_ROOT_PATH;值为NEVER表明只使用CMAKE_SYSROOT;值为BOTH则会使用两个变量。也可以通过find_file的三个选项进行调整:


附录:参考文档
  1. https://cmake.org/cmake/help/latest/command/find_file.html
上一篇下一篇

猜你喜欢

热点阅读