.so 依赖目录 cmake_CMAKE最全实战(2)
閱讀本篇文章前,請閱讀CMAKE最全實戰(1),這2篇是有關聯。
1.靜態庫與動態庫構建
從本節開始,我們不再折騰Hello World 了,我們來折騰Hello World 的共享庫。
本節的任務:
1.建??個靜態庫和動態庫,提供HelloFunc 函數供其他程序編程使?,HelloFunc向終端輸出Hello World 字符串。
2.安裝頭?件與共享庫。
準備?作
在cmake ?錄建? t3 ?錄,?于存放本節涉及到的?程
建?共享庫
cd make/t3
mkdir lib
在 t3 ?錄下建?CMakeLists.txt,內容如下:
PROJECT(HELLOLIB)
ADD_SUBDIRECTORY(lib)
在 lib ?錄下建?兩個源?件hello.c 與hello.h
hello.c 內容如下:
#include "hello.h"void hello_func(void) {printf("Hello World!");return;}hello.h 內容如下:
#ifndef HELLO_H_#define HELLO_H_ (1)#include void hello_func(void);#endif在 lib ?錄下建?CMakeLists.txt,內容如下:
SET(LIBHELLO_SRC hello.c)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
編譯共享庫
仍然采?out-of-source 編譯的?式,按照習慣,我們建??個build ?錄,在build?錄中
cmake ..
make
這時,你就可以在lib ?錄得到?個libhello.so,這就是我們期望的共享庫。如果你要指定libhello.so ?成的位置,可以通過在主?程?件CMakeLists.txt 中修改 ADD_SUBDIRECTORY(lib)指令來指定?個編譯輸出位置或者在lib/CMakeLists.txt 中添加SET(LIBRARY_OUTPUT_PATH )來指定?個新的位置。
這兩者的區別CMAKE最全實戰(1) 已經提到了,所以,這?不再贅述,下?,我們解釋?下?個新的指令
ADD_LIBRARY
ADD_LIBRARY(libname [SHARED|STATIC|MODULE] [EXCLUDE_FROM_ALL]
source1 source2 ... sourceN)
不需要寫全libhello.so,只需要填寫hello 即可,cmake 系統會?動為你?成 libhello.X。
類型有三種:
SHARED,動態庫
STATIC,靜態庫
MODULE,在使? dyld 的系統有效,如果不?持dyld,則被當作SHARED 對待。
EXCLUDE_FROM_ALL 參數的意思是這個庫不會被默認構建,除?有其他的組件依賴或者??構建。
添加靜態庫
同樣使?上?的指令,我們在?持動態庫的基礎上再為?程添加?個靜態庫,按照?般的習慣,靜態庫名字跟動態庫名字應該是?致的,只不過后綴是.a 罷了。
下?我們?這個指令再來添加靜態庫:
ADD_LIBRARY(hello STATIC ${LIBHELLO_SRC})
然后再在build ?錄進?外部編譯,我們會發現,靜態庫根本沒有被構建,仍然只?成了?個動態庫。因為hello 作為?個target 是不能重名的,所以,靜態庫構建指令?效。
如果我們把上?的hello 修改為hello_static:
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})
就可以構建?個libhello_static.a 的靜態庫了。
這種結果顯示不是我們想要的,我們需要的是名字相同的靜態庫和動態庫,因為 target 名稱是唯?的,所以,我們肯定不能通過 ADD_LIBRARY 指令來實現了。這時候我們需要?到另外?個指令:
SET_TARGET_PROPERTIES,其基本語法是:
SET_TARGET_PROPERTIES(target1 target2 ...
PROPERTIES prop1 value1
prop2 value2 ...)
這條指令可以?來設置輸出的名稱,對于動態庫,還可以?來指定動態庫版本和 API 版本。
在本例中,我們需要作的是向lib/CMakeLists.txt 中添加?條:
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello") 這樣,我們就可以同時得到libhello.so/libhello.a 兩個庫了
與他對應的指令是:
GET_TARGET_PROPERTY(VAR target property)
具體?法如下例,我們向lib/CMakeListst.txt 中添加:
GET_TARGET_PROPERTY(OUTPUT_VALUE hello_static OUTPUT_NAME)
MESSAGE(STATUS “This is the hello_static OUTPUT_NAME:”${OUTPUT_VALUE})
如果沒有這個屬性定義,則返回NOTFOUND。
讓我們來檢查?下最終的構建結果,我們發現,libhello.a 已經構建完成,位于 build/lib ?錄中,但是libhello.so 去消失了。這個問題的原因是:cmake 在構建?個新的target 時,會嘗試清理掉其他使?這個名字的庫,因為,在構建libhello.a 時,就會清理掉libhello.so.
為了回避這個問題,?如再次使?SET_TARGET_PROPERTIES 定CLEAN_DIRECT_OUTPUT 屬性。
向 lib/CMakeLists.txt 中添加:
SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)
SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
這時候,我們再次進?構建,會發現build/lib ?錄中同時?成了libhello.so 和 libhello.a
動態庫版本號
按照規則,動態庫是應該包含?個版本號的,我們可以看?下系統的動態庫,?般情況是
libhello.so.1.2
libhello.so ->libhello.so.1
libhello.so.1->libhello.so.1.2
為了實現動態庫版本號,我們仍然需要使? SET_TARGET_PROPERTIES 指令。
具體使??法如下:
SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1) VERSION 指代動態庫版本,SOVERSION 指代 API 版本。
將上述指令加?lib/CMakeLists.txt 中,重新構建看看結果。
在 build/lib ?錄會?成: libhello.so.1.2 libhello.so.1->libhello.so.1.2 libhello.so ->libhello.so.1
安裝共享庫和頭?件
需要將libhello.a, libhello.so.x 以及 hello.h 安裝到系統?錄,才能真正讓其他?開發 使 ? , 在 本 例 中 我 們 將 hello 的 共 享 庫 安 裝 到 /lib ? 錄 , 將 hello.h 安 裝 到/include/hello ?錄。
利?CMAKE最全實戰(1) 了解到的INSTALL 指令,我們向lib/CMakeLists.txt 中添加如下指令:
INSTALL(TARGETS hello hello_static
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib)
INSTALL(FILES hello.h DESTINATION include/hello)
注意,靜態庫要使?ARCHIVE 關鍵字
通過:
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
make
make install
可以將頭?件和共享庫安裝到系統?錄/usr/lib 和/usr/include/hello 中了
如果報錯
CMake Error: cmake_symlink_library: System Error: Operation not supported
CMake Error: cmake_symlink_library: System Error: Operation not supported
make[2]: *** [lib/CMakeFiles/hello_dynamic.dir/build.make:85: lib/libhello.so.1.2] Error 1
make[2]: *** Deleting file 'lib/libhello.so.1.2'
make[1]: *** [CMakeFiles/Makefile2:130: lib/CMakeFiles/hello_dynamic.dir/all] Error 2
make: *** [Makefile:130: all] Error 2
則說明你你可能是通過hgfs共享的Windows?錄下進?make,?成so時需要在純linux環境運?,?如把t3拷?到~/0/makefile/t3, 并且先把對應的build?錄??的內容清空,重新cmake .. 再make
本?節,我們談到了:如何通過ADD_LIBRARY 指令構建動態庫和靜態庫。如何通過SET_TARGET_PROPERTIES 同時構建同名的動態庫和靜態庫。如何通過SET_TARGET_PROPERTIES 控制動態庫版本最終使?上?節談到的INSTALL 指令來安裝頭?件和動態、靜態庫。在下?節,我們需要編寫另?個?級?點的 Hello World 來演示怎么使?我們已經構建的構建的共享庫libhello 和外部頭?件。
如何使?外部共享庫和頭?件
上?節我們已經完成了libhello 動態庫的構建以及安裝,本節我們的任務很簡單。編寫?個程序使?我們上?節構建的共享庫。
請在cmake ?錄建? t4 ?錄,本節所有資源將存儲在t4 ?錄。
構建?程
重復以前的步驟,建?src ?錄,編寫源?件main.c,內容如下:
#include "hello.h"
int main(void) {
hello_func();
return 0;
}
編寫?程主?件CMakeLists.txt
PROJECT(NEWHELLO)
ADD_SUBDIRECTORY(src)
編寫src/CMakeLists.txt
ADD_EXECUTABLE(main main.c)
外部構建
按照習慣,仍然建?build ?錄,使? cmake ..?式構建。
cmake ..
make
構建失敗,如果需要查看細節,可以使?第?節提到的?法
make VERBOSE=1 來構建
錯誤輸出為是:
cmake/t4/src/main.c:1:19: error: hello.h: 沒有那個?件或?錄
引?頭?件搜索路徑
假如hello.h 位于/usr/include/hello ?錄中,并沒有位于系統標準的頭?件路徑。
為了讓我們的?程能夠找到hello.h 頭?件,我們需要引??個新的指令。
INCLUDE_DIRECTORIES,其完整語法為:
INCLUDE_DIRECTORIES([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)
這條指令可以?來向?程添加多個特定的頭?件搜索路徑,路徑之間?空格分割,如果路徑中包含了空格,可以使?雙引號將它括起來,默認的?為是追加到當前的頭?件搜索路徑的后?,你可以通過兩種?式來進?控制搜索路徑添加的?式:
(1)CMAKE_INCLUDE_DIRECTORIES_BEFORE,通過SET 這個cmake 變量為on,可以將添加的頭?件搜索路徑放在已有路徑的前?。
(2)通過AFTER 或者BEFORE 參數,也可以控制是追加還是置前。
現在我們在src/CMakeLists.txt 中添加?個頭?件搜索路徑,?式很簡單,加?:
INCLUDE_DIRECTORIES(/usr/include/hello)
進?build ?錄,重新進?構建,這是找不到 hello.h 的錯誤已經消失,但是出現了?個新的錯誤:
main.c:(.text+0x12): undefined reference to `HelloFunc'
因為我們并沒有link 到共享庫libhello 上。
為 target 添加共享庫
現在需要完成的任務是將?標?件鏈接到 libhello,這?我們需要引?兩個新的指令:
LINK_DIRECTORIES 和TARGET_LINK_LIBRARIES
LINK_DIRECTORIES 的全部語法是:
LINK_DIRECTORIES(directory1 directory2 ...)
這個指令?常簡單,添加?標準的共享庫搜索路徑,?如,在?程內部同時存在共享庫和可執??進制,在編譯時就需要指定?下這些共享庫的路徑。這個例?中我們沒有?到這個指令。
TARGETLINKLIBRARIES 的全部語法:
TARGET_LINK_LIBRARIES(target library1
library2
...)
這個指令可以?來為target 添加需要鏈接的共享庫,本例中是?個可執??件,但是同樣可以?于為??編寫的共享庫添加共享庫鏈接。為了解決我們前?遇到的HelloFunc 未定義錯誤,我們需要作的是向 src/CMakeLists.txt 中添加如下指令:
TARGET_LINK_LIBRARIES(main hello)
也可以寫成
TARGET_LINK_LIBRARIES(main libhello.so)
hello 指的是我們上?節構建的共享庫 libhello,進?build ?錄重新進?構建。
cmake ..
make
得到了?個連接到libhello 的可執?程序main,位于build/bin?錄,運? main 的結果是輸出:
Hello World
檢查?下main 的鏈接情況:
ldd bin/main
可以清楚的看到main 確實鏈接了共享庫libhello,?且鏈接的是動態庫 libhello.so.1。
如何鏈接到靜態庫呢?
?法很簡單:
將 TARGET_LINK_LIBRRARIES 指令修改為: TARGET_LINK_LIBRARIES(main libhello.a)
重新構建后再來看?下main 的鏈接情況。說明,main 確實鏈接到了靜態庫libhello.a。
ldd src/main
特殊的環境變量CMAKE_INCLUDE_PATH 和CMAKE_LIBRARY_PATH。
注意,這兩個是環境變量?不是cmake 變量。
CMAKE_INCLUDE_PATH=/home/include cmake ..等?式。這兩個變量主要是?來解決以前autotools ?程中--extra-include-dir 等參數的?持的。
也就是,如果頭?件沒有存放在常規路徑(/usr/include, /usr/local/include 等),則可以通過這些變量就?彌補。以本例中的hello.h 為例,它存放在/usr/include/hello ?錄,所以直接查找肯定是找不到的。使?了絕對路徑INCLUDE_DIRECTORIES(/usr/include/hello)告訴?程這個頭?件?錄。
為了將程序更智能?點,我們可以使? CMAKE_INCLUDE_PATH 來進?,使?bash 的?法如下:
export CMAKE_INCLUDE_PATH=/usr/include/hello
在頭?件中將INCLUDE_DIRECTORIES(/usr/include/hello)替換為:
上述的?些指令我們在后?會介紹。
這?簡單說明?下,FIND_PATH ?來在指定路徑中搜索?件名,?如:
FIND_PATH(myHeader NAMES hello.h PATHS /usr/include /usr/include/hello)
這?我們沒有指定路徑,但是,cmake 仍然可以幫我們找到hello.h 存放的路徑,就是因為我們設置了環境變量CMAKE_INCLUDE_PATH。如果你不使?FIND_PATH,CMAKE_INCLUDE_PATH 變量的設置是沒有作?的,你不能指望它會直接為編譯器命令添加參數-I。
以此為例,CMAKE_LIBRARY_PATH 可以?在 FIND_LIBRARY 中。同樣,因為這些變量直接為FIND_指令所使?,所以所有使?FIND_指令的cmake 模塊都會受益。
如何通過INCLUDE_DIRECTORIES 指令加??標準的頭?件搜索路徑。
如何通過LINK_DIRECTORIES 指令加??標準的庫?件搜索路徑。
如果通過TARGET_LINK_LIBRARIES 為庫或可執??進制加?庫鏈接。
并解釋了如果鏈接到靜態庫。到這?為?,您應該基本可以使?cmake ?作了,但是還有很多?級的話題沒有探討,?如編譯條件檢查、編譯器定義、平臺判斷、如何跟 pkgconfig 配合使?等等。到這?,或許你可以理解前?講到的“cmake 的使?過程其實就是學習cmake 語?并編寫 cmake 程序的過程”,既然是“cmake 語?”,?然涉及到變量、語法等.
下?節,我們將拋開程序的話題,看看常?的 CMAKE 變量以及?些基本的控制語法規則。
cmake 常?變量和常?環境變量
cmake 變量引?的?式
使?${}進?變量的引?。在IF 等語句中,是直接使?變量名?不通過${}取值。主要有隱式定義和顯式定義兩種,前?舉了?個隱式定義的例?,就是 PROJECT 指令,他會隱式的定義_BINARY_DIR 和_SOURCE_DIR 兩個變量。顯式定義的例?我們前?也提到了,使? SET 指令,就可以構建?個?定義變量了。
?如:
SET(HELLO_SRC main.c),就PROJECT_BINARY_DIR 可以通過${HELLO_SRC}來引?這個?定義變量了。
cmake 常?變量
(1)CMAKE_BINARY_DIR
PROJECT_BINARY_DIR
_BINARY_DIR
這三個變量指代的內容是?致的,如果是 in source 編譯,指得就是?程頂層?錄,如果是 out-ofsource 編譯,指的是?程編譯發?的?錄。PROJECT_BINARY_DIR 跟其他指令稍有區別,現在,你可以理解為他們是?致的。
(2)CMAKE_SOURCE_DIR
PROJECT_SOURCE_DIR
_SOURCE_DIR
這三個變量指代的內容是?致的,不論采?何種編譯?式,都是?程頂層?錄。也就是在in source 編譯時,他跟 CMAKE_BINARY_DIR 等變量?致。PROJECT_SOURCE_DIR 跟其他指令稍有區別,現在,你可以理解為他們是?致的。
(3)CMAKE_CURRENT_SOURCE_DIR
指的是當前處理的CMakeLists.txt 所在的路徑,?如上?我們提到的 src ??錄。
(4)CMAKE_CURRRENT_BINARY_DIR
如果是in-source 編譯,它跟 CMAKE_CURRENT_SOURCE_DIR ?致,如果是out-of-source 編譯,它指的是target 編譯?錄。
使?上?提到的ADD_SUBDIRECTORY(src bin)可以更改這個變量的值。使?SET(EXECUTABLE_OUTPUT_PATH )并不會對這個變量造成影響,它僅僅修改了最終?標?件存放的路徑。
(5)CMAKE_CURRENT_LIST_FILE
輸出調?這個變量的CMakeLists.txt 的完整路徑
(6)CMAKE_CURRENT_LIST_LINE
輸出這個變量所在的?
(7)CMAKE_MODULE_PATH
這個變量?來定義??的cmake 模塊所在的路徑。如果你的?程?較復雜,有可能會??編寫?些cmake模塊,這些cmake 模塊是隨你的?程發布的,為了讓cmake 在處理 CMakeLists.txt 時找到這些模塊,你需要通過SET 指令,將??的cmake 模塊路徑設置?下。?如
SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
這時候你就可以通過INCLUDE 指令來調???的模塊了。
(8)EXECUTABLE_OUTPUT_PATH 和 LIBRARY_OUTPUT_PATH
分別?來重新定義最終結果的存放?錄,前?我們已經提到了這兩個變量。
(9)PROJECT_NAME
返回通過PROJECT 指令定義的項?名稱。
cmake 調?環境變量的?式
使?$ENV{NAME}指令就可以調?系統的環境變量了。
?如
MESSAGE(STATUS “HOME dir: $ENV{HOME}”)
設置環境變量的?式是:
SET(ENV{變量名} 值)
(1)CMAKE_INCLUDE_CURRENT_DIR
?動添加CMAKE_CURRENT_BINARY_DIR 和CMAKE_CURRENT_SOURCE_DIR 到當前處理的 CMakeLists.txt。相當于在每個CMakeLists.txt 加?:
(2)CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE
將?程提供的頭?件?錄始終?于系統頭?件?錄的前?,當你定義的頭?件確實跟系統發?沖突時可以提供?些幫助。
(3)CMAKE_INCLUDE_PATH 和 CMAKE_LIBRARY_PATH 我們在上?節已經提及。
系統信息
(1)CMAKE_MAJOR_VERSION,CMAKE 主版本號,?如2.4.6 中的2
(2)CMAKE_MINOR_VERSION,CMAKE 次版本號,?如2.4.6 中的4
(3)CMAKE_PATCH_VERSION,CMAKE 補丁等級,?如2.4.6 中的6
(4)CMAKE_SYSTEM,系統名稱,?如Linux-2.6.22
(5)CMAKE_SYSTEM_NAME,不包含版本的系統名,?如Linux
(6)CMAKE_SYSTEM_VERSION,系統版本,?如 2.6.22
(7)CMAKE_SYSTEM_PROCESSOR,處理器名稱,?如i686。
(8)UNIX,在所有的類 UNIX 平臺為 TRUE,包括OS X 和cygwin
(9)WIN32,在所有的 win32 平臺為 TRUE,包括cygwin
主要的開關選項
(1)CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS,?來控制IF ELSE 語句的書寫?式,在下?節語法部分會講到。
(2)BUILD_SHARED_LIBS。這個開關?來控制默認的庫編譯?式,如果不進?設置,使? ADD_LIBRARY 并沒有指定庫類型的情況下,默認編譯?成的庫都是靜態庫。如果SET(BUILD_SHARED_LIBS ON)后,默認?成的為動態庫。
(3)CMAKE_C_FLAGS
設置C 編譯選項,也可以通過指令 ADD_DEFINITIONS()添加。
(4)CMAKE_CXX_FLAGS
設置C++編譯選項,也可以通過指令 ADD_DEFINITIONS()添加。
本篇就介紹到這里,歡迎關注、點贊、分享、收藏。
總結
以上是生活随笔為你收集整理的.so 依赖目录 cmake_CMAKE最全实战(2)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: rtsp服务器搭建_直播系统搭建所用到的
- 下一篇: 辽宁活跃ip段_有泰国女排影子!激情辽宁