iT邦幫忙

2023 iThome 鐵人賽

DAY 10
0

本日內容

  • Scope Keywords
  • Compiler Flags
  • Linker Flags
  • Archiver Flags
  • Deduplicating Options
  • 預告

連結: Day 10 - Colab

今天來講一下有哪些 target shorthands 能用來設定 compiler flags 和 linker flags 能用
由於本系列不會介紹 C++, compiler 或是 linker 的原理, 如果有興趣可以參考 John Levine 的 Linkers and Loaders

在本系列中, 只需要知道 compiler 和 linker 在 build process 中扮演什麼角色即可

CMake 已經將 compiler 和 linker flags 抽象出來, 讓我們在 build target 的時候, 這些 flags 不會 直接直接傳給 CMake 呼叫的 compiler 或是 linker, 而是會先經過一些處理, 比如去重複 (De-duplicating)
所以在執行 compiler 或 linker 的指令可能和預期的不一樣, 後面會說明

在開始介紹 flags 之前, 先來講一下 target 中很重要的部分: Scope Keywords

Scope Keywords

Day 8 有介紹過可以透過設定 Target Properties 來控制 CMake 如何處理這個 target (ex. executable, library, etc.)
CMake 也可以將某些 target properties 在處理後傳給 compiler 和 linker, 而這些 properteis 是可以被繼承的!
CMake 提供以下幾種 scope keywords 來控制繼承的範圍

  • PRIVATE
    • 只適用 target 本身
  • PUBLIC
    • target 本身和 link 他的 targets (consuming targets) 也會繼承該 properties
  • INTERFACE
    • target 本身 不會套用 設定的 properties, 而是 link 他的 targets 才會繼承

所以在設定 compiler 或是 linker flags 前, 要先想想該 flags 是否需要讓 consuming targets 套用相同設定

好啦, 有了這個概念後, 我們來看看 CMake 提供了哪些 compiler 和 linker 相關的 target properties 吧

Compiler Flags

CMake 將 compiler flags 分為以下幾種類別

  • Header search paths
  • Compile definitions
  • Compile options

這裡會以 gcc 為例, 介紹如何設定 target 的 compiler flags
Note: gcc flags 可以參考 GNU Compiler Collections
比如我們很常用到的 -O-g

Header Search Paths

target_include_directories(targetName [AFTER|BEFORE] [SYSTEM]
  <PRIVATE|PUBLIC|INTERFACE> dir1 [dir2 ...]
  [<PRIVATE|PUBLIC|INTERFACE> dir3 [dir4 ...]]
  ...
)
  • 指定 Header search paths
  • AFTER, BEFORE
    • 加入 CMake 搜尋 include directories 的路徑順序在後面還是前面
  • SYSTEM
    • 會將路徑加入 system header search paths, 該路徑的搜尋順序會在一般的 search paths 後
  • PRIVATE, PUBLIC, INTERFACE
    • 上面提過的 scope keywords

Compile Definitions

target_compile_definitions(targetName
  <PRIVATE|PUBLIC|INTERFACE> item1 [item2 ...]
  [<PRIVATE|PUBLIC|INTERFACE> item3 [item4 ...]]
  ...
)
  • 設定 compiler macros
  • 可以設為 VAR 或是 VAR=VALUE

Compile Options

target_compile_options(targetName [BEFORE]
  <PRIVATE|PUBLIC|INTERFACE> item1 [item2 ...]
  [<PRIVATE|PUBLIC|INTERFACE> item3 [item4 ...]]
  ...
)
  • 其他的 compiler flags
  • 要注意重複的 options 會被去重複, 下面會說明

Linker Flags

和 Compiler flags 類似, CMake 將 linker flags 分為以下幾種

  • Link libraries
  • Link options

Link Libraries

target_link_libraries(targetName
  <PRIVATE|PUBLIC|INTERFACE> item1 [item2 ...]
  [<PRIVATE|PUBLIC|INTERFACE> item3 [item4 ...]]
  ...
)
  • Target 會用到的 libaries
  • item#
    • 可以是以下幾種
      • (建議用這種) CMake library target name
        • 在產生 linker commands 的時候, library name 會轉成該 library 的 built path
      • absolute path
      • library name

Link Options

target_link_options(targetName [BEFORE]
  <PRIVATE|PUBLIC|INTERFACE> item1 [item2 ...]
  [<PRIVATE|PUBLIC|INTERFACE> item3 [item4 ...]]
  ...
)
  • 傳給 linker 的 flags
    • 僅有 general linker flags, 不需要指定 link 其他 library, 上面的 target_link_libraries 已經做掉了
  • 僅適用 executables, shared libraries, module libraries
    • static library 會忽略此 property, 要用 STATIC_LIBRARY_OPTIONS property
    • 遺憾的是, 沒有對應的 target property shorthand 能用, 只能用 set_property(STATIC_LIBRARY_OPTIONS) 能用
  • 由於 CMake 只會將設定傳給 compiler, linker 則是由 compiler 啟動, 所以需要由 compiler 將 flags 傳給 linker, 但是每個 compiler 設定 linker 的用法不同, 比如
    • gcc: Wl,...
    • clang: -Xlinker ...
    • 所以可以用 LINKER: prefix 讓 CMake 能把 linker flags 根據 compiler 轉成對應的 option (希望不會太繞口:p)
    target_link_options(MyApp PRIVATE
      "LINKER:-stats"
    )
    

Archiver Flags

和 compiler flags 和 linker flags 不同, 他沒有 target property shorthand 能用😭😭😭

所以只能用 set_property(), 比如

set_property(TARGET TestLib_Greeting PROPERTY 
  STATIC_LIBRARY_OPTIONS "--thin"
)

會得到

[ 50%] Linking CXX static library libTestLib_Greeting.a
cd /content/cmake-example/cmake-example/cmake-example/build/lib && /usr/local/lib/python3.10/dist-packages/cmake/data/bin/cmake -P CMakeFiles/TestLib_Greeting.dir/cmake_clean_target.cmake
cd /content/cmake-example/cmake-example/cmake-example/build/lib && /usr/local/lib/python3.10/dist-packages/cmake/data/bin/cmake -E cmake_link_script CMakeFiles/TestLib_Greeting.dir/link.txt --verbose=1
/usr/bin/ar qc libTestLib_Greeting.a --thin CMakeFiles/TestLib_Greeting.dir/greeting.cpp.o
/usr/bin/ranlib libTestLib_Greeting.a
gmake[2]: Leaving directory '/content/cmake-example/cmake-example/cmake-example/build'

Deduplicating Options

CMake 會在把 options 傳給 compiler, linker 之前, 先把重複的 options 幹掉, 舉個例子

這在 gcc 沒什麼問題, 但如果今天是用 clang 的話就會壞掉, 因為就像上面提到的, clang 會需要把每個 options 都加上 Xlinker

target_link_options(MyTarget PRIVATE
  "LINKER:-stats"
  "LINKER:-z,defs"
)

而為了不同 linker 有不同 options 的問題而加的 LINKER: prefix, 不會受到 de-duplicate 的影響, 但後面的 defs 還是會被刪掉
變成 -Xlinker,-stats -Xlinker,-z,defs

compiler 也會有一樣的狀況

# This won't work as expected either
target_compile_options(MyTarget PRIVATE
  -Xassembler --keep-locals
  -Xassembler --warn
)

會變成 -Xassembler --keep-locals --warn

[ 75%] Building CXX object CMakeFiles/Main.dir/main.cpp.o
/usr/bin/c++  -I/content/cmake-example/include -std=c++11 -Xassembler --keep --no_esc -MD -MT CMakeFiles/Main.dir/main.cpp.o -MF CMakeFiles/Main.dir/main.cpp.o.d -o CMakeFiles/Main.dir/main.cpp.o -c /content/cmake-example/main.cpp 👈

如何保留重複的 options?

可以在 option 加上 prefix SHELL:, 加上之後 CMake 會把 options 合併成空白分隔的 single quoted string, 就不會被 split

來修改上面的例子看看

target_link_options(SomeTarget PRIVATE
  "LINKER:SHELL:-stats"
  "LINKER:SHELL:-z,defs"
)

linker options 會變成 -Xlinker -stats -Xlinker -z -Xlinker defs (clang compiler 的話)

target_compile_options(SomeTarget PRIVATE
  "SHELL:-Xassembler --keep-locals"
  "SHELL:-Xassembler --warn"
)

compiler options 會變成 -Xassembler --keep-locals -Xassembler --warn

就正常啦!

target_...() 系列的 property command 都可以用 SHELL: 來避免被 de-duplicate
要注意的是, STATIC_LIBRARY_OPTIONS 不支援 LINKER:

預告

下一篇會回頭介紹 Day 8 有大概介紹過幾種的 Library 類型, 但更深入討論, 並附上範例 code~


上一篇
[Day 9] 第一個 CMake 專案!
下一篇
[Day 11] Library 類型
系列文
30 天 CMake 跨平台之旅30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言