【cmake系列使用教程】java
cmake使用教程(一)-起步linux
cmake使用教程(三)-安裝、測試、系統自檢github
cmake使用教程(四)-文件生成器正則表達式
cmake使用教程(六)-蛋疼的語法shell
cmake使用教程(七)-流程和循環express
cmake使用教程(八)-macro和functionmacos
這個系列的文章翻譯自官方cmake教程:cmake tutorial。
示例程序地址:github.com/rangaofei/t…
不會僅僅停留在官方教程。本人做爲一個安卓開發者,實在是沒有linux c程序開發經驗,望大佬們海涵。教程是在macos下完成,大部分linux我也測試過,有特殊說明的我會標註出來。本教程基於cmake-3.10.2,同時認爲你已經安裝好cmake。
cmake中的file使用也很簡單,與c語言的文件io類似。file命令屬於腳本命令,能夠卸載腳本中。後邊講到的aux_source_directory
屬於工程命令,不能用在腳本中。
首先來講一個我在編寫程序的時候遇到的問題,在學習apue的過程當中,有許多實例小程序,由於IDE用的是Clion,因此須要在cmake腳本中生成執行文件,最開始的時候是簡單的添加add_executable
命令來不斷的增長新的程序構建,隨着學習的深刻,手動添加太麻煩了,因而寫了一個簡單的腳原本半自動構建程序,目錄結構以下:
apue git:(master) ✗ cd src
➜ src git:(master) ✗ tree
.
├── CmakeLists.txt
├── part1
│ ├── CmakeLists.txt
│ ├── copytest.c
│ ├── groupid.c
│ ├── mycopy.c
│ ├── myerror.c
│ ├── myls.c
│ ├── myshell.c
│ ├── mystdcopy.c
│ ├── newshell.c
│ └── processid.c
├── part11
│ ├── CmakeLists.txt
│ ├── pthread1.c
│ ├── pthread2.c
│ ├── pthread3.c
│ └── pthread4.c
├── part3
│ ├── CmakeLists.txt
│ ├── holetest.c
│ └── seektest.c
├── unp_1
│ ├── CMakeLists.txt
│ ├── daytimetcpcli.c
│ ├── daytimetcpcliv6.c
│ └── daytimetcpsrv.c
└── unp_3
├── CMakeLists.txt
└── byteorder.c
複製代碼
src是apue的源代碼目錄,包含多個章節對應的文件夾和一個主cmakelist文件,章節文件夾下是具體的c和h文件和一個cmakelist文件,看一下part3章節文件夾中的cmakelist文件:
AUX_SOURCE_DIRECTORY(. PART_THREE)
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
FOREACH (FILE ${PART_THREE})
MESSAGE(STATUS ${FILE})
STRING(REPLACE "./" "" LIB_NAME ${FILE})
STRING(REPLACE ".c" "" LIB_NAME ${LIB_NAME})
add_executable(${LIB_NAME} ${FILE})
target_link_libraries(${LIB_NAME} apue.a)
ENDFOREACH ()
複製代碼
這段代碼首先將當前章節的全部文件收入到PART_THREE變量中,以list形式存儲,下邊兩行代碼設置了可執行文件和庫文件的存儲路徑,而後FOREACHE循環中輸出了全部的可執行文件和連接庫,可執行文件名稱是用.c文件用正則替換無用信息後生成的。
而後在主cmakelist中添加add_subdirectory(part3)
後直接執行便可。這也是今天要講的主要內容。
file(WRITE <filename> <content>...)
file(APPEND <filename> <content>...)
複製代碼
將content的內容寫入filename中,假如文件不存在則會建立文件,假如filename中包含路徑,則相應的文件夾也會被建立。WRITE會擦出文件內容,從新寫入content,APPEND會在文件末尾追加內容。寫一個最簡單的測試:
file(WRITE test.txt "this is a test to wirte\n")
file(APPEND test/test.txt "this is a test to append")
file(APPEND test.txt "this is a test to append")
複製代碼
此時目錄結構以下:
.
└── write.cmake
0 directories, 1 file
複製代碼
執行該腳本後:
➜ Stepfile git:(master) ✗ cmake -P write.cmake
➜ Stepfile git:(master) ✗ tree
.
├── test
│ └── test.txt
├── test.txt
└── write.cmake
1 directory, 3 files
複製代碼
前邊介紹過configure_file這個命令,是用來在構建工程時替換文件內容的,注意一下區別。
file(READ <filename> <variable>
[OFFSET <offset>] [LIMIT <max-in>] [HEX])
複製代碼
這個也比較簡單: 將filename文件中的內容讀取到variable總,能夠指定OFFSET的值,也就是開始讀取的位置,指定LISTMI的值,讀取的長度,HEX是否以16進制形式讀取。
file(STRINGS <filename> <variable> [<options>...])
複製代碼
相似於讀取字符碼,而不讀取字節碼。這個命令會將filename中的字符串讀取到variable中,而且variable是一個list,每一個元素保存每行的內容。二進制文件不會被讀取,而且換行符會被忽略。舉個例子,咱們剛纔寫入的test.txt
的文件內容是:
this is a test to wirte
this is a test to append
have tab #這個是我手動添加的
複製代碼
咱們讀取這個文件並打印結果,編寫string.cmake
文件以下:
file(STRINGS test.txt strings)
foreach(str IN LISTS strings)
message(STATUS ${str})
endforeach(str)
複製代碼
由於結果會用list保存,因此用foreach循環來查看結果:
-- this is a test to wirte
-- this is a test to append
-- have tab
複製代碼
關於一些選項,用的不太多:
OPTION | 說明 |
---|---|
LENGTH_MAXIMUM | 讀取字符的最大個數 |
LENGTH_MINIMUM | 讀取的字符的最少個數 |
LIMIT_COUNT | 提取的不一樣字符的最大數量 |
LIMIT_INPUT | 限制讀取的最大字節 |
LIMIT_OUTPUT | 限制寫入變量的最大字節 |
NEWLINE_CONSUME | 不忽略換行符 |
NO_HEX_CONVERSION | 不須要自動轉換爲16進制 |
REGEX | 提取匹配正則表達式的字符串 |
ENCODING | 從新編碼UTF-8, UTF-16LE, UTF-16BE, UTF-32LE, UTF-32BE |
file(<HASH> <filename> <variable>)
複製代碼
利用這個命令能夠提取出文件的hash碼
MD5,SHA1,SHA224,SHA256,SHA384,SHA512,SHA3_224,SHA3_256,SHA3_384,SHA3_512
複製代碼
若是看過個人bomebrew教程可應該知道,在生成formula.rb
文件的時候須要填寫打包好的文件的SHA256來驗證下載文件的完整性,因此能夠利用這個寫一個簡單的腳原本輸出hash值,寫一個簡單的例子吧:
file(SHA256 test.txt hash)
message(STATUS ${hash})
複製代碼
-- f9bb70f1a2036a73f611858d01a8fb498efc7c83568faf0c74e5a52037492702
複製代碼
file(GLOB <variable>
[LIST_DIRECTORIES true|false] [RELATIVE <path>]
[<globbing-expressions>...])
file(GLOB_RECURSE <variable> [FOLLOW_SYMLINKS]
[LIST_DIRECTORIES true|false] [RELATIVE <path>]
[<globbing-expressions>...])
複製代碼
GLOB命令將全部匹配<globbing-expressions>
(可選,假如不寫,毛都匹配不到)的文件挑選出來,默認以字典順序排序。
file(GLOB files *)
foreach(file IN LISTS files)
message(STATUS ${file})
endforeach(file)
複製代碼
這段代碼的意思是挑選出當前文件下的全部文件,而後打印:
-- /Users/rangaofei/Documents/program/tutorial/Stepfile/filelist.cmake
-- /Users/rangaofei/Documents/program/tutorial/Stepfile/hash.cmake
-- /Users/rangaofei/Documents/program/tutorial/Stepfile/string.cmake
-- /Users/rangaofei/Documents/program/tutorial/Stepfile/test
-- /Users/rangaofei/Documents/program/tutorial/Stepfile/test.txt
-- /Users/rangaofei/Documents/program/tutorial/Stepfile/write.cmake
複製代碼
其實我這個文件夾下的內容以下:
.
├── filelist.cmake
├── hash.cmake
├── string.cmake
├── test
│ └── test.txt
├── test.txt
└── write.cmake
1 directory, 6 files
複製代碼
test是一個文件夾,可是在腳本中輸出了這個文件夾。假如咱們不想要這個文件夾,咱們能夠經過LIST_DIRECTORIES
設置爲false便可(默認爲true),修改第一行代碼以下:
file(GLOB files LIST_DIRECTORIES false *)
複製代碼
-- /Users/rangaofei/Documents/program/tutorial/Stepfile/filelist.cmake
-- /Users/rangaofei/Documents/program/tutorial/Stepfile/hash.cmake
-- /Users/rangaofei/Documents/program/tutorial/Stepfile/string.cmake
-- /Users/rangaofei/Documents/program/tutorial/Stepfile/test.txt
-- /Users/rangaofei/Documents/program/tutorial/Stepfile/write.cmake
複製代碼
此次只輸出了文件,而文件夾沒有在裏邊,假如咱們不須要絕對路徑,只須要相對某個文件夾的路徑,則能夠經過設置RELATIVE
的值來設置。 將文件修改以下:
set(CUR ${CMAKE_CURRENT_SOURCE_DIR})
file(GLOB files LIST_DIRECTORIES false RELATIVE ${CUR}/.. *)
foreach(file IN LISTS files)
message(STATUS ${file})
endforeach(file)
複製代碼
咱們設置了CUR爲當前的文件夾,而後設置相對路徑爲當前文件夾的上級文件夾,而個人當前文件夾名稱爲Stepfile,則輸出會包含當前文件夾的名字+文件名字:
-- Stepfile/filelist.cmake
-- Stepfile/hash.cmake
-- Stepfile/string.cmake
-- Stepfile/test.txt
-- Stepfile/write.cmake
複製代碼
就是這麼蛋疼。還要說一下這個蛋疼的僞正則匹配,通常文件是夠用的。
*.cxx - 匹配全部的cxx結尾的文件
*.vt? - 匹配全部的vta,...,vtz等文件
f[3-5].txt - 匹配f3.txt, f4.txt, f5.txt這三個文件
複製代碼
cmake官方不推薦使用GLOB來收集文件,由於在工程或者模塊中的CMakeLists.txt文件未更改而用file搜尋的文件夾下有文件的刪除或者增長,cmake構建並不會知曉,而是使用舊的list。
這個命令是用來列出全部子文件夾中的文件和當前全部文件,具體深度多少我也不知道。用法基本同上,只是多了一個FOLLOW_SYMLINKS
可選項。2.6.1版本以前對於連接的文件夾一樣會列出全部的連接過去的文件夾下的文件,由於這樣會引發一些麻煩,因此在之後的版本中去掉了這個屬性,而是將連接當作一個文件,不會列出連接到的文件夾下的文件。假如須要列出,則添加FOLLOW_SYMLINKS
參數便可。
cmake_minimum_required(VERSION 3.6)
# if(POLICY CMP0009)
# cmake_policy(SET CMP0009 NEW)
# endif()
set(CUR ${CMAKE_CURRENT_SOURCE_DIR})
file(GLOB_RECURSE files FOLLOW_SYMLINKS LIST_DIRECTORIES true RELATIVE ${CUR}/.. *)
foreach(file IN LISTS files)
message(STATUS ${file})
endforeach(file)
複製代碼
這段代碼將會列出當前全部文件、子文件夾中的文件以及連接中的文件。
aux_source_directory(<dir> <variable>)
複製代碼
注意這個命令不能用於script中,他是project命令。
尋找dir文件夾下全部的源文件,存入variable中。這個命令與以前的命令有所區別,由於它只會蒐集當前設置語言的文件,cmake默認的設置語言是c/cxx,則會收集到的文件只有這些語言能識別的文件,好比在step中添加以下代碼
aux_source_directory(./ SRCLIST)
foreach(file IN LISTS SRCLIST)
message(STATUS ${file})
endforeach(file)
複製代碼
當前目錄結構以下
.
├── CMakeLists.txt
├── TutorialConfig.h.in
├── build
└── tutorial.cxx
複製代碼
看一下輸出了什麼
-- ./tutorial.cxx
複製代碼
只有一個文件被假如list中了。
file(RENAME <oldname> <newname>)
複製代碼
重命名文件或者文件夾
file(REMOVE [<files>...])
file(REMOVE_RECURSE [<files>...])
複製代碼
刪除指定的文件,REMOVE_RECURSE
則會刪除文件和文件夾,假如不存在,不會拋出錯誤。
file(MAKE_DIRECTORY [<directories>...])
複製代碼
遞歸建立文件,包括路徑中的文件夾
file(RELATIVE_PATH <variable> <directory> <file>)
複製代碼
計算file相對於directory的相對路徑,存入variable中。相似於前邊的收集文件。
file(TO_CMAKE_PATH "<path>" <variable>)
file(TO_NATIVE_PATH "<path>" <variable>)
複製代碼
在cmake路徑和本地路徑之間相互轉換。cmake路徑使用的是/
file(DOWNLOAD <url> <file> [<options>...])
file(UPLOAD <file> <url> [<options>...])
複製代碼
這兩個命令真是讓個人菊花緊到極致了。第一個是從url下載文件命名爲file,第二個是將本地文件file上傳至url。 如下的option適用於這兩個命令
參數 | 說明 |
---|---|
INACTIVITY_TIMEOUT | 超時時間 |
LOG | 將日誌寫入變量中 |
SHOW_PROGRESS | 顯示進度 |
STATUS | a;b 形式,a是返回的狀態碼,b是錯誤代碼,假如沒錯誤,b是0(鬼知道,我沒試) |
TIMEOUT | 鏈接超時時間 |
USERPWD : | 用戶名和密碼 |
HTTPHEADER | http請求頭 |
EXPECTED_HASH ALGO= | 驗證算法(適用於下載) |
file(TIMESTAMP <filename> <variable> [<format>] [UTC])
複製代碼
將filename文件的時間戳存儲在varibale中。
file(GENERATE OUTPUT output-file
<INPUT input-file|CONTENT content>
[CONDITION expression])
複製代碼
這個命令不能用於script,只能在project中才有做用。 在教程四中咱們介紹過用add_custom_command
方式在構建時添加文件,如今講的file方式,基本相似,INPUT和CONTENT必需要選一個,前者是以文件爲內容,後者是以字符串爲內容。在3.10版本以後,INPUT使用的是相對當前文件夾的路徑,OUTPUT使用的是生成的文件夾的路徑。另外,只有當condition爲真得時候纔會執行生成文件,而且表達式的值必須返回0或者1。假如設置的最小版本小於3.10,徐志明CMP0070,設置爲new,則與3.10相同,設置爲old,則是相對路徑,不測試old了,由於之後不會用這個。
在step1中cdCMakeLists.txt中添加
if(POLICY CMP0070)
cmake_policy(SET CMP0070 NEW)
endif()
file(GENERATE OUTPUT out.txt
CONTENT "java"
)
複製代碼
在build文件夾中確實生成了out.txt
,且內容是java。
還有兩個命令:
file(<COPY|INSTALL> <files>... DESTINATION <dir>
[FILE_PERMISSIONS <permissions>...]
[DIRECTORY_PERMISSIONS <permissions>...]
[NO_SOURCE_PERMISSIONS] [USE_SOURCE_PERMISSIONS]
[FILES_MATCHING]
[[PATTERN <pattern> | REGEX <regex>]
[EXCLUDE] [PERMISSIONS <permissions>...]] [...])
複製代碼
copy和install文件到指定目標文件夾。
輸入的文件是相對於當前文件夾的相對路徑,而目標文件夾則是相對於構建文件夾的相對路徑。
COPY可保留輸入文件時間戳,並優化文件(若是在目標文件夾已經存在相同的文件且時間戳相同)。除非給出顯式權限或NO_SOURCE_PERMISSIONS
,不然將保留權限(默認值爲USE_SOURCE_PERMISSIONS
)。
INSTALL與COPY基本相同,可是會輸出install日誌,而且會默認使用NO_SOURCE_PERMISSIONS
權限。
file(LOCK <path> [DIRECTORY] [RELEASE]
[GUARD <FUNCTION|FILE|PROCESS>]
[RESULT_VARIABLE <variable>]
[TIMEOUT <seconds>])
複製代碼
相似於java中的同步鎖。
假如指定了[DIRECTORY]選項則鎖定<path>/cmake.lock
文件,不然鎖定<path>
文件。GUARD用來肯定做用域,默認是PROCESS,其餘兩個看意思也能明白。RELEASE用來顯式的解鎖。
假如沒有指定TIMEOUT選項,則系統會一直等到文件被上鎖或者發生致命的錯誤;假如設置爲0則會當即執行lock操做並返回狀態碼;假如設置不爲0,則會等待相應的時間(以秒爲單位)來上鎖。
假如沒有設置RESULT_VARIABLE
,則任何錯誤都會阻止程序運行。設置的話結果會存儲到varobale中,成功是0,錯誤會存儲錯誤信息。
請注意,鎖是建議性的 - 不能保證其餘進程會尊重此鎖,即鎖定同步兩個或多個共享某些可修改資源的CMake實例。 應用於DIRECTORY選項的相似邏輯 - 鎖定父目錄不會阻止其餘LOCK命令鎖定任何子目錄或文件。
試圖鎖定文件兩次是不容許的。 若是它們不存在,任何中間目錄和文件自己都將被建立。 RELEASE操做中忽略GUARD和TIMEOUT選項。