Cmake命令之find_file

find_file命令按照全路徑查找指定的文件,并將查找的結(jié)果存儲在<VAR>中,如果指定了NO_CACHE選項,那么<VAR>是一個普通變量,否則<var>是一個緩存條目(會存儲在CMakeCache.txt文件中)。
??如果找到了指定的文件,那么結(jié)果存入<VAR>中,除非<VAR>被清空,否則后續(xù)繼續(xù)調(diào)用find_file也不會再繼續(xù)查找;如果沒有找到文件,那么<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選項,查找路徑會稍微復雜一些,具體查找過程會在四、查找過程進行詳細介紹。

下面來看幾個例子(所有例子的運行環(huán)境為macOS),例子中使用的文件或目錄樹結(jié)構(gòu)為:

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

  1. 使用速記命令格式查找文件,注意指定查找路徑和未指定查找路徑的區(qū)別。
# 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}")

執(zhí)行cmake .運行結(jié)果:
myfile1 find result: result-NOTFOUND
myfile2 find result: result2-NOTFOUND
myfile3 find result in ./path: /XXX/YYY/ZZZ/path/myfile2

  1. 使用通用命令格式查找文件,可以指定多個查找目標。當指定查找多個目標時,查找結(jié)果<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}")

執(zhí)行cmake .運行結(jié)果為:
find result: find_result1-NOTFOUND
find result: /XXX/YYY/ZZZ/path/myfile2
find result: /XXX/YYY/ZZZ/myfile1

三、更多命令參數(shù)詳解

  • <vAR>:存儲命令運行的結(jié)果,默認是緩存條目,當指定NO_CACHE選項時為普通變量。
  • NAMES:指定一個或多個待查找的文件。
  • <HINTS>、<PATHS>:添加搜索路徑,ENV var子選項將會從系統(tǒng)環(huán)境變量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來添加一個臨時的環(huán)境變量,注意/XXX/YYY/ZZZ是我本地運行的路徑,需要根據(jù)實際情況指定。
export MY_TEST_PATH=/XXX/YYY/ZZZ/path
cmake .

運行結(jié)果:
find file from /XXX/YYY/ZZZ/path: /XXX/YYY/ZZZ/path/myfile2

  • PATH_SUFFIXES:為每一個搜索路徑添加一個后綴的子目錄,在新生成的路徑下也進行查找,注意原有的搜索路徑也會進行查找。假定我們的搜索路徑是/XXX/YYY/ZZZ/XXX/YYY/WWW,當指定PATH_SUFFIXES path后,搜索路徑就變?yōu)椋?code>/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}")

執(zhí)行cmake .運行結(jié)果:
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}")

執(zhí)行cmake .后輸出結(jié)果為:
find result: /XXX/YYY/ZZZ/path/myfile2

那么指定的文檔字符串描述在哪兒呢?結(jié)合<VAR>必須是緩存條目才有效的信息,那么可以在執(zhí)行cmake .后,在CMakeCache.txt文件中找到它,我本地執(zhí)行cat CMakeCache.txt | grep "myfile2”的結(jié)果為:
//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選項),命令執(zhí)行結(jié)果會緩存到CMakeCache.txt文件中,因此只要find_file找到了指定的文件,后續(xù)再次調(diào)用find_file不會再執(zhí)行查找過程,即使重新執(zhí)行cmake .find_file也不會再執(zhí)行查找過程,而是從CMakeCache.txt文件中加載查找結(jié)果。
    ??2. 當指定了NO_CACHE選項,在本次構(gòu)建過程中,只要find_file找到了指定文件,后續(xù)的find_file的調(diào)用不會再執(zhí)行查找過程,但是重新執(zhí)行一次構(gòu)建,又會再次查找。
    ??需要注意的一點是,NO_CACHE會導致每次構(gòu)建都會重新查找,增加構(gòu)建時間消耗。
# 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) # 在前面已經(jīng)查找到myfile1的情況下,再次調(diào)用,實際上不會執(zhí)行查找過程了
message("second find result: ${result_no_cache}")

執(zhí)行cmake .的結(jié)果:
first find result: /XXX/YYY/ZZZ/myfile1
second find result: /XXX/YYY/ZZZ/myfile1

說明:只要找到了指定文件,后續(xù)調(diào)用find_file不會再執(zhí)行。
此時我們刪除myfile1文件,再次執(zhí)行cmake .:
rm myfile1
cmake .
運行結(jié)果為:
first find result: result_no_cache-NOTFOUND
second find result: /XXX/YYY/ZZZ/myfile2

說明:重新運行cmake .進行構(gòu)建,普通變量不會保存在CMakeCache.txt中,因此會重新執(zhí)行查找過程

# 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) # 在前面已經(jīng)查找到myfile1的情況下,再次調(diào)用,實際上不會執(zhí)行查找過程了
message("second find result: ${result_cache}")

執(zhí)行cmake .的結(jié)果:
first find result: /XXX/YYY/ZZZ/myfile1
second find result: /XXX/YYY/ZZZ/myfile1

說明:只要找到了指定文件,后續(xù)調(diào)用find_file不會再執(zhí)行。
此時我們刪除myfile1文件,再次執(zhí)行cmake .:
rm myfile1
cmake .
運行結(jié)果為:
****first find result: /XXX/YYY/ZZZ/myfile1
second find result: /XXX/YYY/ZZZ/myfile1***
說明:與第一次執(zhí)行結(jié)果一致,說明緩存條目會存儲在CMakeCache.txt中,即使重復執(zhí)行構(gòu)建過程,也不會再次執(zhí)行查找,除非刪除CMakeCache.txt文件,我們可以通過查看CMakeCache.txt文件,看到result_cache變量:
cat CMakeCache.txt | grep "result_cache"
result_cache:FILEPATH=/XXX/YYY/ZZZ/myfile1

  • REQUIRED:在3.18版本引入。當指定了該選項,如果未找到執(zhí)行的文件,那么構(gòu)建過程會終止,并輸出錯誤信息,否則會在再次調(diào)用find_file的時候重新查找指定文件。
# CMakeLists.txt
find_file(result NAMES noexistfile PATHS ./ REQUIRED)
message("result: ${result}")

執(zhí)行cmake .運行結(jié)果:
CMake Error at CMakeLists.txt:6 (find_file):
Could not find result using the following files: noexistfile

四、查找過程

當沒有指定NO_DEFAULT選項時,搜索過程會按照如下順序進行:

  1. 如果是從find模塊或者通過find_package<PackageName>命令調(diào)用進來,那么首先會在CMake變量<PackageName>_ROOT和環(huán)境變量<PackageName>_ROOT進行查找。如果find模塊的調(diào)用是嵌套的,那么會先在當前的模塊中的<PackageName>_ROOTENV{<PackageName>_ROOT}進行查找,然后在父模塊的<PackageName>_ROOTENV{<PackageName>_ROOT}進行查找。這個是隨著3.12版本中引入??梢酝ㄟ^NO_PACKAGE_ROOT_PATH選項或者將CMAKE_FIND_USE_PACKAGE_ROOT_PATH變量設(shè)置為FALSE來跳過這以查找過程。
    對于每一個查找路徑<prefix>,還會添加include子目錄進行查找(也就是查找<prefix>/include)。此外如果設(shè)置了CMAKE_LIBRARY_ARCHITECTURE變量值為<arch>,也會將include/<arch>子目錄添加到查找路徑,也就是對<prefix>/include/<arch>進行查找。

  2. 通過命令行傳入CMake變量指定的路徑,一般是cmake . -DVAR=value這種形式傳入,如果有多個路徑,使用分號進行分隔??梢酝ㄟ^設(shè)置NO_CMAKE_PATH選項,或者將CMAKE_FIND_USE_CMAKE_PATH變量設(shè)置為FALSE來跳過這一查找過程。
    VAR有如下幾個取值:
    1)CMAKE_PREFIX_PATH:以CMAKE_PREFIX_PATH指定的路徑作為前綴<prefix>,include子目錄也會被添加到查找路徑中,也就是<prefix>/include;特別的,如果設(shè)置了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,目錄結(jié)構(gòu)如下:

├── 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}")

執(zhí)行cmake . -DCMAKE_PREFIX_PATH=./結(jié)果如下,可以看到include子目錄也會被添加到查找路徑中:
result: /XXX/YYY/ZZZ/myfile1
result2: /XXX/YYY/ZZZ/include/myfile3
result3: result3-NOTFOUND

執(zhí)行cmake . -DCMAKE_PREFIX_PATH=./ -DCMAKE_LIBRARY_ARCHITECTURE=x64,本次同時也傳遞了CMAKE_LIBRARY_ARCHITECTURE變量,結(jié)果如下,可以看到include子目錄和include/x64子目錄都被添加到了查找路徑:
result: /XXX/YYY/ZZZ/myfile1
result2: /XXX/YYY/ZZZ/include/myfile3
result3: /XXX/YYY/ZZZ/include/x64/myfile4

3. 查找CMake環(huán)境變量指定的路徑,如果要指定多個路徑,可以通過分號分隔。具體的環(huán)境變量取值和搜索方式與步驟2中一致,此處不在贅述,以CMAKE_INCLUDE_PATH包含當前目錄作為搜索目錄進行說明。

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

執(zhí)行如下命令:
export CMAKE_INCLUDE_PATH=./ # 設(shè)置環(huán)境變量
cmake .

執(zhí)行結(jié)果如下:
result: /XXX/YYY/ZZZ/myfile1

4. 搜索HINTS選項指定的路徑。

5. 搜索系統(tǒng)的標準環(huán)境變量??梢酝ㄟ^設(shè)置NO_SYSTEM_ENVIRONMENT_PATH選項或者將CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH變量設(shè)置為FALSE來跳過該搜索步驟。例如PATH環(huán)境變量,在我的macOS系統(tǒng)中,echo ${PATH}可以得到如下輸出:

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

6. 搜索當前系統(tǒng)的平臺文件定義的CMake變量。包含CMAKE_SYSTEM_PREFIX_PATHCMAKE_SYSTEM_INCLUDE_PATH、CMAKE_SYSTEM_FRAMEWORK_PATH三個。可以通過設(shè)置NO_CMAKE_SYSTEM_PATH選項或者將CMAKE_FIND_USE_CMAKE_SYSTEM_PATH變量設(shè)置為FALSE來跳過這一查找過程。在我的macOS系統(tǒng)中,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為空。此外使用的默認順序為:優(yōu)先使用CMAKE_FIND_ROOT_PATH,然后使用CMAKE_SYSROOT??梢酝ㄟ^CMAKE_FIND_ROOT_PATH_MODE_INCLUDE變量進行調(diào)整:值為ONLY表明只使用CMAKE_FIND_ROOT_PATH;值為NEVER表明只使用CMAKE_SYSROOT;值為BOTH則會使用兩個變量。也可以通過find_file的三個選項進行調(diào)整:

  • 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
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容