CMake常用指令

CMake文件符號的意思

Square Brackets [ ]
Angle Brackets < >

https://stackoverflow.com/a/23242584/22299707

建置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
cmake -S . -B build
cmake --build build -j 8
``````



# Linux下用pkg_check_modules尋找套件
```cmake
cmake_minimum_required(VERSION 3.15)
project(fy-version)

set(CMAKE_C_STANDARD 11)

find_package (PkgConfig REQUIRED)
pkg_check_modules(FYAML IMPORTED_TARGET libfyaml)
if (FYAML_FOUND)
include_directories(${FYAML_INCLUDE_DIRS})
link_directories(${FYAML_LIBRARY_DIRS})
list(APPEND LINK_LIB_LIST ${FYAML_LIBRARIES})

message(STATUS "found library:${FYAML_LIBRARIES}")
endif ()

# 建立執行檔
add_executable(fy-version yaml_reader.c)

target_link_libraries(${PROJECT_NAME} PkgConfig::FYAML)

https://cmake.org/cmake/help/latest/module/FindPkgConfig.html

https://stackoverflow.com/questions/42634710/how-to-use-pkg-config-in-cmake-juci

https://blog.csdn.net/zhizhengguan/article/details/111826697

Debug模式編譯

1
set(CMAKE_BUILD_TYPE Debug)

引用CUDA

Declare CUDA as a LANGUAGE in your project

1
project(GTC LANGUAGES CUDA CXX)
  • 連接library
    利用FindCUDAToolkit(cmake版本>3.17)
    1
    2
    3
    4
    5
    6
    find_package(CUDAToolkit)
    add_executable(
    binary_linking_to_cudart
    my_cpp_file_using_cudart.cpp
    )
    target_link_libraries(binary_linking_to_cudart PRIVATE CUDA::cudart)
    利用FindCUDAToolkit

https://cliutils.gitlab.io/modern-cmake/chapters/packages/CUDA.html

https://developer.download.nvidia.com/video/gputechconf/gtc/2019/presentation/s9444-build-systems-exploring-modern-cmake-cuda-v2.pdf

cmake 加入編譯好的.o檔

1
2
3
4
5
6
7
8
9
10
11
12
SET(OBJS
${CMAKE_CURRENT_SOURCE_DIR}/libs/obj.o
)

ADD_EXECUTABLE(myProgram ${OBJS} <other-sources>)

SET_SOURCE_FILES_PROPERTIES(
${OBJS}
PROPERTIES
EXTERNAL_OBJECT true
GENERATED true
)

https://stackoverflow.com/a/38610428

編譯不是cmake的專案或函式庫

1
2
3
4
5
6
7
8
9
10
ExternalProject_Add(Qt
DOWNLOAD_DIR ${CMAKE_CURRENT_BINARY_DIR}
URL ${qt_file}
UPDATE_COMMAND ""
SOURCE_DIR ${qt_source}
BUILD_IN_SOURCE 1
CONFIGURE_COMMAND ${qt_configure}
BUILD_COMMAND ${qt_build}
INSTALL_COMMAND "${qt_install}"
)

https://stackoverflow.com/a/3493578/22299707

編譯專案外的makefile專案資料夾

以下指令可以在外部資料夾下make指令

1
2
3
4
5
6
7
8
9
10
include(ExternalProject)
ExternalProject_Add(deepstreamapp
SOURCE_DIR ${DEEPSTREAMAPP}/sample_apps/deepstream-app # This is not likely to be CMAKE_CURRENT_LIST_DIR
CONFIGURE_COMMAND ""
INSTALL_COMMAND ""
DOWNLOAD_COMMAND ""
BUILD_COMMAND make
BUILD_IN_SOURCE true
)

接下來可以用以下指令找出所有編譯好的.o檔並且加到專案內

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
file(GLOB_RECURSE  DEEPSTREAMAPPCONFIGPARSER "/opt/nvidia/deepstream/deepstream/sources/apps/apps-common/src/deepstream-yaml/*.o")

SET(OBJS
${DEEPSTREAMAPPCONFIGPARSER}
)

SET_SOURCE_FILES_PROPERTIES(
${OBJS}
PROPERTIES
EXTERNAL_OBJECT true
GENERATED true
)

add_executable(${PROJECT_NAME}
${OBJS}
main.cpp
)

參考:https://discourse.cmake.org/t/external-project-using-makefile/2692/5

增加函式庫

https://cmake.org/cmake/help/latest/guide/tutorial/Adding%20a%20Library.html#step-2-adding-a-library
target_include_directories()target_link_libraries()

vscode-C開發環境

安裝vscode套件

C++ 、CMake 、CMake Tools

製作launch.json

而launch.json可以呼叫task.json裡面的工作,只需要在preLaunchTask中指定,範例如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${command:cmake.launchTargetPath}",
"args": [
"-c",
"licenceplate_nvmultiurisrcbin.txt"
],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/iisi-ds63",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set Disassembly Flavor to Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
}
]
}

cmake tool 設定文件

https://code.visualstudio.com/docs/cpp/CMake-linux

https://github.com/microsoft/vscode-cmake-tools/tree/024d14df75d397a682da4a864eac420824e4ddba/docs

ld找不到動態函式庫的除錯方法

注意是否有從C++ 呼叫C語言的函式,如果有必須要使用extern C

https://hackmd.io/@rhythm/HyOxzDkmD

https://www.airs.com/blog/archives/38

https://www.google.com/search?q=Linkers+part+site%3Ahttps%3A%2F%2Fwww.airs.com%2F&rlz=1C1GCEU_zh-TWTW902TW902&oq=Linkers+part+site%3Ahttps%3A%2F%2Fwww.airs.com%2F&gs_lcrp=EgZjaHJvbWUyBggAEEUYOdIBCDM1ODlqMGo3qAIAsAIA&sourceid=chrome&ie=UTF-8#ip=1

注意function是否為static function

c語言中的static function在function被定義的檔案以外的scope是看不到的

undefined reference to

表示必要的函式庫或是.o檔沒有被連接

  1. nm指令確認函式確實存在.so檔裡面例如:
    nm -D libnvds_utils.so --defined-only的輸出如下,這個指令只會列出有對外開放的函式
    1
    2
    3
    4
    5
    6
    0000000000001470 T nvds_dependencies_version_print
    00000000000018d0 T nvds_mask_utils_resize_to_binary_argb32
    00000000000013f0 T nvds_version
    0000000000001410 T nvds_version_print
    0000000000001310 T _Z18libnvds_utils_initv
    0000000000001300 T _Z20libnvds_utils_deinitv
    https://stackoverflow.com/a/4514781
  • nm不只可以用在函式庫檔,.o檔也可以用
    nm deepstream_source_bin.o --defined-only
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    0000000000000000 b install_mux_eosmonitor_probe
    0000000000000010 b last_reset_time_global.31491
    0000000000000008 b mutex.31460
    00000000000030a8 t nvstreammux_eosmonitor_probe_func
    0000000000006ab0 T reset_encodebin
    00000000000065e8 T reset_source_pipeline
    000000000000133c t restart_stream_buf_prob
    0000000000002ffc t rtspsrc_monitor_probe_func
    0000000000001270 t seek_decode
    0000000000000020 t set_camera_csi_params
    00000000000000bc t set_camera_v4l2_params
    0000000000006a04 T set_source_to_playing
    0000000000002178 t smart_record_callback
    0000000000002270 t smart_record_event_generator
    0000000000002e74 t watch_source_async_state_change
    00000000000026d0 t watch_source_status
    ...

/usr/bin/ld: cannot find …

編譯過程中發現ld找不到某個函式庫例如/usr/bin/ld: cannot find -ljpeg,可以利用指令ld <函式庫> --verbose可以查看ld找了那些路徑,例如目前ld找不到-ljpeg可以利用ld -ljpeg --verbose,輸出如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ld: mode aarch64linux
attempt to open /usr/local/lib/aarch64-linux-gnu/libjpeg.so failed
attempt to open /usr/local/lib/aarch64-linux-gnu/libjpeg.a failed
attempt to open /lib/aarch64-linux-gnu/libjpeg.so failed
attempt to open /lib/aarch64-linux-gnu/libjpeg.a failed
attempt to open /usr/lib/aarch64-linux-gnu/libjpeg.so failed
attempt to open /usr/lib/aarch64-linux-gnu/libjpeg.a failed
attempt to open /usr/local/lib/libjpeg.so failed
attempt to open /usr/local/lib/libjpeg.a failed
attempt to open /lib/libjpeg.so failed
attempt to open /lib/libjpeg.a failed
attempt to open /usr/lib/libjpeg.so failed
attempt to open /usr/lib/libjpeg.a failed
attempt to open /usr/aarch64-linux-gnu/lib/libjpeg.so failed
attempt to open /usr/aarch64-linux-gnu/lib/libjpeg.a failed
ld: cannot find -ljpeg

注意makefile的擺放順序

連接器指令放最後面
例如

1
gcc -I/usr/local/include -o yaml_reader yaml_reader.c  -L/usr/local/lib -lfyaml

https://stackoverflow.com/questions/22426574/gcc-undefined-reference-to

https://stackoverflow.com/questions/16710047/usr-bin-ld-cannot-find-lnameofthelibrary

注意cmake是否有把程式加入編譯

add_executable(…)內加入的cpp檔會被編譯,確認是否有將程式加入

Jetson設定

  1. 遠端桌面
    Nomachine

  2. 硬體狀態jtop

    1
    2
    3
    sudo apt update
    sudo apt install python3-pip
    sudo pip3 install -U jetson-stats
  3. 關閉GUI節省GPU資源

  • 暫時關閉以及打開方法
    1
    sudo init 3  #暫時關閉
1
sudo init 5  #打開
  • 重啟後不再打開GUI
    1
    sudo systemctl set-default multi-user.target #關閉
1
sudo systemctl set-default multi-user.target #打開

參考:
https://forum.nomachine.com/topic/problems-connecting-with-nomachine-in-pc-client-without-monitor

Ubuntu安裝CUDA cuDNN TeosorRT

  1. 安裝CUDA
    將CUDA的repo加入apt
    1
    2
    3
    4
    5
    6
    wget https://developer.download.nvidia.com/compute/cuda/11.7.1/local_installers/cuda-repo-debian11-11-7-local_11.7.1-515.65.01-1_amd64.deb
    sudo dpkg -i cuda-repo-debian11-11-7-local_11.7.1-515.65.01-1_amd64.deb
    sudo rm /etc/apt/sources.list.d/*cuda*
    sudo apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/3bf863cc.pub
    sudo add-apt-repository "deb https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/ /"
    sudo apt-get update

接下來注意不要直接sudo apt-get -y install cuda,因為可能會直接安裝最新版的CUDA,而不是你指定的版本

首先確認有哪些版本可以下載

1
apt-cache policy vlc

輸出如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
cuda:
Installed: (none)
Candidate: 12.1.1-1
Version table:
12.1.1-1 500
500 https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64 Packages
12.1.0-1 500
500 https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64 Packages
12.0.1-1 500
500 https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64 Packages
12.0.0-1 500
500 https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64 Packages
11.8.0-1 500
500 https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64 Packages
11.7.1-1 500
500 https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64 Packages
11.7.0-1 500
500 https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64 Packages
......

在這裡我們想安裝CUDA 11.7.1,指令如下

1
sudo apt-get  install cuda=11.7.1-1

再次確認即將安裝的是不是CUDA11.7,是的話再按y

  1. 安裝cuDNN和TensorRT
    1
    2
    3
    4
    5
    sudo apt-get install libnvinfer8=8.4.1-1+cuda11.6 libnvinfer-plugin8=8.4.1-1+cuda11.6 libnvparsers8=8.4.1-1+cuda11.6 \
    libnvonnxparsers8=8.4.1-1+cuda11.6 libnvinfer-bin=8.4.1-1+cuda11.6 libnvinfer-dev=8.4.1-1+cuda11.6 \
    libnvinfer-plugin-dev=8.4.1-1+cuda11.6 libnvparsers-dev=8.4.1-1+cuda11.6 libnvonnxparsers-dev=8.4.1-1+cuda11.6 \
    libnvinfer-samples=8.4.1-1+cuda11.6 libcudnn8=8.4.1.50-1+cuda11.6 libcudnn8-dev=8.4.1.50-1+cuda11.6 \
    python3-libnvinfer=8.4.1-1+cuda11.6 python3-libnvinfer-dev=8.4.1-1+cuda11.6

參考:
https://docs.nvidia.com/metropolis/deepstream/6.1.1/dev-guide/text/DS_Quickstart.html#

https://askubuntu.com/questions/340530/how-can-i-check-the-available-version-of-a-package-in-the-repositories

CMake教學系列三建立CMake專案

下載範例

下面指令將會下載本文所需的範例

1
2
git clone https://gitlab.com/CLIUtils/modern-cmake.git
cd modern-cmake/examples/simple-project/

CMakeLists.txt解說

  • 在第29行add_library(MyLibExample simple_lib.cpp simple_lib.hpp)增加了一個名為MyLibExampletarget並且將會編譯一個MyLibExamplelibrary
  • 第34行增加了一個add_executable(MyExample simple_example.cpp)增加了一個名為MyExampletarget並且將會編譯一個MyExample的執行檔
  • 第38行target_link_libraries(MyExample PRIVATE MyLibExample)連接了MyLibExampleMyExample這兩個target,注意你必須先製作target才能夠連接他們

建置專案、編譯

接下來你可以用以下指令建置專案並且編譯。CMake會產生一個build資料夾,在裡面你應該會看到libMyLibExample.alibrary和MyExample執行檔。你可以查看CMakeCache.txt看有哪些cache variable,接下來用./build/MyExample執行MyExample執行檔

1
2
cmake -S . -B build
cmake --build build -j 8

常見錯誤

路徑中有空白

1
set(VAR a b v)

這個寫法會讓VAR變成一個list,並且擁有a b c三個元素,因此如果你的路徑像這樣,你的路徑就會被拆成兩段。

1
2
set(MY_DIR "/path/with spaces/")
target_include_directories(target PRIVATE ${MY_DIR})

解決方法就是在${MY_DIR}外面再包一個引號

1
2
set(MY_DIR "/path/with spaces/")
target_include_directories(target PRIVATE "${MY_DIR}")

除錯

印出變數

雖然message指令也可以印出變數,不過你有更好的選擇,cmake_print_propertiescmake_print_variables,記得要先include(CMakePrintHelpers)。如此一來你可以更加容易的印出target的屬性

1
2
3
4
5
6
include(CMakePrintHelpers)
cmake_print_variables(MY_VARIABLE)
cmake_print_properties(
TARGETS my_target
PROPERTIES POSITION_INDEPENDENT_CODE
)

–trace-source 和 –trace-expand

--trace-source讓你可以指定只要看你想看的CMakeLists.txt--trace-expand變數全部展開,例如原本是

1
add_library(simple_lib ${SOURCES} ${HEADERS} )

加了--trace-expand變成

1
add_library(simple_lib simple_lib.c simple_lib.h )

下載這個範例,並且試看看cmake -S . -B build --trace-source=CMakeLists.txtcmake -S . -B build --trace-source=CMakeLists.txt --trace-expand有什麼不一樣

1
2
git clone git@github.com:hsf-training/hsf-training-cmake-webpage.git
cd hsf-training-cmake-webpage/code/04-debug

除錯CMake的find_…模組

沿用上面的範例,在這個範例你可以看到第16行有一個find_library(MATH_LIBRARY m)

1
2
git clone git@github.com:hsf-training/hsf-training-cmake-webpage.git
cd hsf-training-cmake-webpage/code/04-debug

試看看cmake -S . -B build --debug-find。記得要先清除build資料夾否會出現debug訊息。你也可以用CMAKE_FIND_DEBUG_MODE來針對你想要debug的find_…模組來除錯

1
2
3
set(CMAKE_FIND_DEBUG_MODE TRUE)
find_program(...)
set(CMAKE_FIND_DEBUG_MODE FALSE)

設定build types

如果你想要執行C++ debugger,你會需要設定很多flag,CMake提供四種build types來幫你設定這些flag。

  • CMAKE_BUILD_TYPE=Debug : 輸出所有除錯訊息
  • CMAKE_BUILD_TYPE=RelWithDebInfo : release build 不過有一些額外的除錯訊息
  • CMAKE_BUILD_TYPE=Release : 最佳化release build
  • CMAKE_BUILD_TYPE=MinSizeRel : minimum size release
    下面範例示範如何用CMake的Debug模式編譯,並且用GDB除錯
    1
    2
    3
    4
    cd hsf-training-cmake-webpage/code/04-debug
    cmake -S . -B build-debug -DCMAKE_BUILD_TYPE=Debug
    cmake --build build-debug
    gdb build-debug/simple_example
    GDB指令
    1
    2
    3
    4
    5
    # GDB                
    break my_sin
    r
    watch sign
    c

尋找套件

1
find_package(MyPackage 1.2)

這個命令會首先尋找CMAKE_MODULE_PATH這份路徑清單,在這些路徑底下尋找FindMyPackage.cmake這個檔案,注意他是直接把find_package第一個參數MyPackage產生出FindMyPackage.cmake這個搜尋目標,所以如果我們寫成find_package(OpenCV 3),那搜尋目標就是FindOpenCV.cmake
如果找不到FindMyPackage.cmake他就會接著尋找MyPackageConfig.cmake,如果MyPackage_DIR存在的話也會搜尋這個路徑。
在CMake3.12+之後,如果你的套件不是安裝在系統預設路徑,你可以設定環境變數<PackageName>_ROOT讓CMake搜尋。以下以Bash命令設定環境變數為例

1
export HDF5_ROOT=$HOME/software/hdf5-1.12.0

或者是設定CMAKE環境變數,以下以Bash命令設定環境變數為例

1
export CMAKE_PREFIX_PATH=$HOME/software/hdf5-1.12.0:$HOME/software/boost-1.74.0:$CMAKE_PREFIX_PATH

FindPackage.cmake

這是舊方法(MODULE方法),這裡有CMake提供的FindPackage清單

PackageConfig

這是由package開發者所提供,簡而言之如果你是package開發者,你應該提供<package>Config.cmake並且自行維護。

CMake結合PkgConfig

參考:
https://hsf-training.github.io/hsf-training-cmake-webpage/08-debugging/index.html

https://hsf-training.github.io/hsf-training-cmake-webpage/09-findingpackages/index.html

現代CMake觀念
https://pabloariasal.github.io/2018/02/19/its-time-to-do-cmake-right/

CMake結合PkgConfig
https://stackoverflow.com/a/74038236

CMake教學系列二 CMake的觀念

在第一篇我們已經學會如何編譯單一檔案的C++程式,接下來將介紹一些CMake的觀念

關於Targets

在CMake有兩種方式建立targettarget的名稱必須是唯一的

  • add_executable(myexample simple.cpp) : 建立了一個名為myexample的target,並且命名輸出的執行檔為myexample
  • add_library(mylibrary simplelib.cpp) : 建立了一個名為myexample的target,並且命名輸出的函式庫為myexample
    target很像程式裡面物件的概念,他擁有許多屬性,例如SOURCES在這個範例他會擁有simple.cpp這個檔案,target所有的屬性可以在這裡查到

特別的targets

有些C++函式庫只有標頭檔(例如函式庫Eigen),這種情況下你沒辦法真正的輸出一個library檔,但是他又被製作成target讓其他target使用。這種函式庫我們叫做interface libraries而再CMake我們可以用下面指令把他製作成target。注意到跟前面不同的是他只需要設為INTERFACE屬性而且他不需要輸入*.cpp檔。

1
add_library(some_header_only_lib INTERFACE)

另一種狀況是你要直接使用預先編譯好的pre-built library,這種情況你也不會有*.cpp可以給CMake編譯。這種library在CMake裡我們稱為imported library,我們可以用關鍵字IMPORTED來告訴CMake。

1
add_library(some_library STATIC IMPORTED)

連接

一旦你有了target就可以用target_link_libraries連接所需要的東西,連接的時候有PUBLIC, PRIVATE, 和 INTERFACE三種屬性可以選擇,他有點像程式語言中的存取控制。如果TargetB引用TargetA,則TargetA的PUBLIC屬性都會傳遞給TargetB。

範例1: Include directories

  • target_include_directories(TargetA PRIVATE mydir)連接TargetA和mydir資料夾屬性為PRIVATE,這時候TargetA的INCLUDE_DIRECTORIES屬性就會包含mydir資料夾
  • target_include_directories(TargetA INTERFACE mydir)連接TargetA和mydir資料夾屬性為INTERFACE,這時候TargetA的INTERFACE_INCLUDE_DIRECTORIES屬性就會包含mydir資料夾
  • target_include_directories(TargetA PUBLIC mydir)連接TargetA和mydir資料夾屬性為PUBLIC,則INCLUDE_DIRECTORIES INTERFACE_INCLUDE_DIRECTORIES都會包含mydir資料夾

CMake變數

為了方便起見,接下來的範例會直接執行example.cmake檔,而不是建立一個CMakeLists.txt。首先你需要先建立一個example.cmake,如果要執行example.cmake可以利用CMake的 -p 選項。這樣可以節省去許多編譯設定,也比較容易實驗

1
2
# Assuming you have a file called example.cmake:
cmake -P example.cmake

Local variables

example.cmake輸入以下指令,然後執行cmake -P example.cmake就可以看到終端機輸出你的變數。在這裡set指令設定變數,message指令印出變數,這裡我們使用的是STATUSmessage,還有其他的狀態你可以到官網查看

1
2
set(MY_VARIABLE "I am a variable")
message(STATUS "${MY_VARIABLE}")

{: file=’example.cmake’}

Cached variables

cached variables在CMake十分重要,通常我們會用CMake圖形介面或是CMake命令介面設定許多cached variables,這些變數都會被寫到CMakeCache.txt檔案裡面。當你執行CMake的時候CMake會先讀取這些Cached variables。下面這個範例會設定一個Cached variables,不過因為我們用-P執行*.cmake,所以不會真的輸出一個CMakeCache.txt,你可以參考上一篇的範例觀察CMake如何產生CMakeCache.txt

1
2
set(MY_VARIABLE "I am a variable")
message(STATUS "${MY_VARIABLE}")

{: file=’example.cmake’}
而因為Cached variables幾乎都是可以讓使用者設定的選項,所以有一個更方便的指令option

1
option(MY_OPTION "On or off" OFF)

Other variables

  • 你可以藉由$ENV{my_env}來取得環境變數my_env的值,你也可以利用if(DEFINED ENV{my_env})來檢查my_env這個環境變數是不是有被設定(注意這個指令沒有$)
  • target的屬性其實也是一種變數,你可以利用get_propertyset_property,或者是get_target_propertiesset_target_properties來查看和設定target屬性,除此之外還有CMake本身的屬性,可以從cmake-properties這份清單查詢。

Target properties and variables

你已經知道target的屬性可以控制target的行為,如果你仔細觀察會發現,有許多target屬性(例如CXX_EXTENSIONS)會有一個對應的CMake變數,並且以CMAKE_為開頭(例如CMAKE_CXX_EXTENSIONS),這些CMake變數是用來初始化對應的target屬性用的。因此你可以先設定這些CMake變數,這樣就可以快速設定target對應屬性的初始值。

搜尋工具

CMake有一些方便的glob指令可以用來處理string

1
file(GLOB OUTPUT_VAR *.cxx)

在這裡要提到一個很重要的flagCONFIGURE_DEPENDS(CMake 3.12+),如果沒有這個flag則在重新執行建置的時候,cmake不會再次用glob去搜尋,如此一來如果這些被搜尋的資料夾放入的新的檔案也不會被CMake發現。因此如果你想要在每一次建置專案的時候都重新搜尋,記得加上這個flag

1
file(GLOB OUTPUT_VAR *.cxx CONFIGURE_DEPENDS)

參考:
CMake Fundamentals Part 4
https://jeremimucha.com/2021/02/cmake-fundamentals-part4/

Modern CMake is like inheritance
https://kubasejdak.com/modern-cmake-is-like-inheritance

CMake doc : Importing Libraries
https://cmake.org/cmake/help/latest/guide/importing-exporting/index.html#importing-libraries

https://cliutils.gitlab.io/modern-cmake/chapters/basics.html

https://hsf-training.github.io/hsf-training-cmake-webpage/04-targets/index.html

https://hsf-training.github.io/hsf-training-cmake-webpage/05-variables/index.html

安裝Vagrant製作測試環境

安裝

Ubuntu22.04安裝Vagrant

1
2
3
wget -O- https://apt.releases.hashicorp.com/gpg | gpg --dearmor | sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install vagrant

安裝VirtualBox
/etc/apt/sources.list最下面加上這一行

1
deb [arch=amd64 signed-by=/usr/share/keyrings/oracle-virtualbox-2016.gpg] https://download.virtualbox.org/virtualbox/debian jammy contrib

然後下載和註冊public key

1
wget -O- https://www.virtualbox.org/download/oracle_vbox_2016.asc | sudo gpg --dearmor --yes --output /usr/share/keyrings/oracle-virtualbox-2016.gpg

顯示fingerprint,應該要為B9F8 D658 297A F3EF C18D 5CDF A2F6 83C5 2980 AECF

1
gpg --show-keys --with-fingerprint /usr/share/keyrings/oracle-virtualbox-2016.gpg

最後安裝

1
2
sudo apt-get update
sudo apt-get install virtualbox-6.1

建立一個Ubuntu20.04 box

1
2
3
vagrant init ubuntu/focal64
vagrant up
vagrant ssh

x11 forwarding

https://jcook0017.medium.com/how-to-enable-x11-forwarding-in-windows-10-on-a-vagrant-virtual-box-running-ubuntu-d5a7b34363f

共享資料夾

https://developer.hashicorp.com/vagrant/tutorials/getting-started/getting-started-synced-folders

參考:
安裝VirtualBox
https://www.virtualbox.org/wiki/Linux_Downloads

顯示gpg fingerprint
https://superuser.com/a/1747762

Linux安裝prebuild函式庫以OpenCV為例

我的部落格文章轉錄–Linux環境撰寫Shared Library有詳細介紹如何製作和安裝Shared Library,如果想要了解更多Shared Library安裝和製作方式可以參考這篇。
{: .prompt-tip }

在這篇我們想直接使用OpenCV預編譯的函式庫,省去自己編譯函式庫的時間,首先我們先到官網提到的Third-party packages的System packages in popular Linux distributions,找到自己的Linux distributions,在這裡我們是使用Ubuntu22,而我們要下載的是development files for opencv也就是libopencv-dev_4.5.4+dfsg-9ubuntu4_amd64.deb這個連結,在Install Howto的地方可以看到安裝指令。

如果你不想弄亂你的環境,建議你可以用Vagrant建立一台測試環境來測試一下安裝後的結果,或是做一些實驗。
安裝Vagrant的方法在安裝vagrant製作測試環境
{: .prompt-tip }

1
2
sudo apt-get update
sudo apt-get install libopencv-dev

Files的地方可以看到他幫我們裝了什麼東西以及他們被安裝的位置。在Requires的地方可以看到他還幫我們安裝了哪些相依套件。
在這裡我們比較一下libopencv-core-devlibopencv-core4.5d這兩個函式庫。在這兩個package的Files的地方可以發現libopencv-core-dev幫我們在/usr/include/opencv4多裝了很多標頭檔(*.hpp),因為如果要在我們自己的C++中使用OpenCV,必須要include OpenCV的標頭檔,而dev套件已經幫我們幫把標頭檔都放在/usr/include/opencv4讓我們可以引用了。而在編寫OpenCV的C++專案的時候,要記得把這個include資料夾放到你的專案裡。

pkg-config幫我們列出全部的標頭檔位置和opencv的名稱

標頭檔路徑只需要加上g++選項-I/usr/include/opencv4就可以了,不過如果要把所有用到的library都手動寫出來實在很麻煩,這時候pkg-config可以幫我們把全部的opencv library全部列出來,我們可以試看看在終端機輸入下面指令pkg-config --libs --cflags opencv4(如果安裝的是opencv 2.x或3.x要輸入pkg-config --libs --cflags opencv),終端機的回應應該會長的像下面這樣。

1
2
$ pkg-config --libs --cflags opencv4
-I/usr/include/opencv4 -lopencv_stitching -lopencv_alphamat -lopencv_aruco -lopencv_barcode -lopencv_bgsegm -lopencv_bioinspired -lopencv_ccalib -lopencv_dnn_objdetect -lopencv_dnn_superres -lopencv_dpm -lopencv_face -lopencv_freetype -lopencv_fuzzy -lopencv_hdf -lopencv_hfs -lopencv_img_hash -lopencv_intensity_transform -lopencv_line_descriptor -lopencv_mcc -lopencv_quality -lopencv_rapid -lopencv_reg -lopencv_rgbd -lopencv_saliency -lopencv_shape -lopencv_stereo -lopencv_structured_light -lopencv_phase_unwrapping -lopencv_superres -lopencv_optflow -lopencv_surface_matching -lopencv_tracking -lopencv_highgui -lopencv_datasets -lopencv_text -lopencv_plot -lopencv_ml -lopencv_videostab -lopencv_videoio -lopencv_viz -lopencv_wechat_qrcode -lopencv_ximgproc -lopencv_video -lopencv_xobjdetect -lopencv_objdetect -lopencv_calib3d -lopencv_imgcodecs -lopencv_features2d -lopencv_dnn -lopencv_flann -lopencv_xphoto -lopencv_photo -lopencv_imgproc -lopencv_core

g++的指令是長這樣

1
/usr/bin/g++ -g main.cpp -o main `pkg-config --libs --cflags opencv4`

你可以到linux下設定vscode-cmake-gcc-gdb來開發c-專案查看如何用VSCode連接和編譯OpenCV library。

參考:
https://blog.gtwang.org/programming/ubuntu-linux-install-opencv-cpp-python-hello-world-tutorial/
https://pkgs.org/search/?q=opencv
https://ubuntu.pkgs.org/22.04/ubuntu-universe-amd64/libopencv-dev_4.5.4+dfsg-9ubuntu4_amd64.deb.html