code style

2016年10月24日 星期一

CMake筆記(6) - 編譯選項

Makefile的建置資訊:
在預設的情況下,CMake 生成的 makefile 只會顯示編譯的進度,並不會把各步驟實際調用的命令、參數一一列出。如果需要這類資訊的話,就可設定makefile中的verbose,有3種方法:

1.在CMakeList.txt 中加入, set(CMAKE_VERBOSE_MAKEFILE ON)
2.在cmake執行時加入,$ cmake -DCMAKE_VERBOSE_MAKEFILE=ON ...options...
3.在make執行時加入,make VERBOSE=1

編譯組態:
透過指定 CMAKE_BUILD_TYPE 變數即可改變編譯組態,預設的 CMAKE_BUILD_TYPE 是 None,即不加上任何額外編譯選項。內建的 CMAKE_BUILD_TYPE 支援以下幾種組態:
  • None : 編譯器預設值
  • Debug : 產生除錯資訊
  • Release : 進行執行速度最佳化
  • RelWithDebInfo : 進行執行速度最佳化,但仍然會啟用 debug flag
  • MinSizeRel : 進行程式碼最小化

也可以從cmake執行時才加入,例如:
cmake -DCMAKE_BUILD_TYPE=Release ./src

編譯選項:
變數 CMAKE_C_FLAGS 存放的內容會被傳給 C 編譯器,作用在所有的編譯組態上。如果希望只針對特定一種組態有效,可以設定 CMAKE_C_FLAGS_<編譯組態>,例如 CMAKE_C_FLAGS_RELEASE、CMAKE_C_FLAGS_DEBUG。如果是C++編譯器,則是CMAKE_CXX_FLAGS 和 CMAKE_CXX_FLAGS_<編譯組態>。
舉個具體的例子,在 Visual C++ 底下編譯含有 sprintf 的原始碼時,常常收到 C4996 警告,除了可以在原始碼中使用 #pragma 關閉,也可以透過編譯選項搞定。在 CMakeLists.txt 設定編譯選項的方式如下:

if(MSVC)
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /wd4996")
endif()

連結選項:
CMAKE_EXE_LINKER_FLAGS 變數決定了連結執行檔時傳給編譯器的選項,同樣也有各種不同編譯組態的變形。下例為加入靜態連結

if(MINGW)
    set(CMAKE_EXE_LINKER_FLAGS "-static")
    set(CMAKE_EXE_LINKER_FLAGS_RELEASE "-s")
endif()

上例的各種組態都採用 -static 來連結可執行檔,並且只有在 Release 情況下另外加入 -s 連結選項,亦即 Release 的連結選項為 -static -s

Preprocessor:
add_definitions("-DFOO -DBAR")

編譯器搜尋路徑:
以下兩個指令分別用來加入編譯器尋找標頭檔、程式庫的路徑,在 gcc 相當於 -I 和 -L 選項。
include_directories([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...) 
link_directories(dir1 dir2 ...)

使用屬性控制編譯選項:
以上介紹的變數和指令都是屬於全域範圍的設定,如果希望選項僅對一個 target 有效,就必須從 target 的屬性(properties)下手。

在 CMake 裡一個 target 最終的輸出檔名、編譯連結選項等等都是由屬性控制,若沒有手動設定這些屬性,則 CMake 會由相關的全域變數來決定屬性值。我們可以利用 set_target_properties() 指令單獨指定一個 target 的屬性,如此一來就可以蓋過全域變數的設定。

set_target_properties() 指令格式如下:
set_target_properties(target1 target2 ...
    PROPERTIES
    屬性名稱1  值
    屬性名稱2  值
    ...
    )

例如下例,這些屬性會蓋過原先全域變數的設定,而且僅作用在 app 這個 target 上:
set_target_properties(app
    PROPERTIES
    LINK_FLAGS          -static
    LINK_FLAGS_RELEASE  -s
    )

注意事項:
1. 小心覆寫變數
像 CMAKE_C_FLAGS_RELEASE、CMAKE_C_FLAGS_DEBUG 這些變數,CMake 會自動依照 Generator 指定的編譯器初始化。例如在 gcc 下 CMAKE_C_FLAGS_RELEASE 預設的值為 -O3 -DNDEBUG,而 CMAKE_C_FLAGS_DEBUG 預設值為 -g。

小心下面這種寫法:
set(CMAKE_C_FLAGS_RELEASE "-fno-inline")

原意可能是希望在做最佳化時忽略 inline 關鍵字,但是這樣一來其實會把原本的 -O3 -DNDEBUG 給覆寫掉,所以程式並不會做任何最佳化。

比較好的寫法應該是:
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -fno-inline")

2.注意字串和串列的不同

set(CMAKE_EXE_LINKER_FLAGS "-static")
# ... (1)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s")
# ... (2)   
set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} "-s")   

(1) 會傳遞 -static -s 給編譯器 (2) 會傳遞 -static;-s 給編譯器

實際應用的範例:
設定g++為編譯器,並build release的版本:

cmake_minimum_required (VERSION 2.6)

project (Test)

set(CMAKE_VERBOSE_MAKEFILE on)
set(CMAKE_CXX_COMPILER "g++")
set(CMAKE_CXX_FLAGS "-Wall")
set(CMAKE_CXX_FLAGS_DEBUG "-g3")
set(CMAKE_CXX_FLAGS_RELEASE "-O2")

if(${CMAKE_BUILD_TYPE} MATCHES "debug")
    message("debug version")
else()
    message("release version")
endif()

include_directories(lib/include)
set(TEST_SOURCES src/main.cxx)
set(LIB_SOURCES lib/lib.cxx)
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/build)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
 

add_library(MyLib ${LIB_SOURCES})
add_executable(TestRun ${TEST_SOURCES})
target_link_libraries(TestRun MyLib)

沒有留言:

張貼留言