CMake使用總結

前言

做第一個linux項目時,Makefile是一行行敲入的,第二個項目後,開始使用cmake。至於爲何選擇cmake,倒不是覺得它有什麼好,僅僅是因爲當時項目組中的一個linux前輩向我們推薦了這個。經過一番研究之後,並在項目中使用,現將使用經驗總結一下,供大家參考。

入門篇

學習一項新知識的時候,最好是從sample開始。cmake官方網站就給出了一個簡單的例子。在開始之前,還是先安裝cmake程序,在ubuntu下非常簡單,輸入以下命令即可:

$sudo apt-get install cmake

 

下面來看看sample中的內容。sample包含一個主目錄,下面兩個子目錄: Demo和Hello。其中Hello包含程序庫代碼,Demo中爲可執行程序,需要連接Hello中的程序庫。總共有三個CMakeLists.txt文件,每個目錄下一個。主目錄下的CMakeLists.txt文件內容爲:

    # The name of our project is "HELLO".  CMakeLists files in this project can
    # refer to the root source directory of the project as ${HELLO_SOURCE_DIR} and
    # to the root binary directory of the project as ${HELLO_BINARY_DIR}.
    project (HELLO)
    # Recurse into the "Hello" and "Demo" subdirectories.  This does not actually
    # cause another cmake executable to run.  The same process will walk through
    # the project's entire directory structure.
    add_subdirectory (Hello)
    add_subdirectory (Demo)
            

Hello目錄下的CMakeLists.txt文件爲:

    # Create a library called "Hello" which includes the source file "hello.cxx".
    # The extension is already found.  Any number of sources could be listed here.
    add_library (Hello hello.cxx)
            

最後,Demo包含如下CMakeLists.txt文件:

    # Make sure the compiler can find include files from our Hello library.
    include_directories (${HELLO_SOURCE_DIR}/Hello)
    # Make sure the linker can find the Hello library once it is built.
    link_directories (${HELLO_BINARY_DIR}/Hello)
    # Add executable called "helloDemo" that is built from the source files
    # "demo.cxx" and "demo_b.cxx".  The extensions are automatically found.
    add_executable (helloDemo demo.cxx demo_b.cxx)
    # Link the executable to the Hello library.
    target_link_libraries (helloDemo Hello)
            

CMake在主目錄執行時,會處理該目錄下CMakeLists.txt文件,然後進入到子目錄,處理子目錄下的CMakeLists.txt.

從字面上看,我們差不多可以理解這三個文件的涵義。第一個CMakeLists.txt文件指定包含Hello和Demo兩個子目錄。第二個文件則指定生成Hello庫文件,第三個文件則是生成一個可執行文件helloDemo,另外兩個附加語句則用來指明頭文件路徑以及所要鏈接的庫。

乍一看,爲了編譯一個程序,需要寫三個CMakeLists.txt文件,相對於原來只需寫一個Makefile文件,不是更復雜了嗎?事實上不盡然,雖然要寫三個CMakeLists文件,但每個文件都非常簡單,總共算起來,並不比一個Makefile文件多多少。更重要的是,這其中隱含着linux哲學:分而治之。每個模塊自行編寫配置文件,只負責自己份內的事務,所以可擴展性好。在進行大型項目開發,就可以體現出優勢了。

CMakeLists.txt相當於定義了一套生成Makefile文件的規則,下面就可以生成Makefile文件了,命令如下:

$cmake .

注:.表示當前目錄,如果CMakeLists.txt不在當前目錄,請在cmake後面指定。在主目錄下和Demo、Hello子目錄下均會生成一個Makefile文件,有了這個文件,我們就可以敲入make編譯目標程序了。

總結起來,使用CMake編譯應用程序的流程爲:

CMake語法介紹

CMake語法非常簡單,包含註釋、命令和空格。以#開頭的行爲註釋行,命令則由命令名、括號及以空格進行分隔的參數組成。命令可以是諸如add_library這樣的內置命令,也可以是子定義的宏或者函數。CMake的輸入是主目錄下的CMakeLists.txt文件,該文件可以使用include或者add_directory命令添加其它的輸入文件。

命令

命令的形式如下:

command (args ...)

其中<I>command</I>爲命令名,<B>args</B>爲空格分隔的參數列表,如果參數中包含空格,使用雙引號引起來。命令不區分大小寫。

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)
        

 

小結

本文通過一個簡單的工程示例,我們瞭解到要讓CMake工作起來,需要編寫CMakeLists.txt文件,CMake據此生成Makefile。然後簡單的過了以下CMake的語法,有了這個基礎,我們就可以自行編寫CMakeLists.txt文件了。當然,對於一個大型的工程,僅僅掌握這些還不夠,需要進一步掌握一些CMakeLists.txt文件的編寫技巧,將在後續的文件中繼續這個話題。

參考書目

Jan EngelsCMake Tutorial.

CMake官方網站:www.cmake.org.

在前面一篇文章中,我們從一個sample入手,瞭解了CMake的基本用法和語法。但這個例子與實際開發還有一段距離,主要存在以下幾點問題:

  • 生成的二進制程序和源程序混在一起
  • 使用gcc進程程序編譯,而不是使用交叉編譯工具
  • 爲指定編譯選項,通常會生成debug版本供調試用,release版本用於發佈

在本章,我們將sample程序逐步改造,解決上述問題。

項目文件組織

一個項目,通常包含若干子模塊。比如上一篇的sample,我們可以認爲它包含兩個子模塊,Hello爲程序庫,Demo爲主程序。很少有項目會把目標二進制文件和源程序放在一起的,通常會建立一個bin目錄,存放生成的二進制文件,發佈程序則放在release。根據我在項目開發中的習慣,將目錄結構修改如下:

            CMakeSample
               |--- release
               |--- doc
               |--- lib
               |--- source
                      |--- include
                      |--- bin
                      |--- Hello
                      |--- Demo

其中,release存放程序發佈相關文件,包括程序文件、腳本、參數等。doc包含項目開發中的相關文檔,如設計說明以及通過doxgen等工具從代碼中生成的文檔。lib存放項目中使用的第三方庫,項目中自己編寫的庫不放在此目錄,應該作爲項目的一個模塊放在source目錄下。include包含整個項目中使用的公共頭文件,如果子模塊中的頭文件僅在該子模塊類使用,不需放到include目錄。bin目錄存放編譯後的調試版本代碼。其它的子目錄則爲各模塊的代碼及頭文件。

按照以上目錄結構,將Hello下的hello.h移到include目錄,因爲這個頭文件被Demo模塊包含。這個sample中未使用第三方庫,所以暫時爲空。

常用的幾個內置變量

從上文中我們知道,通過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

    指定庫文件放置的路徑

常用的命令

除了內置變量,我們還可以通過命令來修改編譯選項,現將一些常用的命令列出來:

  • include_directories

    指定頭文件的搜索路徑,相當於指定gcc編譯器的-I參數

  • link_directories

    動態鏈接庫或靜態鏈接庫的搜索路徑,相當於指>定gcc的-L參數

  • add_subdirectory

    包含子目錄,當工程包含多個子目錄時,此命令有用

  • add_definitions

    添加編譯參數,比如add_definitions(-DDEBUG)將在gcc命令行添加DEBUG宏定義

  • add_executable

    編譯可執行程序

  • target_link_libraries

    指定鏈接庫,相同於指定-l參數

小結

本文經過修改目錄結構,指定編譯工具鏈,生成動態鏈接庫等動作,將前文的sample修改成一個比較接近實際工程的嵌入式環境。當然這個sample仍然只是一個自娛自樂的小玩意,但對於說明CMake用法已經足夠了,有了這些基本的CMake知識,在項目中使用CMake就成爲可能了。當然不同的環境會遇到一些新問題,藉助於互聯網,沒有什麼解決不了的問題。趕快動手吧,拋棄手工編寫Makefile的痛苦,加入CMake使用者行列吧。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章