《CMake實踐》筆記二:INSTALL/CMAKE_INSTALL_PREFIX

《CMake實踐》筆記一:PROJECT/MESSAGE/ADD_EXECUTABLEphp

《CMake實踐》筆記二:INSTALL/CMAKE_INSTALL_PREFIXhtml

《CMake實踐》筆記三:構建靜態庫與動態庫 及 如何使用外部共享庫和頭文件正則表達式

 

4、更好一點的Hello World

沒有最好,只有更好ui

從本小節開始,後面全部的構建咱們都將採用 out-of-source 外部構建,約定的構建目錄是工程目錄下的build自錄。spa

本小節的任務是讓前面的Hello World更像一個工程,咱們須要做的是:code

(1)、爲工程添加一個子目錄src,用來放置工程源代碼;component

(2)、添加一個子目錄doc,用來放置這個工程的文檔hello.txthtm

(3)、在工程目錄添加文本文件COPYRIGHT, README;blog

(4)、在工程目錄添加一個runhello.sh腳本,用來調用hello二進制ip

(5)、將構建後的目標文件放入構建目錄的bin子目錄;

(6)、最終安裝這些文件:將hello二進制與runhello.sh安裝至/usr/bin,將doc目錄的內容以及COPYRIGHT/README安裝到/usr/share/doc/cmake/t2

 

一、準備工做:

在/backup/cmake/目錄下創建t2目錄。將t1工程的main.c和CMakeLists.txt拷貝到t2目錄中。

 

二、添加子目錄src:

mkdir src
mv main.c src

如今的工程看起來是這個樣子:一個子目錄src,一個CMakeLists.txt。

上一節咱們提到,須要爲任何子目錄創建一個CMakeLists.txt,進入子目錄src,編寫CMakeLists.txt以下:

ADD_EXECUTABLE(hello main.c)

將t2工程的CMakeLists.txt修改成:

PROJECT(HELLO)
ADD_SUBDIRECTORY(src bin)

而後創建build目錄,進入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目錄。這裏須要提一下的是SUBDIRS指令,使用方法是:SUBDIRS(dir1 dir2...),可是這個指令已經不推薦使用。它能夠一次添加多個子目錄,而且,即便外部編譯,子目錄體系仍然會被保存。若是咱們在上面的例子中將ADD_SUBDIRECTORY (src bin)修改成SUBDIRS(src)。那麼在build目錄中將出現一個src目錄,生成的目標代碼hello將存放在src目錄中。

 

三、換個地方保存目標二進制

不管是SUBDIRS仍是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安裝,一種是打包時的指定目錄安裝。因此,即便最簡單的手工編寫的Makefile,看起來也是這個樣子的:

DESTDIR=
install:
mkdir -p $(DESTDIR)/usr/bin
install -m 755 hello $(DESTDIR)/usr/bin

你能夠經過:

make install

將hello直接安裝到/usr/bin目錄,也能夠經過make install DESTDIR=/tmp/test將他安裝在/tmp/test/usr/bin目錄,打包時這個方式常常被使用。

稍微複雜一點的是還須要定義PREFIX,通常autotools工程,會運行這樣的指令:

./configure –prefix=/usr 或者 ./configure --prefix=/usr/local 來指定PREFIX.好比上面的Makefile就能夠改寫成:
DESTDIR=
PREFIX=/usr
install:
mkdir -p $(DESTDIR)/$(PREFIX)/bin
install -m 755 hello $(DESTDIR)/$(PREFIX)/bin

那麼咱們的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 mystaticlib
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE 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過濾後的文件權限。

咱們來看一個例子:

INSTALL(DIRECTORY icons scripts/ DESTINATION share/myproj
PATTERN "CVS" EXCLUDE
PATTERN "scripts/*"
PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ
GROUP_EXECUTE GROUP_READ)

這條指令的執行結果是:

將icons目錄安裝到 <prefix>/share/myproj,將scripts/中的內容安裝到<prefix>/share/myproj不包含目錄名爲CVS的目錄,對於scripts/*文件指定權限爲 OWNER_EXECUTE OWNER_WRITE OWNER_READ ROUP_EXECUTE GROUP_READ。

安裝時CMAKE腳本的執行:

INSTALL([[SCRIPT <file>] [CODE <code>]] [...])
SCRIPT參數用於在安裝時調用cmake腳本文件(也就是<abc>.cmake文件)
CODE參數用於執行CMAKE指令,必須以雙引號括起來。好比:
INSTALL(CODE "MESSAGE(\"Sample install message.\")")
安裝還有幾個被標記爲過期的指令,好比INSTALL_FILES等,這些指令已經再也不推薦使用,因此,這裏就再也不贅述了。下面,咱們就來改寫咱們的工程文件,讓他來支持各類文件的安裝,而且,咱們要使用CMAKE_INSTALL_PREFIX指令。

 

五、修改Helloworld支持安裝

首先咱們先補上爲添加的文件。

添加doc目錄及文件:

cd /backup/cmake/t2
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,這裏有兩種方式:

一是經過在doc目錄創建 CMakeLists.txt並將doc目錄經過ADD_SUBDIRECTORY加入工程來完成。

另外一種方法是直接在工程目錄經過 INSTALL(DIRECTORY來完成),前者比較簡單,各位能夠根據興趣本身完成,咱們來嘗試後者,順便演示如下DIRECTORY的安裝。由於hello.txt要安裝到/<prefix>/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目錄看一下安裝結果:

./usr
./usr/share
./usr/share/doc
./usr/share/doc/cmake
./usr/share/doc/cmake/t2
./usr/share/doc/cmake/t2/hello.txt
./usr/share/doc/cmake/t2/README
./usr/share/doc/cmake/t2/COPYRIGHT
./usr/bin
./usr/bin/hello
./usr/bin/runhello.sh

若是你要直接安裝到系統,可使用以下指令:

cmake -DCMAKE_INSTALL_PREFIX=/usr ..

 

七、一個疑問

若是我沒有定義CMAKE_INSTALL_PREFIX會安裝到什麼地方?你能夠嘗試如下,cmake ..;make;make install,你會發現CMAKE_INSTALL_PREFIX的默認定義是/usr/local

 

八、小結

本小節主要描述瞭如何在工程中使用多目錄、各類安裝指令以及CMAKE_INSTALL_PREFIX變量(你真夠牛的,這麼點東西竟然羅唆了這麼多文字)在下一小節,咱們將探討如何在cmake中構建動態庫和靜態庫,以及如何使用外部頭文件和外部共享庫,畢竟,這是程序編寫中最長使用的(對了,你知道用怎樣的gcc參數能夠直接構建靜態庫和動態庫嗎?)

未完,待續。。。。

 


如下是我本身增長的東西。

把這一節試了一下,感受仍是有點不爽,由於要編譯這個工程,首先要本身創建一個目錄,而後進入這個目錄後先運行一次cmake的命令,在執行make 和 make install。所以我的將這個例子本身稍微改了一下,讓安裝和卸載更方便使用。

首先目錄結構仍是同樣的,工程目錄下一個src目錄,一個CMakeLists.txt,一個空的COPYRIGHT和README,一個doc目錄,裏面有一個空的hello.txt文件。

而後本身增長一個install.sh和uninstall.sh腳本,用於安裝這個工程和卸載這個工程,整個目錄結構以下:

 

腳本的內容也很簡單,能夠指定安裝目錄,或者用默認的安裝目錄:

#!/bin/sh

if [ "$#" -eq 0 ]
then 
    echo "use default install dir: target"
    INSTALL_DIR="../target"
elif [ "$#" -eq 1 ]
then 
    echo "use custom install dir: $1"
    INSTALL_DIR=$1
else
    echo "error"
fi

rm -drf bulid
rm -drf $INSTALL_DIR

mkdir bulid

cd bulid
cmake -DCMAKE_INSTALL_PREFIX=$INSTALL_DIR ..
make
make install

這樣,在拿到這個共亨的gz文件後,解壓,直接運行install.sh腳本,入過不帶路徑,安裝在當前目錄下的target目錄下,不然安裝在指定的目錄下:

 

相關文章
相關標籤/搜索