美文网首页
Cmake命令之find_file

Cmake命令之find_file

作者: Domibaba | 来源:发表于2023-08-25 18:29 被阅读0次

    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

    三、更多命令参数详解

    • <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选项时,搜索过程会按照如下顺序进行:

    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的三个选项进行调整:

    • 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为根的目录。

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

    相关文章

      网友评论

          本文标题:Cmake命令之find_file

          本文链接:https://www.haomeiwen.com/subject/ecqpmdtx.html