1.安裝
?$sudo apt-get install cmake
2.示例:簡單的文件目錄
? ? sample |——Demo? (盛放可執行程序binary directory)
? ? ? ? ? ? ? ? ? ? ? ? ? ? |——CMakeLists.txt (內容為:include_directories? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (${HELLO_SOURCE_DIR}/Hello? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? #確認compiler能在Hello 庫中找到它include的庫
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?link_directories (${HELLO_BINARY_DIR}/Hello) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?#確認一旦built時,linker能找到Hello庫
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? add_executable (helloDemo demo.cxx demo_b.cxx) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?#可執行文件叫做helloDemo,它的源碼文件是"demo.cxx" ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?#和"demo_b.cxx"
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?target_link_libraries (helloDemo Hello) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?#link可執行文件helloDemo 到Hello lib ? ? ? )
? ? ? ? ? ? ? ? | ——Hello? (盛放源代碼source directory)
? ? ? ? ? ? ? ? ? ? ? ? ? ? |——CMakeLists.txt (內容為:add_library (Hello hello.cxx) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?#創建Hello 庫,源文件為hello.cxx) ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? |——CMakeLists.txt? (內容為:project (HELLO) #工程名
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? add_subdirectory (Hello) # 子目錄,路徑為 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ${HELLO_SOURCE_DIR})
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? add_subdirectory (Demo) #子目錄 ,路徑為 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ${HELLO_BINARY_DIR} ? )
3.CMake 執行過程
? ? ? CMake在主目錄執行時,會處理該目錄下CMakeLists.txt文件,然后進入到子目錄,處理子目錄下的CMakeLists.txt.
? ? ? 從字面上看,我們差不多可以理解這三個文件的涵義。第一個CMakeLists.txt文件指定包含Hello和Demo兩個子目錄。第二個Hello中的CMakeLists.txt文件則指定生成Hello庫文件,第三個Demo中的CMakeLists.txt文件則是生成一個可執行文件helloDemo,另外兩個附加語句則用來指明頭文件路徑以及所要鏈接的庫。雖然要寫三個CMakeLists文件,但每個文件都非常簡單,總共算起來,并不比一個Makefile文件多多少。更重要的是,這其中隱含著linux哲學:分而治之。每個模塊自行編寫配置文件,只負責自己份內的事務,所以可擴展性好。在進行大型項目開發,就可以體現出優勢了。
? ? ? CMakeLists.txt相當于定義了一套生成Makefile文件的規則,下面就可以生成Makefile文件了,命令如下:
$cmake .
.?表示當前目錄,如果CMakeLists.txt不在當前目錄,請在cmake后面指定。命令執行后,在主目錄下和Demo、Hello子目錄下均會生成一個Makefile文件,有了這個文件,我們就可以敲入make編譯目標程序了。
4.項目文件組織
? ? ? 我們從一個sample入手,了解了CMake的基本用法和語法。但這個例子與實際開發還有一段距離,主要存在以下幾點問題: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?1.生成的二進制程序和源程序混在一起 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 2.使用gcc進程程序編譯,而不是使用交叉編譯工具 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 3.為指定編譯選項,通常會生成debug版本供調試用,release版本用于發布
? ? ? 一個項目,通常包含若干子模塊。比如上一篇的sample,我們可以認為它包含兩個子模塊,Hello為程序庫,Demo為主程序。很少有項目會把目標二進制文件和源程序放在一起的,通常會建立一個bin目錄,存放生成的二進制文件,發布程序則放在release。根據我在項目開發中的習慣,將目錄結構修改如下:
CMakeSample
|--- release? (存放程序發布相關文件,包括程序文件、腳本、參數等。)
|--- doc(項目開發中的相關文檔,如設計說明以及通過doxgen等工具從代碼中生成的文檔。)
|--- lib(存放項目中使用的第三方庫)
|--- source(自己編寫的庫不放在lib,應該作為項目的一個模塊放在source目錄下。)
|--- include (包含整個項目中使用的公共頭文件,若子模塊中的頭文件僅被它使用,不放)
|--- bin (bin目錄存放編譯后的調試版本代碼。)
|--- Hello
|--- Demo
其它的子目錄則為各模塊的代碼及頭文件。
按照以上目錄結構,將Hello下的hello.h移到include目錄,因為這個頭文件被Demo模塊包含。這個sample中未使用第三方庫,所以暫時為空。
5.CMake的內置變量
從上文中我們知道,通過set語句可以自定義變量,然而,CMake還包含大量的內置變量,這些變量和自定義變量的用法沒有區別,下面就列出一些常用的變量:
CMAKE_C_COMPILER
指定C編譯器,通常,CMake運行時能夠自動檢測C語言編譯器。進行嵌入式系統開發時,通常需要設置此變量,指定交叉編譯器。
CMAKE_CXX_COMPILER
指定C++編譯器
CMAKE_C_FLAGS
指定編譯C文件時編譯選項,比如-g指定產生調試信息。也可以通過add_definitions命令添加編譯選項。
EXECUTABLE_OUTPUT_PATH
指定可執行文件存放的路徑。
LIBRARY_OUTPUT_PATH
指定庫文件放置的路徑
CMAKE_BUILD_TYPE ? ??build 類型(Debug, Release),-DCMAKE_BUILD_TYPE=Debug
BUILD_SHARED_LIBS ? ??Switch between shared and static libraries
內置變量的使用:
>>?在CMakeLists.txt中指定,使用set
>> cmake命令中使用,如cmake -DBUILD_SHARED_LIBS=OFF
6.CMake的常用命令
除了內置變量,我們還可以通過命令來修改編譯選項,現將一些常用的命令列出來:
include_directories
指定頭文件的搜索路徑,相當于指定gcc編譯器的-I參數
link_directories
動態鏈接庫或靜態鏈接庫的搜索路徑,相當于指定gcc的-L參數
add_subdirectory
包含子目錄,當工程包含多個子目錄時,此命令有用
add_definitions
添加編譯參數,比如add_definitions(-DDEBUG)將在gcc命令行添加DEBUG宏定義
add_executable
編譯可執行程序
target_link_libraries
指定鏈接庫,相同于指定-l參數
7.CMake語法介紹
CMake語法非常簡單,包含注釋、命令和空格。以#開頭的行為注釋行,命令則由命令名、括號及以空格進行分隔的參數組成。命令可以是諸如add_library這樣的內置命令,也可以是子定義的宏或者函數。CMake的輸入是主目錄下的CMakeLists.txt文件,該文件可以使用include或者add_directory命令添加其它的輸入文件。
命令的形式如下:
command (args ...)
其中command為命令名,args為空格分隔的參數列表,如果參數中包含空格,使用雙引號引起來。命令不區分大小寫。
lists and strings. CMake的基本數據類型為字符串,字符串又可以組成list類型,有兩種方式:一種通過分號分隔,一種通過空格分隔。比如以下例子給VAR賦了同樣的值:
set(VAR a;b;c)? ? set(VAR a b c)
字符串列表主要用于foreach進行迭代,有些命令也用于對list進行處理。
CMake支持字符串和list類型的簡單變量,變量以${VAR}形式引用。多個參數可以用set命令組成一個list,命令將展開list,例如:
set(Foo a b c)
command(${Foo})
等價于
command(a b c)
如果你希望將list當作一個參數傳遞給命令,就應該用雙引號把list引起來,如command("${Foo}")等價于command("a b c")
寫CMakeLists.txt文件就象寫一個簡單的程序,CMake提供了三種流程控制結構:
條件語句if
# some_command will be called if the variable's value is not:
# empty, 0, N, NO, OFF, FALSE, NOTFOUND, or -NOTFOUND.
if(var)
some_command(...)
endif(var)
循環結構
set(VAR a b c)
# loop over a, b,c with the variable f
foreach(f ${VAR})
some_command(${f})
endforeach(f)
宏和函數,函數在2.6及以上版本才支持,函數和宏的區別在于函數中可定義局部變量,而宏定義的變量都是全局變量。
# define a macro hello
macro(hello MESSAGE)
message(${MESSAGE})
endmacro(hello)
# call the macro with the string "hello world"
hello("hello world")
# define a function hello
function(hello MESSAGE)
message(${MESSAGE})
endfunction(hello)
project(HELLO)? #指定項目名稱,生成的VC項目的名稱;
>>使用${HELLO_SOURCE_DIR}表示項目根目錄
include_directories:指定頭文件的搜索路徑,相當于指定gcc的-I參數
>> include_directories (${HELLO_SOURCE_DIR}/Hello)??#增加Hello為include目錄
link_directories:動態鏈接庫或靜態鏈接庫的搜索路徑,相當于gcc的-L參數
>> link_directories (${HELLO_BINARY_DIR}/Hello)?????#增加Hello為link目錄
add_subdirectory:包含子目錄
>> add_subdirectory (Hello)
add_executable:編譯可執行程序,指定編譯,好像也可以添加.o文件
>> add_executable (helloDemo demo.cxx demo_b.cxx)???#將cxx編譯成可執行文件——
add_definitions:添加編譯參數
>> add_definitions(-DDEBUG)將在gcc命令行添加DEBUG宏定義;
>> add_definitions( “-Wall -ansi –pedantic –g”)
target_link_libraries:添加鏈接庫,相同于指定-l參數
>> target_link_libraries(demo Hello) #將可執行文件與Hello連接成最終文件demo
add_library:
>> add_library(Hello hello.cxx)??#將hello.cxx編譯成靜態庫如libHello.a
add_custom_target:
message( status|fatal_error, “message”):
set_target_properties( ... ): lots of properties... OUTPUT_NAME, VERSION, ....
link_libraries( lib1 lib2 ...): All targets link with the same set of libs
8. FAQ
1)??怎樣獲得一個目錄下的所有源文件
>> aux_source_directory( )
>>?將dir中所有源文件(不包括頭文件)保存到變量variable中,然后可以add_executable (ss7gw ${variable})這樣使用。
2)??怎樣指定項目編譯目標
>>??project命令指定
3)??怎樣添加動態庫和靜態庫
>> target_link_libraries命令添加即可
4)??怎樣在執行CMAKE時打印消息
>> message([SEND_ERROR | STATUS | FATAL_ERROR] "message to display" ...)
>>?注意大小寫
5)??怎樣指定頭文件與庫文件路徑
>> include_directories與link_directories
>>可以多次調用以設置多個路徑
>> link_directories僅對其后面的targets起作用
6)??怎樣區分debug、release版本
>>建立debug/release兩目錄,分別在其中執行cmake -DCMAKE_BUILD_TYPE=Debug(或Release),需要編譯不同版本時進入不同目錄執行make即可;
Debug版會使用參數-g;Release版使用-O3 –DNDEBUG
>>?另一種設置方法——例如DEBUG版設置編譯參數DDEBUG
IF(DEBUG_mode)
add_definitions(-DDEBUG)
ENDIF()
在執行cmake時增加參數即可,例如cmake -D DEBUG_mode=ON
7)??怎樣設置條件編譯
例如debug版設置編譯選項DEBUG,并且更改不應改變CMakelist.txt
>>?使用option command,eg:
option(DEBUG_mode "ON for debug or OFF for release" ON)
IF(DEBUG_mode)
add_definitions(-DDEBUG)
ENDIF()
>>?使其生效的方法:首先cmake生成makefile,然后make edit_cache編輯編譯選項;Linux下會打開一個文本框,可以更改,該完后再make生成目標文件——emacs不支持make edit_cache;
>>?局限:這種方法不能直接設置生成的makefile,而是必須使用命令在make前設置參數;對于debug、release版本,相當于需要兩個目錄,分別先cmake一次,然后分別make edit_cache一次;
>>?期望的效果:在執行cmake時直接通過參數指定一個開關項,生成相應的makefile——可以這樣做,例如cmake –DDEBUGVERSION=ON
8)??怎樣添加編譯宏定義
>>?使用add_definitions命令,見命令部分說明
9)??怎樣添加編譯依賴項
用于確保編譯目標項目前依賴項必須先構建好
>>add_dependencies
10)????????怎樣指定目標文件目錄
>>?建立一個新的目錄,在該目錄中執行cmake生成Makefile文件,這樣編譯結果會保存在該目錄——類似
>> SET_TARGET_PROPERTIES(ss7gw PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${BIN_DIR}")
11)????????很多文件夾,難道需要把每個文件夾編譯成一個庫文件?
>>?可以不在子目錄中使用CMakeList.txt,直接在上層目錄中指定子目錄
12)????????怎樣設定依賴的cmake版本
>>cmake_minimum_required(VERSION 2.6)
13)????????相對路徑怎么指定
>> ${projectname_SOURCE_DIR}表示根源文件目錄,${ projectname _BINARY_DIR}表示根二進制文件目錄?
14)????????怎樣設置編譯中間文件的目錄
>> TBD
15)????????怎樣在IF語句中使用字串或數字比較
>>數字比較LESS、GREATER、EQUAL,字串比STRLESS、STRGREATER、STREQUAL,
>> Eg:
set(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON)
set(AAA abc)
IF(AAA STREQUAL abc)
message(STATUS "true")???#應該打印true
ENDIF()
16)????????更改h文件時是否只編譯必須的cpp文件
>>?是
17)????????機器上安裝了VC7和VC8,CMAKE會自動搜索編譯器,但是怎樣指定某個版本?
>> TBD
18)????????怎樣根據OS指定編譯選項
>> IF( APPLE ); IF( UNIX ); IF( WIN32 )
19)????????能否自動執行某些編譯前、后命令?
>> 可以,TBD
20)????????怎樣打印make的輸出
make VERBOSE=1