CMake 生成靜態(tài)庫(kù)與動(dòng)態(tài)庫(kù)的方法步驟
引言
CMake 實(shí)踐幫助我們對(duì) CMake 有一個(gè)系統(tǒng)全面的了解,并且有大量示例以供參考,至少在實(shí)際項(xiàng)目中可以讓我們有能力看懂并修改項(xiàng)目中現(xiàn)有的 CMake 。
閱讀完 CMake 實(shí)踐文檔,認(rèn)為自己的任務(wù)也就結(jié)束了,可這樣總感覺(jué)不是自己的東西,不如整理一下并吸收其中自己認(rèn)為最有用的東西,這樣也能極大的減輕自己的記憶負(fù)擔(dān)。
與此同時(shí) CMake 實(shí)踐行文組織過(guò)于復(fù)雜,不方便遇到問(wèn)題時(shí)快速查閱,所以我做了一些調(diào)整與總結(jié),希望能夠?qū)ψx者更加友好。
本文不能代替 CMake 實(shí)踐文檔,有時(shí)間還是把 CMake 實(shí)踐文檔認(rèn)真閱讀一遍。
CMake 生成庫(kù)
假設(shè)我們存在一個(gè)這樣的任務(wù):
建立一個(gè)靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù),提供 HelloFunc 函數(shù)以供其他程序編程使用,HelloFunc 向終端輸出 Hello World 字符串。安裝頭文件與共享庫(kù)。
靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)的區(qū)別
- 靜態(tài)庫(kù)的擴(kuò)展名一般為“.a”或“.lib”;動(dòng)態(tài)庫(kù)的擴(kuò)展名一般為“.so”或“.dll”。
- 靜態(tài)庫(kù)在編譯時(shí)會(huì)直接整合到目標(biāo)程序中,編譯成功的可執(zhí)行文件可獨(dú)立運(yùn)行(如果程序編譯成功,即使離開(kāi)靜態(tài)庫(kù),程序也是可以獨(dú)立運(yùn)行)。
- 動(dòng)態(tài)庫(kù)在編譯時(shí)不會(huì)放到連接的目標(biāo)程序中,即可執(zhí)行文件無(wú)法單獨(dú)運(yùn)行(如果程序編譯成功,必須要有動(dòng)態(tài)庫(kù)的存在程序才可以運(yùn)行,比如使用windows運(yùn)行一些游戲程序時(shí),會(huì)報(bào)缺少 .dll 文件的錯(cuò)誤,導(dǎo)致程序無(wú)法正常運(yùn)行,其實(shí)就是缺少動(dòng)態(tài)庫(kù))。
CMake 生成庫(kù)簡(jiǎn)單實(shí)例
按照慣例,我們先來(lái)一個(gè)簡(jiǎn)單地實(shí)例,以便對(duì) CMake 生成庫(kù)有一個(gè)直觀的了解。
創(chuàng)建以下工程結(jié)構(gòu)
yxm@192:~/test3$ tree
.
├── build
├── CMakeLists.txt
└── lib
├── CMakeLists.txt
├── hello.cpp
└── hello.h
2 directories, 4 fileshello.h中的內(nèi)容
#ifndef HELLO_H #define Hello_H void HelloFunc(); #endif
hello.cpp中的內(nèi)容
#include <iostream>
#include "hello.h"
void HelloFunc(){
std::cout << "Hello World" << std::endl;
}項(xiàng)目中的cmake內(nèi)容
PROJECT(HELLO) ADD_SUBDIRECTORY(lib bin)
lib中CMakeLists.txt中的內(nèi)容
SET(LIBHELLO_SRC hello.cpp)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})ADD_LIBRARY 指令詳細(xì)可見(jiàn)下文 CMake 語(yǔ)法。
外部編譯過(guò)程:
進(jìn)入 build,運(yùn)行 cmake …
在 build 目錄下,運(yùn)行 make 命令編譯 Makefile 文件,并生成動(dòng)態(tài)庫(kù)。

CMake 同時(shí)構(gòu)建靜態(tài)庫(kù)與動(dòng)態(tài)庫(kù)
生成動(dòng)態(tài)庫(kù)與靜態(tài)庫(kù)
有上面的例子可以看出,使用 ADD_LIBRARY 指令就可以同時(shí)構(gòu)建靜態(tài)和動(dòng)態(tài)庫(kù):
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
ADD_LIBRARY(hello STATIC ${LIBHELLO_SRC})但是如果使用這種方式,只會(huì)構(gòu)建一個(gè)動(dòng)態(tài)庫(kù),不會(huì)構(gòu)建出靜態(tài)庫(kù),雖然靜態(tài)庫(kù)的后綴是.a,此時(shí)我們可以修改靜態(tài)庫(kù)的名字,這樣是可以同時(shí)構(gòu)建動(dòng)態(tài)庫(kù)和靜態(tài)庫(kù):
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})但是我們往往希望他們的名字是相同的,只是后綴不同而已,此時(shí)可以使用 SET_TARGET_PROPERTIES 指令(該指令詳細(xì)可見(jiàn)下文 CMake 語(yǔ)法),修改lib目錄下CMakeLists.txt文件:
SET(LIBHELLO_SRC hello.cpp)
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})
# 對(duì)hello_static的重名為hello
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")
# cmake 在構(gòu)建一個(gè)新的target 時(shí),會(huì)嘗試清理掉其他使用這個(gè)名字的庫(kù),如果沒(méi)有清理還是會(huì)只會(huì)構(gòu)建一個(gè)動(dòng)態(tài)庫(kù),不會(huì)構(gòu)建出靜態(tài)庫(kù)
SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
# 對(duì)hello_static的重名為hello
SET_TARGET_PROPERTIES(hello PROPERTIES OUTPUT_NAME "hello")
SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)外部編譯過(guò)程:
進(jìn)入 build,運(yùn)行 cmake …
在 build 目錄下,運(yùn)行 make 命令編譯 Makefile 文件,并生成動(dòng)態(tài)庫(kù)與靜態(tài)庫(kù)。

修改動(dòng)態(tài)庫(kù)版本號(hào)
同時(shí)我們還可以修改動(dòng)態(tài)庫(kù)的版本號(hào)
// 一般動(dòng)態(tài)庫(kù)都有一個(gè)版本號(hào)的關(guān)聯(lián) libhello.so.1.2 libhello.so ->libhello.so.1 libhello.so.1->libhello.so.1.2
修改 lib/CMakeLists.txt ,重新構(gòu)建看看結(jié)果:
SET(LIBHELLO_SRC hello.cpp)
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")
SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
SET_TARGET_PROPERTIES(hello PROPERTIES OUTPUT_NAME "hello")
SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)
SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)
安裝共享庫(kù)和頭文件
本例中我們將 hello 的共享庫(kù)安裝到 <prefix>/lib 目錄;將 hello.h 安裝到 <prefix>/include/hello 目錄,這樣共享庫(kù)才能夠被調(diào)用。
修改lib目錄下CMakeLists.txt文件:
SET(LIBHELLO_SRC hello.cpp)
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")
SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
SET_TARGET_PROPERTIES(hello PROPERTIES OUTPUT_NAME "hello")
SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)
# 文件放到該目錄下
INSTALL(FILES hello.h DESTINATION include/hello)
# 二進(jìn)制,靜態(tài)庫(kù),動(dòng)態(tài)庫(kù)安裝都用TARGETS
# ARCHIVE 特指靜態(tài)庫(kù),LIBRARY 特指動(dòng)態(tài)庫(kù),RUNTIME 特指可執(zhí)行目標(biāo)二進(jìn)制。
INSTALL(TARGETS hello hello_static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)外部編譯過(guò)程:
進(jìn)入 build,運(yùn)行 cmake -DCMAKE_INSTALL_PREFIX=/usr …
- 注意:安裝的時(shí)候,指定一下路徑,放到系統(tǒng)下,-D之后加不加空格都可。
- 注意:直接安裝在 usr 系統(tǒng)目錄下,以便后續(xù)可以直接調(diào)用
在 build 目錄下,運(yùn)行 make 命令編譯 Makefile 文件,并生成動(dòng)態(tài)庫(kù)與靜態(tài)庫(kù)。
在 build 目錄下,運(yùn)行 make install

使用外部動(dòng)態(tài)庫(kù)和頭文件
新建一個(gè)目錄來(lái)使用外部共享庫(kù)和頭文件,創(chuàng)建以下工程結(jié)構(gòu):
yxm@192:~/test4$ tree
.
├── build
├── CMakeLists.txt
└── src
├── CMakeLists.txt
└── main.cpp
2 directories, 3 filesmain.cpp中的內(nèi)容
#include <hello.h>
int main(){
HelloFunc();
}項(xiàng)目中的CMakeLists.txt內(nèi)容
PROJECT(HELLO) ADD_SUBDIRECTORY(src bin)
src中CMakeLists.txt中的內(nèi)容
ADD_EXECUTABLE(hello main.cpp)
外部編譯過(guò)程:
進(jìn)入 build,運(yùn)行 cmake …
在 build 目錄下,運(yùn)行 make 命令編譯 Makefile 文件。注意此時(shí) make 會(huì)報(bào)錯(cuò):

解決:make 后頭文件找不到的問(wèn)題
make 時(shí)會(huì)提示找不到頭文件,兩種解決方法:
修改成 include <hello/hello.h> ,但這樣修改代碼冗余。當(dāng)然也可以使用一下關(guān)鍵字:INCLUDE_DIRECTORIES (詳細(xì)見(jiàn)下文 CMake 語(yǔ)法)
這里使用第二種方法,在src下的 CMakeLists.txt 文件中加入頭文件搜索路徑:
INCLUDE_DIRECTORIES(/usr/include/hello) ADD_EXECUTABLE(hello main.cpp)
外部編譯過(guò)程:
進(jìn)入 build,運(yùn)行 cmake …
在 build 目錄下,運(yùn)行 make 命令編譯 Makefile 文件。注意此時(shí)還是 make 會(huì)報(bào)錯(cuò):

解決:找到引用的函數(shù)問(wèn)題
報(bào)錯(cuò)信息:undefined reference to `HelloFunc()',所以我們需要將 .so 文件關(guān)聯(lián)起來(lái)。
解決方法有兩種:
- 關(guān)鍵字:LINK_DIRECTORIES 添加非標(biāo)準(zhǔn)的共享庫(kù)搜索路徑
指定第三方庫(kù)所在路徑,LINK_DIRECTORIES(/home/myproject/libs) - 關(guān)鍵字:TARGET_LINK_LIBRARIES 添加需要鏈接的共享庫(kù)(詳細(xì)見(jiàn)下文 CMake 語(yǔ)法)
? TARGET_LINK_LIBRARIES 的時(shí)候,只需要給出動(dòng)態(tài)鏈接庫(kù)的名字就行了。
這里使用第二種方法,在src下的 CMakeLists.txt 文件中添加需要鏈接的共享庫(kù)(主要要插在executable的后面):
INCLUDE_DIRECTORIES(/usr/include/hello) ADD_EXECUTABLE(hello main.cpp) TARGET_LINK_LIBRARIES(hello libhello.so)
外部編譯過(guò)程:
進(jìn)入 build,運(yùn)行 cmake …
在 build 目錄下,運(yùn)行 make 命令編譯 Makefile 文件。
在 build/bin 目錄下,運(yùn)行 ./hello ,執(zhí)行結(jié)果如下:
yxm@192:~/test4/build$ cd bin/ yxm@192:~/test4/build/bin$ ./hello Hello World
注意:如果你的 linux 虛擬機(jī)是64位會(huì)報(bào)錯(cuò),需要移動(dòng)動(dòng)態(tài)庫(kù)到64位下:

使用外部靜態(tài)庫(kù)
上面的例子使用的是外部動(dòng)態(tài)庫(kù),如果想要使用外部靜態(tài)庫(kù),步驟也是相同的,只需要將上面例子中.so換成.a即可,不過(guò)使用外部靜態(tài)庫(kù)不需要頭文件。
TARGET_LINK_LIBRARIES(main libhello.a)
補(bǔ)充:
特殊的環(huán)境變量 CMAKE_INCLUDE_PATH 和 CMAKE_LIBRARY_PATH
注意:這兩個(gè)是環(huán)境變量而不是 cmake 變量,可以在linux的bash中進(jìn)行設(shè)置
我們上面例子中使用了絕對(duì)路徑INCLUDE_DIRECTORIES(/usr/include/hello)來(lái)指明include路徑的位置,我們還可以使用另外一種方式,使用環(huán)境變量export CMAKE_INCLUDE_PATH=/usr/include/hello
CMake 語(yǔ)法
(1)ADD_LIBRARY 語(yǔ)法
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
- hello:就是正常的庫(kù)名,生成的名字前面會(huì)加上lib,最終產(chǎn)生的文件是libhello.so
- SHARED,動(dòng)態(tài)庫(kù) STATIC,靜態(tài)庫(kù)
- ${LIBHELLO_SRC} :源文件
(2)SET_TARGET_PROPERTIES 語(yǔ)法
這條指令可以用來(lái)設(shè)置輸出的名稱(chēng),對(duì)于動(dòng)態(tài)庫(kù),還可以用來(lái)指定動(dòng)態(tài)庫(kù)版本和 API 版本
- 對(duì)hello_static的重名為hello,例如:SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME “hello”)
- 指定動(dòng)態(tài)庫(kù)版本和 API 版本,例如:SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)
其中VERSION 指代動(dòng)態(tài)庫(kù)版本,SOVERSION 指代 API 版本。
(3)INCLUDE_DIRECTORIES 語(yǔ)法
找頭?件:可以用來(lái)向工程添加多個(gè)特定的頭文件搜索路徑,路徑之間用空格分割
(4)TARGET_LINK_LIBRARIES 語(yǔ)法
TARGET_LINK_LIBRARIES 用于從鏈接庫(kù)到執(zhí)行件上
到此這篇關(guān)于CMake 生成靜態(tài)庫(kù)與動(dòng)態(tài)庫(kù)的方法步驟的文章就介紹到這了,更多相關(guān)CMake 生成靜態(tài)庫(kù)與動(dòng)態(tài)庫(kù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++二叉樹(shù)的前序中序后序非遞歸實(shí)現(xiàn)方法詳細(xì)講解
前序遍歷的順序是根、左、右。任何一顆樹(shù)都可以認(rèn)為分為左路節(jié)點(diǎn),左路節(jié)點(diǎn)的右子樹(shù)。先訪問(wèn)左路節(jié)點(diǎn),再來(lái)訪問(wèn)左路節(jié)點(diǎn)的右子樹(shù)。把訪問(wèn)左路節(jié)點(diǎn)的右子樹(shù)看成一個(gè)子問(wèn)題,就可以完整遞歸訪問(wèn)了2023-03-03
VSCode遠(yuǎn)程開(kāi)發(fā)調(diào)試服務(wù)器c/c++代碼
語(yǔ)音相關(guān)的好多項(xiàng)目要在linux上跑,但代碼開(kāi)發(fā)大多是在PC機(jī)上,本篇簡(jiǎn)單介紹一下怎么在個(gè)人電腦上用VSCode遠(yuǎn)程開(kāi)發(fā)調(diào)試服務(wù)器上的c/c++代碼。感興趣的朋友跟隨小編一起看看吧2020-04-04
C++實(shí)現(xiàn)學(xué)校人員管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)學(xué)校人員管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03
使用單鏈表實(shí)現(xiàn)多項(xiàng)式計(jì)算示例
這篇文章主要介紹了使用單鏈表實(shí)現(xiàn)多項(xiàng)式計(jì)算示例,需要的朋友可以參考下2014-03-03
利用Qt實(shí)現(xiàn)FTP服務(wù)器并支持多客戶端登錄
這篇文章主要為大家詳細(xì)介紹了如何利用Qt實(shí)現(xiàn)FTP服務(wù)器并支持多客戶端登錄,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-12-12
C++簡(jiǎn)單又輕松的講解類(lèi)和對(duì)象中友元函數(shù)
采用類(lèi)的機(jī)制后實(shí)現(xiàn)了數(shù)據(jù)的隱藏與封裝,類(lèi)的數(shù)據(jù)成員一般定義為私有成員,成員函數(shù)一般定義為公有的,依此提供類(lèi)與外界間的通信接口。但是,有時(shí)需要定義一些函數(shù),這些函數(shù)不是類(lèi)的一部分,但又需要頻繁地訪問(wèn)類(lèi)的數(shù)據(jù)成員,這時(shí)可以將這些函數(shù)定義為該類(lèi)的友元函數(shù)2022-06-06
C語(yǔ)言實(shí)現(xiàn)字符串操作函數(shù)的實(shí)例
這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)字符串操作函數(shù)的實(shí)例的相關(guān)資料,開(kāi)發(fā)程序的時(shí)候經(jīng)常使用到一些字符串函數(shù),例如求字符串長(zhǎng)度,拷貝字符串……,需要的朋友可以參考下2017-08-08

