編譯工具:CMake(三)| 最簡單的實例升級
- 前言
- 過程
- 語法解釋
- ADD_SUBDIRECTORY 指令
- 如何安裝
- 目標文件的安裝
- 普通文件的安裝:
- 非目標文件的可執行程序安裝(比如腳本之類)
- 目錄的安裝
- 修改 Helloworld 支持安裝
- 測試
前言
本篇博客的任務是讓前面的 Hello World 更像一個工程,我們需要作的是:
- 為工程添加一個子目錄 src,用來放置工程源代碼;
- 添加一個子目錄 doc,用來放置這個工程的文檔 hello.txt
- 在工程目錄添加文本文件 COPYRIGHT, README;
- 在工程目錄添加一個 runhello.sh 腳本,用來調用 hello 二進制
- 將構建后的目標文件放入構建目錄的 bin 子目錄;
- 最終安裝這些文件:將 hello 二進制與 runhello.sh 安裝至/usr/bin,將 doc 目錄的內容以及 COPYRIGHT/README 安裝到/usr/share/doc/cmake/test2
過程
在/Compilation_tool/cmake/目錄下建立 test2 目錄。
將 test1 工程的 main.c 和 CMakeLists.txt 拷貝到 test2 目錄中。
添加子目錄 src
將main.c文件移動到src文件夾
mkdir src
mv main.c src
現在的工程是這個樣子:
一個子目錄 src,一個 CMakeLists.txt。
CMake要求為任何子目錄建立一個CMakeLists.txt
所以進入src子目錄,編寫CMakeLists.txt如下:
ADD_EXECUTABLE(hello main.c)
將test2工程的CMakeLists.txt修改為:
PROJECT(HELLO)
ADD_SUBDIRECTORY(src bin)
然后建立 build 目錄,進入 build 目錄進行外部編譯。
mkdir build
cd build
cmake ..
make
構建完成后,你會發現生成的目標文件 hello 位于 build/bin 目錄中。
語法解釋
ADD_SUBDIRECTORY 指令
ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
這個指令用于向當前工程添加存放源文件的子目錄,并可以指定中間二進制和目標二進制存放的位置。
EXCLUDE_FROM_ALL 參數的含義是將這個目錄從編譯過程中排除,比如,工程的 example,可能就需要工程構建完成后,再進入 example 目錄單獨進行構建(當然,也可以通過定義依賴來解決此類問題)。
上面的例子定義了將 src 子目錄加入工程,并指定編譯輸出(包含編譯中間結果)路徑為bin 目錄。
如果不進行 bin 目錄的指定,那么編譯結果(包括中間結果)都將存放在build/src 目錄(這個目錄跟原有的 src 目錄對應),
指定 bin 目錄后,相當于在編譯時將 src 重命名為 bin,所有的中間結果和目標二進制都將存放在 bin 目錄。
換個地方保存目標二進制
ADD_SUBDIRECTORY 指令(不論是否指定編譯輸出目錄),可以通過 SET 指令重新定義EXECUTABLE_OUTPUT_PATH
和 LIBRARY_OUTPUT_PATH
變量,來指定最終的目標二進制的位置(指最終生成的 hello 或者最終的共享庫,不包含編譯生成
的中間文件)
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
<projectname>_BINARY_DIR
和 PROJECT_BINARY_DIR
變量,他們指的編譯發生的當前目錄,如果是內部編譯,就相當于 PROJECT_SOURCE_DIR 也就是工程代碼所在目錄,如果是外部編譯,指的是外部編譯所在目錄,也就是本例中的 build
目錄。
所以,上面兩個指令分別定義了:可執行二進制的輸出路徑為 build/bin 和庫的輸出路徑為 build/lib.
應該把這兩條指令寫在工程的 CMakeLists.txt 還是 src 目錄下的CMakeLists.txt,把握一個簡單的原則,在哪里 ADD_EXECUTABLE
或 ADD_LIBRARY
,如果需要改變目標存放路徑,就在哪里加入上述的定義。
在這個例子里,當然就是指 src 下的 CMakeLists.txt 了。
如何安裝
安裝的需要有兩種,一種是從代碼編譯后直接 make install 安裝,一種是打包時的指定目錄安裝。
HelloWorld 進行安裝需要引入一個新的 cmake 指令INSTALL
和一個非常有用的變量CMAKE_INSTALL_PREFIX
CMAKE_INSTALL_PREFIX
變量類似于 configure 腳本的 – prefix
常見的使用方法是這個樣子:
cmake -DCMAKE_INSTALL_PREFIX=/usr
INSTALL 指令用于定義安裝規則,安裝的內容可以包括目標二進制、動態庫、靜態庫以及文件、目錄、腳本等。
INSTALL 指令包含了各種安裝類型,分開解釋如下:
目標文件的安裝
INSTALL(TARGETS targets...[[ARCHIVE|LIBRARY|RUNTIME][DESTINATION <dir>][PERMISSIONS permissions...][CONFIGURATIONS[Debug|Release|...]][COMPONENT <component>][OPTIONAL]] [...])
參數中的 TARGETS
后面跟的就是我們通過 ADD_EXECUTABLE
或者ADD_LIBRARY
定義的目標文件,可能是可執行二進制、動態庫、靜態庫。
目標類型也就相對應的有三種,ARCHIVE
特指靜態庫,LIBRARY
特指動態庫,RUNTIME
特指可執行目標二進制。
DESTINATION
定義了安裝的路徑,如果路徑以/開頭,那么指的是絕對路徑,這時候CMAKE_INSTALL_PREFIX
其實就無效了。如果你希望使用 CMAKE_INSTALL_PREFIX
來定義安裝路徑,就要寫成相對路徑,即不要以/開頭,那么安裝后的路徑就是
${CMAKE_INSTALL_PREFIX}/<DESTINATION 定義的路徑>
舉個簡單的例子:
INSTALL(TARGETS myrun mylib mystaticlibRUNTIME DESTINATION binLIBRARY DESTINATION libARCHIVE DESTINATION libstatic)
上面的例子會將:
可執行二進制 myrun 安裝到${CMAKE_INSTALL_PREFIX}/bin
目錄
動態庫 libmylib 安裝到${CMAKE_INSTALL_PREFIX}/lib
目錄
靜態庫 libmystaticlib 安裝到${CMAKE_INSTALL_PREFIX}/libstatic
目錄
特別注意的是不需要關心 TARGETS 具體生成的路徑,只需要寫上 TARGETS 名稱就可以了。
普通文件的安裝:
INSTALL(FILES files... DESTINATION <dir>[PERMISSIONS permissions...][CONFIGURATIONS [Debug|Release|...]][COMPONENT <component>][RENAME <name>] [OPTIONAL])
可用于安裝一般文件,并可以指定訪問權限,文件名是此指令所在路徑下的相對路徑。如果默認不定義權限 PERMISSIONS,安裝后的權限為:OWNER_WRITE
, OWNER_READ
,GROUP_READ
,和 WORLD_READ
,即 644 權限。
非目標文件的可執行程序安裝(比如腳本之類)
INSTALL(PROGRAMS files... DESTINATION <dir>
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[RENAME <name>] [OPTIONAL])
跟上面的 FILES 指令使用方法一樣,唯一的不同是安裝后權限為:
OWNER_EXECUTE
, GROUP_EXECUTE
, 和 WORLD_EXECUTE
,即 755 權限
目錄的安裝
INSTALL(DIRECTORY dirs... DESTINATION <dir>[FILE_PERMISSIONS permissions...][DIRECTORY_PERMISSIONS permissions...][USE_SOURCE_PERMISSIONS][CONFIGURATIONS [Debug|Release|...]][COMPONENT <component>][[PATTERN <pattern> | REGEX <regex>][EXCLUDE] [PERMISSIONS permissions...]] [...])
這里主要介紹其中的 DIRECTORY
、PATTERN
以及PERMISSIONS
參數。
DIRECTORY
后面連接的是所在 Source 目錄的相對路徑,但務必注意:
abc 和 abc/有很大的區別。如果目錄名不以/結尾,那么這個目錄將被安裝為目標路徑下的 abc,如果目錄名以/結尾,
代表將這個目錄中的內容安裝到目標路徑,但不包括這個目錄本身。PATTERN
用于使用正則表達式進行過濾,PERMISSIONS
用于指定 PATTERN
過濾后的文件權限。
修改 Helloworld 支持安裝
添加 doc 目錄及文件:
cd /Compilation_tool/cmake/test2
mkdir doc
vi doc/hello.txt
隨便填寫一些內容并保存
在工程目錄添加 runhello.sh 腳本,內容為:
hello
添加工程目錄中的 COPYRIGHT 和 README
touch COPYRIGHT
touch README
下面改寫各目錄的 CMakeLists.txt 文件。
1、安裝 COPYRIGHT/README,直接修改主工程文件 CMakelists.txt,加入以下指令:
INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/t2)
2、安裝 runhello.sh,直接修改主工程文件 CMakeLists.txt,加入如下指令:
INSTALL(PROGRAMS runhello.sh DESTINATION bin)
3、安裝 doc 中的 hello.txt
因為 hello.txt 要安裝到//share/doc/cmake/t2,所以我們不能直接安裝整個 doc 目錄,這里采用的方式是安裝 doc 目錄中的內容,也就是使用 ” doc/” 在工程文件中添加
INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake/t2)
測試
現在進入 build 目錄進行外部編譯,注意使用 CMAKE_INSTALL_PREFIX 參數,這里我們將它安裝到了/tmp/t2 目錄:
cmake -DCMAKE_INSTALL_PREFIX=/tmp/t2/usr ..
然后運行
make
make install
這時候在計算機的 tmp路徑下就多了 t2 文件夾
t2的目錄為
如果你要直接安裝到系統,可以使用如下指令:
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
如果沒有定義CMAKE_INSTALL_PREFIX
會安裝到什么地方?
CMAKE_INSTALL_PREFIX 的默認定義是/usr/local