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_PATH
、CMAKE_SYSROOT
等CMake
变量(默认值为空)指定的路径为根路径,与PATHS
或HINTS
指定路径拼接成全路径的进行查找;若指定了NO_DEFAULT_PATH
选项,查找路径会稍微复杂一些,具体查找过程会在四、查找过程
进行详细介绍。
下面来看几个例子(所有例子的运行环境为macOS
),例子中使用的文件或目录树结构为:
├── CMakeLists.txt
├── myfile1
└── path
└── myfile2
- 使用速记命令格式查找文件,注意指定查找路径和未指定查找路径的区别。
# 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
- 使用通用命令格式查找文件,可以指定多个查找目标。当指定查找多个目标时,查找结果
<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
三、更多命令参数详解
-
<vAR>
:存储命令运行的结果,默认是缓存条目,当指定NO_CACHE
选项时为普通变量。 -
NAMES
:指定一个或多个待查找的文件。 -
<HINTS>
、<PATHS>
:添加搜索路径,ENV var
子选项将会从系统环境变量var
获取搜索路径。
# 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
-
PATH_SUFFIXES
:为每一个搜索路径添加一个后缀的子目录,在新生成的路径下也进行查找,注意原有的搜索路径也会进行查找。假定我们的搜索路径是/XXX/YYY/ZZZ
和/XXX/YYY/WWW
,当指定PATH_SUFFIXES path
后,搜索路径就变为:/XXX/YYY/ZZZ
、/XXX/YYY/WWW
、/XXX/YYY/ZZZ/path
、/XXX/YYY/WWW/path
。
# 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
-
DOC
:为<VAR>
缓存条目指定文档字符串描述。注意,当<VAR>
是普通变量时(指定了NO_CACHE
选项),DOC
选项没有意义。
# 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
-
NO_CACHE
:该选项在3.21
版本引入。如果指定了NO_CACHE
选项,那么<VAR>
是普通变量,否则是缓存条目。
1. 默认情况下(未指定NO_CACHE
选项),命令执行结果会缓存到CMakeCache.txt
文件中,因此只要find_file
找到了指定的文件,后续再次调用find_file
不会再执行查找过程,即使重新执行cmake .
,find_file
也不会再执行查找过程,而是从CMakeCache.txt
文件中加载查找结果。
2. 当指定了NO_CACHE
选项,在本次构建过程中,只要find_file
找到了指定文件,后续的find_file
的调用不会再执行查找过程,但是重新执行一次构建,又会再次查找。
需要注意的一点是,NO_CACHE
会导致每次构建都会重新查找,增加构建时间消耗。
# 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
-
REQUIRED
:在3.18
版本引入。当指定了该选项,如果未找到执行的文件,那么构建过程会终止,并输出错误信息,否则会在再次调用find_file
的时候重新查找指定文件。
# 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
选项时,搜索过程会按照如下顺序进行:
-
如果是从
find
模块或者通过find_package<PackageName>
命令调用进来,那么首先会在CMake
变量<PackageName>_ROOT
和环境变量<PackageName>_ROOT
进行查找。如果find
模块的调用是嵌套的,那么会先在当前的模块中的<PackageName>_ROOT
和ENV{<PackageName>_ROOT}
进行查找,然后在父模块的<PackageName>_ROOT
和ENV{<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>
进行查找。 -
通过命令行传入
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/myfile3
和include/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_PATH
、CMAKE_SYSTEM_INCLUDE_PATH
、CMAKE_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_PATH
和CMAKE_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
的三个选项进行调整:
-
CMAKE_FIND_ROOT_PATH_BOTH
:按照上述的默认顺序搜索。 -
NO_CMAKE_FIND_ROOT_PATH
:不使用CMAKE_FIND_ROOT_PATH
。 -
ONLY_CMAKE_FIND_ROOT_PATH
:使用ONLY_CMAKE_FIND_ROOT_PATH
(该变量指定的目录及后缀目录不会添加CMAKE_FIND_ROOT_PATH
为根)下的目录,以及以CMAKE_FIND_ROOT_PATH
为根的目录。
网友评论