深入理解CMake(2):初步解讀Caffe的CMake腳本

預(yù)備說明

分析的是官方Caffe(https://github.com/BVLC/caffe)的CMake腳本,主要分析了根目錄的CMakeLists.txt。
Caffe代碼的commit id為99bd99795dcdf0b1d3086a8d67ab1782a8a08383

所謂CMake腳本這里指的是CMakeLists.txt和xxx.cmake的統(tǒng)稱。

$CAFFE_ROOT/CMakeLists.txt解讀

cmake_minimum_required(VERSION 2.8.7)

設(shè)定cmake最低版本。高版本cmake提供更多的功能(例如cmake3.13開始提供target_link_directories())或解決bug(例如OpenMP的設(shè)定問題),低版本有更好的兼容性。VERSION必須大寫,否則不識別而報錯。非必須但常規(guī)都寫。放在最開始一行。


if(POLICY CMP0046)
  cmake_policy(SET CMP0046 NEW)
endif()

cmake中也有if判斷語句,需要配對的endif()。
POLICY是策略的意思,cmake中的poilcy用來在新版本的cmake中開啟、關(guān)閉老版本中逐漸被放棄的功能特性:

Policies in CMake are used to preserve backward compatible behavior across multiple releases


project(Caffe C CXX)

project()指令,給工程起名字,很正常不過了。這列還寫明了是C/C++工程,其實沒必要寫出來,因為CMake默認(rèn)是開啟了這兩個的。
這句命令執(zhí)行后,自動產(chǎn)生了5個變量:

  • PROJECT_NAME,值等于Caffe
  • PROJECT_SOURCE_DIR,是CMakeLists.txt所在目錄,通常是項目根目錄(奇葩的項目比如protobuf,把CMakeLists.txt放在cmake子目錄的也有)
  • PROJECT_BINARY_DIR,是執(zhí)行cmake命令時所在的目錄,通常是build一類的用戶自行創(chuàng)建的目錄。
  • Caffe_SOURCE_DIR,此時同PROJECT_SOURCE_DIR
  • Caffe_BINARY_DIR,此時同PROJECT_BINARY_DIR
    官方cmake文檔對PROJECT_SOURCE_DIRPROJECT_BINARY_DIR的解釋很晦澀:
    image.png
image.png

自行實踐驗證下:


image.png

image.png


set(CAFFE_TARGET_VERSION "1.0.0" CACHE STRING "Caffe logical version")
set(CAFFE_TARGET_SOVERSION "1.0.0" CACHE STRING "Caffe soname version")

set()指令是設(shè)定變量的名字和取值,CACHE意思是緩存類型,是說在外部執(zhí)行CMake時可以臨時指定這一變量的新取值來覆蓋cmake腳本中它的取值:CMAKE -Dvar_name=var_value

而最后面的雙引號包起來的取值可以認(rèn)為是”注釋“。STRING是類型,不過據(jù)我目前看到和了解到的,CMake的變量99.9%是字符串類型,而且這個字符串類型變量和字符串?dāng)?shù)組類型毫無區(qū)分。

變量在定義的時候直接寫名字,使用它的時候則需要用${VAR_NAME}的形式。此外還可以使用系統(tǒng)的環(huán)境變量,形式為$ENV{ENV_VAR_NAME},例如$ENV{PATH},$ENV{HOME}等。

除了緩存變量,option()指令設(shè)定的東西也可以被用CMake -Dxxx=ON的形式來覆蓋。


add_definitions(-DCAFFE_VERSION=${CAFFE_TARGET_VERSION})

add_definitions()命令通常用來添加C/C++中的宏,例如:

  • add_defitions(-DCPY_ONLY) ,給編譯器傳遞了預(yù)定義的宏CPU_ONLY,相當(dāng)于代碼中增加了一句#define CPU_ONLY
  • add_defitions(-DMAX_PATH_LEN=256),則相當(dāng)于#define MAX_PATH_LEN 256
    根據(jù)文檔,實際上add_definitions()可以添加任意的編譯器flags,只不過像添加頭文件搜索路徑等flags被交給include_directory()等命令了。

在這里具體的作用是,設(shè)定CAFFE_VERSION這一C/C++宏的值為CAFFE_TARGET_VERSION變量的取值,而這一變量在前面分析過,它是緩存變量,有一個預(yù)設(shè)的默認(rèn)值,也可以通過cmake .. -DCAFFE_TARGET_VERSION=x.y.z來指定為x.y.z。


list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/Modules)

這里首先是list(APPEND VAR_NAME VAR_VALUE)這一用法,表示給變量VAR_NAME追加一個元素VAR_VALUE。雖然我寫成VAR_NAME,但前面有提到,cmake中的變量幾乎都是字符串或字符串?dāng)?shù)組,這里VAR_NAME你就當(dāng)它是一個數(shù)組就好了,而當(dāng)后續(xù)使用${VAR_NAME}時輸出的是”整個數(shù)組的值“。(吐槽:這不就是字符串么?為什么用list這個名字呢?搞得像是在寫不純正的LIPS)

具體的說,這里是把項目根目錄(CMakeLists.txt在項目根目錄,${PROJECT_SOURCE_DIR}表示CMakeLists.txt所在目錄)下的cmake/Modules子目錄對應(yīng)的路徑值,追加到CMAKE_MODULE_PATH中;CMAKE_MODULE_PATH后續(xù)可能被include()find_package()等命令所使用。


include(ExternalProject)
include(GNUInstallDirs)

include()命令的作用:

  • 包含文件,
  • 或者,包含模塊
    所謂包含文件,例如include(utils.cmake),把當(dāng)前路徑下的utils.cmake包含進(jìn)來,基本等同于C/C++中的#include指令。通常,include文件的話文件應(yīng)該是帶有后綴名的。
    所謂包含模塊,比如include(xxx),是說在CMAKE_MODULE_PATH變量對應(yīng)的目錄,或者CMake安裝包自帶的Modules目錄(比如mac下brew裝的cmake對應(yīng)的是/usr/local/share/cmake/Modules)里面尋找xxx.cmake文件。注意,此時不需要寫".cmake"這一后綴。

具體的說,這里是把CMake安裝包提供的ExternalProject.cmake(例如我的是/usr/local/share/cmake/Modules/ExternalProject.cmake)文件包含進(jìn)來。ExternalProject,顧名思義,引入外部工程,各種第三方庫什么的都可以考慮用它來弄;

GNUInstallDirs也是對應(yīng)到CMake安裝包提供的GNUInstallDirs.cmake文件,這個包具體細(xì)節(jié)還不太了解,可自行翻閱該文件。


include(cmake/Utils.cmake)
include(cmake/Targets.cmake)
include(cmake/Misc.cmake)
include(cmake/Summary.cmake)
include(cmake/ConfigGen.cmake)

這里是實打?qū)嵉陌嗽陧椖縞make子目錄下的5各cmake腳本文件了,是Caffe作者們(注意,完整的Caffe不是Yangqing Jia一個人寫的)提供的,粗略看了下:

  • cmake/Utils.cmake: 定義了一些通用的(適用于其他項目的)函數(shù),用于變量(數(shù)組)的打印、合并、去重、比較等(吐槽:cmake語法比較奇葩,相當(dāng)一段時間之后我才發(fā)現(xiàn)它是lisp方式的語法,也就是函數(shù)(命令)是一等公民)
  • cmake/Targets.cmake: 定義了Caffe項目本身的一些函數(shù)和宏,例如源碼文件組織、目錄組織等。
  • cmake/Misc.cmake:雜項,比較摳細(xì)節(jié)的一些設(shè)定,比如通常CMAKE_BUILD_TYPE基本夠用了,但是這里通過CMAKE_CONFIGURATION_TYPES來輔助設(shè)定CMAKE_BUILD_TYPE,等等
  • cmake/Summary.cmake:定義了4個打印函數(shù),用來打印Caffe的一些信息,執(zhí)行CMake時會在終端輸出,相比于散落在各個地方的message()語句會更加系統(tǒng)一些
  • cmake/ConfigGen.cmake: 整個caffe編譯好之后,如果別的項目要用它,那它也應(yīng)該用cmake腳本提供配置信息。

這5個cmake腳本中具體的函數(shù)比較多,這里先放過,后續(xù)可能考慮逐一解讀。


caffe_option(CPU_ONLY  "Build Caffe without CUDA support" OFF) # TODO: rename to USE_CUDA
caffe_option(USE_CUDNN "Build Caffe with cuDNN library support" ON IF NOT CPU_ONLY)
caffe_option(USE_NCCL "Build Caffe with NCCL library support" OFF)
caffe_option(BUILD_SHARED_LIBS "Build shared libraries" ON)
caffe_option(BUILD_python "Build Python wrapper" ON)
set(python_version "2" CACHE STRING "Specify which Python version to use")
caffe_option(BUILD_matlab "Build Matlab wrapper" OFF IF UNIX OR APPLE)
caffe_option(BUILD_docs   "Build documentation" ON IF UNIX OR APPLE)
caffe_option(BUILD_python_layer "Build the Caffe Python layer" ON)
caffe_option(USE_OPENCV "Build with OpenCV support" ON)
caffe_option(USE_LEVELDB "Build with levelDB" ON)
caffe_option(USE_LMDB "Build with lmdb" ON)
caffe_option(ALLOW_LMDB_NOLOCK "Allow MDB_NOLOCK when reading LMDB files (only if necessary)" OFF)
caffe_option(USE_OPENMP "Link with OpenMP (when your BLAS wants OpenMP and you get linker errors)" OFF)

# This code is taken from https://github.com/sh1r0/caffe-android-lib
caffe_option(USE_HDF5 "Build with hdf5" ON)

這里是設(shè)定各種option,也就是”開關(guān)“,然后后續(xù)根據(jù)開關(guān)的取值(布爾類型的變量,利用ifelse來判斷),編寫各自的構(gòu)建規(guī)則。
其中caffe_option()cmake/Utils.cmake中定義的,它相比于cmake自帶的option()命令,增加了可選的條件控制字段:

image.png

caffe_option()的具體實現(xiàn)還沒有看懂,不過看一下所有用到的地方也都是很直觀的:

image.png

具體的說,這里就是設(shè)定一些“高層級的編譯選項開關(guān)”,比如是否編matlab接口、是否編python接口,是否用hdf5,是否用openmp,等等。


include(cmake/Dependencies.cmake)

這里是包含Dependencies.cmake,它里面配置了Caffe的絕大多數(shù)依賴庫:

Boost
Threads
OpenMP
Google-glog
Google-gflags
Google-protobuf
HDF5
LMDB
LevelDB
Snappy
CUDA
OpenCV
BLAS
Python
Matlab
Doxygen

其中每一個依賴庫庫都直接(在Dependencies.cmake中)或間接(在各自的cmake腳本文件中)使用find_package()命令來查找包。

使用find_package(),需要明確兩點:

  1. find_package(Xxx)如果執(zhí)行成功,則提供相應(yīng)的Xxx_INCLUDE_DIR、Xxx_LIBRARY_DIR等變量,看起來挺方便,但其實并不是所有的庫都提供了同樣的變量后綴,其實都是由庫的官方作者或第三方提供的xxx.cmake等腳本來得到的,依賴于生態(tài)。
  2. find_packge(Xxx)實際中往往是翻車重災(zāi)區(qū)。它其實有N大查找順序,而CSDN上的博客中往往就瞎弄一個,你照搬后還是不行。具體例子:
  • 系統(tǒng)包管理工具裝的OpenCV不帶contrib模塊,想使用自行編譯的OpenCV但是git clone下來的開源代碼執(zhí)行后找不到自己編譯的OpenCV。其實只要知道N大查找順序,設(shè)定CMAKE_PREFIX_PATH中包含OpenCV路徑后基本都能找到
  • Caffe基于cmake編譯,依賴于Boost,系統(tǒng)里用apt或brew裝了Boost,同時也自行編譯了高版本Boost,現(xiàn)在Caffe編譯時cmake只認(rèn)自行編譯版的Boost,指定N大查找順序也不能找到系統(tǒng)的Boost。切換已安裝的多個Boost給CMake find_package(),這時候需要看看FindBoost.cmake是怎么寫的,必須提供它里面說的字樣的變量(表示include和lib的查找路徑),才能讓find_package()起作用。
  • CMake編譯安裝了多個版本的Caffe(比如官方Caffe、SSD的Caffe),~/.cmake目錄下會緩存一個caffe,而現(xiàn)在手頭有一個做人臉檢測的工程依賴Caffe,而你希望它用官方Caffe而不是SSD-Caffe,這個緩存目錄很可能搗亂,這個我認(rèn)為是某些項目比如Caffe的export輸出是多余的,反而容易造成混淆。

這里暫時不逐一分析每一個包的find_package()情況,只需要注意如果某個包你安裝了但是cmake卻沒有找到,那就需要在find_package()前進(jìn)行設(shè)定,以及之后排查。


if(UNIX OR APPLE)
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -Wall")
endif()

通過設(shè)定CMAKE_CXX_FLAGS,cmake生成各自平臺的makefile、.sln或xcodeproject文件時設(shè)定同樣的CXXFLAGS給編譯器。如果是.c文件,則由c編譯器編譯,對應(yīng)的是CMAKE_C_FLAGS。

這里的set()指令設(shè)定CMAKE_CXX_FLAGS的值,加入了兩個新的flags:"-fPIC"和"-Wall"。實際上用list(APPEND CMAKE_CXX_FLAGS "-fPIC -Wall")是完全可以的。set()只不過是有時候可能考慮設(shè)定變量默認(rèn)值的時候用一用。

-fPIC作用于編譯階段,告訴編譯器產(chǎn)生與位置無關(guān)代碼(Position-Independent Code),則產(chǎn)生的代碼中,沒有絕對地址,全部使用相對地址,故而代碼可以被加載器加載到內(nèi)存的任意位置,都可以正確的執(zhí)行。這正是共享庫所要求的,共享庫被加載時,在內(nèi)存的位置不是固定的。
-Wall則是開啟所有警告。根據(jù)個人的開發(fā)經(jīng)驗,C編譯器的警告不能完全忽視,有些wanring其實應(yīng)當(dāng)當(dāng)做error來對待,例如:

  • 函數(shù)未定義而被使用(忘記#include頭文件)
  • 指針類型不兼容(incompatible)
    都有可能引發(fā)seg fault,甚至bus error。


caffe_set_caffe_link()

這里是設(shè)置Caffe_LINK這一變量,后續(xù)鏈接階段會用到。它定義在cmake/Targets.cmake中:

image.png

可以看到,如果是編共享庫(動態(tài)庫),則就叫caffe;否則,則增加一些鏈接器的flags:-Wl是告訴編譯器,后面緊跟的是鏈接器的flags而不是編譯器的flags(現(xiàn)在的編譯器往往是包含了調(diào)用連接器的步驟)。

這里的幾個鏈接器參數(shù),目前我沒有細(xì)究過,具體看ld文檔:https://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_node/ld_3.html


if(USE_libstdcpp)
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libstdc++")
  message("-- Warning: forcing libstdc++ (controlled by USE_libstdcpp option in cmake)")
endif()

USE_libstdcpp這個變量的含義:
在前面已經(jīng)include(cmake/Dependencies.cmake)的情況下,Dependencies.cmake中的include(cmake/Cuda.cmake)使得Cuda的設(shè)定也被載入。而Cuda.cmake中的最后,判斷如果當(dāng)前操作系統(tǒng)是蘋果系統(tǒng)并且>10.8、cuda版本小于7.0,那么使用libstdc++而不是libc++

image.png

這時候想起來還沒畢業(yè)那會兒的一個新聞,說蘋果移除了libstdc++而讓大家換libc++的事情了,這個USE_libstdcpp就是這個意思了:如果cuda版本老(<7.0)并且OSX版本高(>10.8),就應(yīng)該用libstdc++來兼容cuda。

這里還有一個小插曲:通常執(zhí)行cmake后最前面會輸出它所使用的C、C++編譯器的可執(zhí)行文件完整路徑,然后一個同事的機(jī)器上把CXX環(huán)境變量設(shè)為/usr/bin/gcc,導(dǎo)致編譯.cpp文件時是用CXX這一環(huán)境變量——也就是gcc——來編譯.cpp文件。編譯.cpp,如果是C++編譯器來編譯,鏈接階段默認(rèn)會把標(biāo)準(zhǔn)庫鏈接進(jìn)去,而現(xiàn)在是C編譯器,沒有明確指出要鏈接C++標(biāo)準(zhǔn)庫,就會導(dǎo)致鏈接出問題,雖然他的CMakeLists.txt中曾經(jīng)加入過libstdc++庫,但是顯然這很容易翻車,CXX環(huán)境變量不應(yīng)該設(shè)定為/usr/bin/gcc。


caffe_warnings_disable(CMAKE_CXX_FLAGS -Wno-sign-compare -Wno-uninitialized)

這里添加的編譯器flags,是用來屏蔽特定類型的警告的。雖說眼不見心不煩,關(guān)掉后少些warning輸出,但是0error0warning不應(yīng)該是中級目標(biāo)嗎?


configure_file(cmake/Templates/caffe_config.h.in "${PROJECT_BINARY_DIR}/caffe_config.h")

這是設(shè)定configure file。configure_file()命令是把輸入文件(第一個參數(shù))里面的一些內(nèi)容做替換(比如${var}@var@替換為具體的值,宏定義等),然后放到指定的輸出文件(第二個參數(shù))。其實還有其他沒有列出的參數(shù)。

具體說,這里生成了build/caffe_config.h,里面define了幾個變量:

image.png


set(Caffe_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include)
set(Caffe_SRC_DIR ${PROJECT_SOURCE_DIR}/src)
include_directories(${PROJECT_BINARY_DIR})

這里是設(shè)定兩個自定義變量Caffe_INCLUDE_DIRCaffe_SRC_DIR的值,只不過它倆比較特殊,想想:如果以后別人find_package(Caffe),其實就需要其中的Caffe_INCLUDE_DIR的值。anyway,那些是后續(xù)export命令干的事情,這里忽略。

這里第三句include_directories()命令,把build目錄加入到頭文件搜索路徑了,其實就是為了確保caffe_config.h能被正常include(就一個地方用到它):

image.png


# cuda_compile() does not have per-call dependencies or include pathes
# (cuda_compile() has per-call flags, but we set them here too for clarity)
#
# list(REMOVE_ITEM ...) invocations remove PRIVATE and PUBLIC keywords from collected    definitions and include pathes
if(HAVE_CUDA)
  # pass include pathes to cuda_include_directories()
  set(Caffe_ALL_INCLUDE_DIRS ${Caffe_INCLUDE_DIRS})
  list(REMOVE_ITEM Caffe_ALL_INCLUDE_DIRS PRIVATE PUBLIC)
  cuda_include_directories(${Caffe_INCLUDE_DIR} ${Caffe_SRC_DIR}                         ${Caffe_ALL_INCLUDE_DIRS})

  # add definitions to nvcc flags directly
  set(Caffe_ALL_DEFINITIONS ${Caffe_DEFINITIONS})
  list(REMOVE_ITEM Caffe_ALL_DEFINITIONS PRIVATE PUBLIC)
  list(APPEND CUDA_NVCC_FLAGS ${Caffe_ALL_DEFINITIONS})
endif()

擦亮眼睛:Caffe的cmake腳本中分別定義了Caffe_INCLUDE_DIRCaffe_INCLUDE_DIRS兩個變量,只相差一個S,稍不留神容易混掉:不帶S的值是$Caffe_ROOT/include,帶S的值是各個依賴庫的頭文件搜索路徑(在Dependencies.cmake中多次list(APPEND得到的。類似的,Caffe_DEFINITIONS也是在Dependencies.cmake中設(shè)定的。

這里判斷出如果有CUDA的話就把Caffe_INCLUDE_DIRS變量中的PUBLICPRIVATE都去掉,把Caffe_DEFINITIONS中的PUBLICPRIVATE也去掉。

add_definitions()中添加的宏,用PUBLIC或PRIVATE修飾,有什么用?
以及,set()或list(APPEND來設(shè)定、更新的庫名字,用PUBLIC、PRIVATE或INTERFACE修飾,有什么用?這里比較疑惑,盡管我找到了stack overflow上的這篇回答,但是仍然一頭霧水:https://stackoverflow.com/questions/26037954/cmake-target-link-libraries-interface-dependencies

anyway,反正這里最后都做了list(REMOTE_ITEM操作,把PUBLICPRIVATE去掉了。


add_subdirectory(src/gtest)
add_subdirectory(src/caffe)
add_subdirectory(tools)
add_subdirectory(examples)
add_subdirectory(python)
add_subdirectory(matlab)
add_subdirectory(docs)

使用add_subdirectory(),意思是說把子目錄中的CMakeLists.txt文件加載過來執(zhí)行,從這個角度看似乎等同于include()命令。實則不然,因為它除了按給定目錄名字后需要追加"/CMakeLists.txt"來構(gòu)成完整路徑外,往往都是包含一個target(類似于git中的submodule了),同時還可以設(shè)定別的一些參數(shù):

  • 指定binary_dir
  • 設(shè)定EXCLUDE_FROM_ALL,也就是”搞一個獨立的子工程“,此時需要有project()指令,并且不被包含在生成的.sln工程的ALL目標(biāo)中,需要單獨構(gòu)建。

粗略看看各個子目錄都是做什么的:

  • src/gtest,googletest的源碼
  • src/caffe,caffe的源碼構(gòu)建,因為前面做了很多操作(依賴庫、路徑,etc),這里寫的就比較少。任務(wù)只有2個:構(gòu)建一個叫做caffe的庫,以及test。
  • tools,這一子目錄下每一個cpp文件都生成一個xxx.bin的目標(biāo),而最常用的就是caffe訓(xùn)練接口build/caffe這個可執(zhí)行文件了。
  • examples,這一子目錄下有cpp_classification的C++代碼,以及mnist,cifar10,siamse這三個例子的數(shù)據(jù)轉(zhuǎn)換的代碼,這四個都是C++文件,每一個都被編譯出一個可執(zhí)行
  • python,pycaffe接口,python/caffe/_caffe.cpp編譯出動態(tài)庫
  • matlab,matlab接口,./+caffe/private/caffe_.cpp編譯出?編譯出一個定制的目標(biāo),至于是啥類型,也許是動態(tài)庫吧,玩過matlab和C/C++混編的都知道,用mex編譯C/C++為.mexa文件,然后matlab調(diào)用.mexa文件,其實就是動態(tài)庫
  • docs,文檔,doxygen、jekyll都來了,以及拷貝每一個.ipynb文件。沒錯,add_custom_command()能定制各種target,只要你把想要執(zhí)行的shell腳本命令用cmake的語法來寫就可以了,很強(qiáng)大。

add_custom_target(lint COMMAND ${CMAKE_COMMAND} -P ${PROJECT_SOURCE_DIR}/cmake/lint.cmake)

這里依然是定制的target,具體看來是調(diào)用scripts/cpplint.py(谷歌官方C++代碼風(fēng)格檢查工具)來執(zhí)行代碼風(fēng)格檢查。(個人覺得G家的C++風(fēng)格有一點不太好:縮進(jìn)兩個空格太少了,費眼睛,強(qiáng)烈建議和Visual Studio保持一致,用tab并且tab寬度為4個空格)。

所謂linter就是語法檢查器,除了cpplint其實還可以用cpp_check、gccclang等,我的vim中配置的就是用cpp_checkgcc,不妨試試:https://github.com/zchrissirhcz/dotvim


if(BUILD_python)
  add_custom_target(pytest COMMAND python${python_version} -m unittest discover -s caffe/test WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/python )
  add_dependencies(pytest pycaffe)
endif()

如果開啟了BUILD_python開關(guān),那么執(zhí)行一個定制的target(執(zhí)行pytest)。
add_dependencies()意思是指定依賴關(guān)系,這里要求pycaffe目標(biāo)完成后再執(zhí)行pytest目標(biāo),因為pytest需要用到pycaffe生成的caffe模塊。pycaffe在前面提到的add_subdirectory(python)中被構(gòu)建。


configure_file(
    ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Uninstall.cmake.in
    ${CMAKE_CURRENT_BINARY_DIR}/cmake/Uninstall.cmake
    IMMEDIATE @ONLY)

add_custom_target(uninstall
    COMMAND ${CMAKE_COMMAND} -P
    ${CMAKE_CURRENT_BINARY_DIR}/cmake/Uninstall.cmake)

這里是添加”uninstall"這一target,具體定制的target其實就是執(zhí)行cmake/Uninstall.cmake腳本。這個腳本根據(jù)cmake/Uninstall.cmake.in做變量取值替換等來生成得到。


# ---[ Configuration summary
caffe_print_configuration_summary()

# ---[ Export configs generation
caffe_generate_export_configs()

在Caffe根目錄的CMakeLists.txt的最后,是打印各種配置的總的情況,以及輸出各種配置(后者其實包含了install()指令的調(diào)用)

(2019-03-03 00:31:09 本篇寫之前覺得不難,但是斷斷續(xù)續(xù)分析下來竟然用了大半天時間,對于CMake的一些指令細(xì)節(jié)重新查過,發(fā)現(xiàn)之前的掌握確實還不夠)

最后編輯于
?著作權(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)容