前言
CMake實戰(zhàn)教程(一)
CMake實戰(zhàn)教程(二)
從本小節(jié)開始,后面所有的構(gòu)建我們都將采用 out-of-source build
外部構(gòu)建的方式去編寫構(gòu)建工程代碼,構(gòu)建目錄是工程目錄下的 build
目錄。
從上一篇文章我們就知道,通過aux_source_directory
命令可以掃描某個目錄下的所有源碼,但是更深一層的目錄源碼就找不到了,因此不滿足我們真正工程項目的需要,畢竟真的工程會有很多個目錄的,這些目錄在的地方還不一樣,現(xiàn)在就來解決這個問題…
定個小目標(biāo)
- CMake自動構(gòu)建多個源碼目錄下的工程
實現(xiàn)
首先,起碼工程中得有多個目錄對吧,按照編程習(xí)慣,我喜歡把源碼放到 src
目錄下,把頭文件放到 inc
目錄下,那么把上一篇文章的代碼分離放開,具體如下:
jie@pc:~/github/cmake/section4$ tree
.
├── build.sh
├── CMakeLists.txt
├── inc
│ └── power.h
├── main.c
└── power
├── CMakeLists.txt
└── power.c
2 directories, 6 files
很顯然,從上面的目錄結(jié)構(gòu)中可以看出,有兩個CMakeLists.txt
文件,一個是在根目錄下,一個是在src目錄下:
./CMakeLists.txt
./src/CMakeLists.txt
其實無論在何處,CMakeLists.txt
文件都可以被 CMake
命令識別并且自動構(gòu)建,但是,我們自動構(gòu)建的時候,都會有一個頂層目錄CMakeLists.txt
文件,它就是cmake的入口文件,而在頂層目錄下,如果某些子目錄中存在CMakeLists.txt
文件,那么 CMake
可以將頂層的環(huán)境變量傳遞到子目錄下,就好比,你在某個文件中定義了個全局變量,你在那個文件中的子函數(shù)都可以使用這個全局變量,有點類似的道理,說白了就是作用域。。。
添加子目錄
那么為了能讓 CMake
去識別到src
目錄下的./src/CMakeLists.txt
文件,我們可以在頂層./CMakeLists.txt
文件添加一個子目錄,用以下這個命令:
add_subdirectory(source_dir [binary_dir]
[EXCLUDE_FROM_ALL])
add_subdirectory
這條命令的作用是為構(gòu)建添加一個子路徑。
source_dir
選項指定了CMakeLists.txt
源文件和代碼文件的位置。如果source_dir
是一個相對路徑,那么source_dir
選項會被解釋為相對于當(dāng)前的目錄,不過它也可以是一個絕對路徑。在source_dir
指定路徑下的CMakeLists.txt
將會在當(dāng)前輸入文件的處理過程執(zhí)行到該命令之前,立即被CMake
處理。binary_dir
選項指定了輸出文件的路徑,同樣的,binary_dir
可以相對路徑,也可以是一個絕對路徑。不過杰杰目前暫未使用到binary_dir
,感興趣的兄弟姐妹可以自己去了解它。- 如果指定了
EXCLUDE_FROM_ALL
選項,在子路徑下的目標(biāo)默認(rèn)不會被包含到父路徑的 ALL 目標(biāo)里,并且也會被排除在工程文件之外,用戶必須顯式構(gòu)建在子路徑下的目標(biāo)。
添加路徑
include_directories([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)
同理可以使用include_directories
命令將給定的路徑添加到編譯器搜索包含文件(.h 文件)的路徑列表中,這是為了讓編譯器找到合適的頭文件。如果指定了 SYSTEM
選項,編譯器將會認(rèn)為該路徑是某種平臺上的系統(tǒng)包含路徑。
遍歷
在cmake中,很多變量都是以列表的形式存在,比如上面的兩個命令,添加頭文件與添加子目錄,我們可以用列表的形式添加進(jìn)去,比如include_directories(dir1 dir2 dir3)
,那么這些dir,那么cmake也提供了遍歷這些列表的方法,這與C語言中的for循環(huán)都差不多,可以使用以下命令去遍歷它:
foreach(loop_var arg1 arg2 ...)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
endforeach(loop_var)
注意,這個命令需要配合 endforeach 命令使用,endforeach 表示結(jié)束遍歷
所有的 foreach
和與之匹配的 endforeach
命令之間的命令,會根據(jù)list的每個變量都執(zhí)行一遍,在每次迭代中,循環(huán)變量${loop_var}將會被設(shè)置為 list 中的當(dāng)前變量值。
為了演示,我就將foreach
命令去變量指定的INCDIRS
(雖然只有一個變量,不過不影響演示),把INCDIRS
列表的內(nèi)容一個個添加到路徑中:
### 頭文件
set(INCDIRS "inc")
# 添加頭文件目錄
foreach(incdir ${INCDIRS})
include_directories(${incdir})
endforeach()
當(dāng)然,我在這提到遍歷并不是因為必須要使用這個命令,而是為后續(xù)的文章做鋪墊。
構(gòu)建庫
如果一個工程中有非常多的源碼,那么總不能將所有的源碼都使用add_executable
命令去生成可執(zhí)行文件是不是,我們可以讓這個可執(zhí)行文件去依賴庫,靜態(tài)庫,動態(tài)庫都可以,那么在這里就簡單說說add_executable
命令依賴靜態(tài)庫生成可執(zhí)行文件,那么問題來了,靜態(tài)庫從哪來?
很顯然,靜態(tài)庫是我們的一些源碼文件生成的,在生成靜態(tài)庫后,再通過靜態(tài)庫去構(gòu)建可執(zhí)行文件,那么就用到了CMake中的add_library
命令:
add_library( [STATIC | SHARED | MODULE]
[EXCLUDE_FROM_ALL]
source1 source2 ... sourceN)
它的作用就是使用指定的源文件向工程中添加一個庫。添加一個名為
的庫文件,
在一個工程的全局域內(nèi)必須是唯一的,這個庫文件將會根據(jù)命令里列出的源文件(source1 source2 ... sourceN
)來創(chuàng)建。待構(gòu)建的庫文件的實際文件名根據(jù)對應(yīng)平臺的命名約定來構(gòu)造(比如 可以是lib
或者
)。通過指定STATIC
,SHARED
,或者 MODULE
參數(shù)用來指定要創(chuàng)建的庫的類型。
STATIC
庫是目標(biāo)文件的歸檔文件,在鏈接其它目標(biāo)的時候使用。SHARED
庫會被動態(tài)鏈接,在運行時被加載。MODULE
庫是不會被鏈接到其它目標(biāo)中的插件,但是可能會在運行時使用 dlopen-系列的函數(shù)動態(tài)鏈接。如果沒有類型被顯式指定,這個選項將會根據(jù)變量BUILD_SHARED_LIBS
的當(dāng)前值是否為真決定是STATIC
還是SHARED
。
# 查找當(dāng)前目錄下的所有源文件
# 并將名稱保存到 DIR_LIB_SRCS 變量
file(GLOB_RECURSE DIR_LIB_SRCS *.c)
file(GLOB_RECURSE DIR_LIB_HDRS *.h)
### 生成鏈接庫
add_library(${LIB_NAME} ${DIR_LIB_SRCS} ${DIR_LIB_HDRS})
當(dāng)然,為了我們的CMakeLists.txt
文件有更好的兼容性,也不必每次都去寫對應(yīng)的庫文件名字
,杰杰自己會使用正則表達(dá)式去解析當(dāng)前目錄的名字,并以這個目錄名字去作為庫的命名,正則表達(dá)式如下:
# 正則表達(dá)式得到當(dāng)前目錄名字作為提供給上一層的庫名字
string(REGEX REPLACE ".*/(.*)" "\\\\1" LIB_NAME ${CMAKE_CURRENT_SOURCE_DIR})
工程demo
頂層./CMakeLists.txt
文件內(nèi)容:
# CMake 最低版本號要求
cmake_minimum_required(VERSION 2.8)
# 項目信息
project(targets)
# 設(shè)置可執(zhí)行文件目標(biāo)
set(TARGETS "targets")
# 子目錄
set(SUBDIRS "power")
# 頭文件
set(INCDIRS "inc")
# 指定編譯器
set(CMAKE_C_COMPILER "gcc")
#判斷編譯器類型,如果是gcc編譯器,則在編譯選項中加入c++11支持
if(CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}")
endif(CMAKE_COMPILER_IS_GNUCXX)
#指定編譯類型
SET(CMAKE_BUILE_TYPE "RELEASE")
# 查找當(dāng)前目錄下的所有源文件
# 并將名稱保存到 DIR_SRCS 變量
aux_source_directory(. DIR_SRCS)
# 指定生成目標(biāo)
add_executable(${TARGETS} ${DIR_SRCS})
# 添加頭文件目錄
foreach(incdir ${INCDIRS})
include_directories(${incdir})
endforeach()
# 添加子目錄
foreach(subdir ${SUBDIRS})
add_subdirectory(${subdir})
endforeach()
# 添加鏈接庫
target_link_libraries(${TARGETS} ${SUBDIRS})
src
目錄下的./src/CMakeLists.txt
文件內(nèi)容:
# 查找當(dāng)前目錄下的所有源文件
# 并將名稱保存到 DIR_LIB_SRCS 變量
file(GLOB_RECURSE DIR_LIB_SRCS *.c)
file(GLOB_RECURSE DIR_LIB_HDRS *.h)
# 正則表達(dá)式得到當(dāng)前目錄名字作為提供給上一層的庫名字
string(REGEX REPLACE ".*/(.*)" "\\\\1" LIB_NAME ${CMAKE_CURRENT_SOURCE_DIR})
# 生成鏈接庫
add_library(${LIB_NAME} ${DIR_LIB_SRCS} ${DIR_LIB_HDRS})
最后直接運行./build
去構(gòu)建工程與編譯工程即可
代碼下載
https://github.com/jiejieTop/cmake
-
源碼
+關(guān)注
關(guān)注
8文章
639瀏覽量
29185 -
代碼
+關(guān)注
關(guān)注
30文章
4779瀏覽量
68521 -
Build
+關(guān)注
關(guān)注
0文章
26瀏覽量
12053
發(fā)布評論請先 登錄
相關(guān)推薦
評論