CMake

References

Note

📘 NOTE
case insensitive
📘 NOTE
CMake 所產生出來的 Makefile 無法獨立運作,建置過程仍然必須依賴 CMake,因此無法直接將 makefile 搬移到沒有 CMake 的環境中使用。事實上,只要更換環境就應該重新執行 CMake,針對目前環境配置產生 Makefile。

VScode Setting

{ "cmake.configureOnEdit": false, // CMake Tools 禁止 CMakeLists.txt 存檔自動構建 }

Configure

配置 CMake。
配置完後,你可以在 CMakeCache.txt 可找到一些配置的相關資訊。
一般而言,編譯器、Makefile 產生器會自動選擇全域可見的那些

GUI

在 VScode 按 F7 自動構建。

CLI

cmake -S <專案目錄>
      -B <建置目錄>
      -G <Makefile 產生器>
      -D <變數>=<>
cmake -S . -B build -G "Unix Makefiles"
  • -S:指定原始碼目錄(通常是專案的跟目錄)
  • -B:指定建置目錄(產生 Makefile 與執行檔的目錄)
  • -G:指定 Makefile 產生器
    • "Ninja":ninja
    • "Unix Makefiles":make
    • "NMake Makefiles":nmake
    • "MinGW Makefiles":mingw32-make
    • "Visual Studio 17 2022":MSVC
  • -D:設定變數
    可於 CMakeLists.txt 中使用 set 指定。
    • CMAKE_C_COMPILER=gcc:C 編譯器(gcc)
    • CMAKE_CXX_COMPILER=g++:C++ 編譯器(g++)
    • CMAKE_BUILD_TYPE=Release:建構型態(Release, Debug
    • CMAKE_TOOLCHAIN_FILE=.../vcpkg.cmake:套件管理器(vcpkg
cmake -P <腳本名稱>
  • -P:將 CMake 檔案當成腳本執行

    可單純作為試驗邏輯使用

Build

執行檔應位於 build 目錄頂層

CLI

cmake --build build

Example

範例 0

單檔案

範例 1

多檔案

範例 2

靜態鏈結函式庫

範例 3

動態鏈結函式庫

🚨 CAUTION
這裡要指定一下 Makefiles 產生器,要不然編譯器都不知道為啥會自動選擇 VS 的

專案目錄結構

Project3
├── CMakeLists.txt
├── lib
│   ├── CMakeLists.txt
│   ├── my_dll.cpp
│   └── my_dll.h
└── main.cpp

CMakeLists.txt

cmake_minimum_required(VERSION 3.16) project(Project3) add_subdirectory(lib)

lib/CMakeLists.txt

set(LIBHELLO_SRC ./my_dll.h ./my_dll.cpp) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ../lib_out) # 動態鏈結函式庫 add_library(my_dll SHARED ${LIBHELLO_SRC}) install(TARGETS my_dll) set_target_properties(my_dll PROPERTIES LINKER_LANGUAGE C RUNTIME_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} OUTPUT_NAME "my_dll" PREFIX "" )

lib/my_dll.h

#ifndef CPP_LEARN_MY_DLL_H #define CPP_LEARN_MY_DLL_H #define EXPORT_DLL __declspec(dllexport) extern "C" EXPORT_DLL int add(int a, int b); #endif // CPP_LEARN_MY_DLL_H

lib/my_dll.cpp

#include "my_dll.h" int add(int a, int b) { return (a + b); }

run_dll.cpp

#include <windows.h> #include <iostream> typedef int (*add)(int, int); int main() { HINSTANCE handle = LoadLibrary("./my_dll.dll"); auto f = (add) GetProcAddress(handle, "add"); std::cout << f(1, 32) << std::endl; FreeLibrary(handle); return 0; }

Commands

basic

  • set:變數

    ​​set(var hello)

    ✅:foo 變數為 string list

    ​​set(foo this is a list) ​​set(foo this;is;a;list)
  • messages:標準輸出

    ​​message(${var})
  • math:數學計算

    ​​math(EXPR var "1 + 2 * 3") ​​message("var = ${var}") # var = 7
  • if / elseif / else / endif:條件邏輯

    ​​if(expr) ​​ command1(arg) ​​elseif() ​​ command2(arg) ​​else() ​​ command3(arg) ​​endif()
  • foreach / endforeach:迴圈

    ​​set(V alpha beta gamma) ​​message(${V}) ​​# alphabetagamma ​​foreach(i ${V}) ​​ message(${i}) ​​endforeach() ​​# alpha ​​# beta ​​# gamma
  • macro / endmacro / function / endfunction:函式、巨集
    ✅:function 會建立 local 變數,而 macro 則會影響 global 變數

    ​​set(k 5) ​​macro(mprint MESSAGE) ​​ set(k ${MESSAGE}) ​​ message(${MESSAGE}) ​​endmacro(mprint) ​​function(fprint MESSAGE) ​​ set(k ${MESSAGE}) ​​ message(${MESSAGE}) ​​endfunction(fprint) ​​message("k=${k}") # k=5 ​​mprint("from mprint") # from mprint ​​fprint("from fprint") # from fprint ​​message("k=${k}") # k=from mprint

build

  • add_subdirectory:將指定目錄的 CMake 專案(內含 CMakeLists.txt)一起加入建置

    ​​add_subdirectory(libstatic1/)
  • add_executable:新增一個 CMake 目標,其為執行檔

    ​​add_executable(project-01)
  • add_library:新增一個 CMake 目標,其為鏈結函式庫
    靜態鏈結函式庫

    ​​add_library(libstatic1 STATIC)

    動態鏈結函式庫

    ​​add_library(my_dll SHARED ./my_dll.h ./my_dll.cpp)
  • add_definitions:構建時,加入 preprocessor 定義

    ​​add_definitions("-DFOO -DBAR")
  • target_sources:構建 CMake 目標時,需要哪些源代碼(無須標頭檔)

    ​​target_sources(project-01 ​​ PRIVATE ​​ main.cpp ​​ f1.cpp ​​ f2.cpp ​​)
  • include_directories / target_include_directories:添加標頭檔目錄
    target 是針對特定 CMake 目標,現代較推薦後者。

    ​​include_directories(${PROJECT_SOURCE_DIR}/external/headers) # 2. 再搜索一般情況 ​​include_directories(BEFORE ${PROJECT_SOURCE_DIR}/include) # 1. 先搜索 BEFORE ​​include_directories(AFTER /usr/local/include) # 3. 再搜索 AFTER
  • link_libraries / target_link_libraries:添加鏈結函式庫目錄
    target 是針對特定 CMake 目標,現代較推薦後者。
    通常來自 find_packageadd_subdirectory

    ​​target_link_libraries(project-02 ​​ PRIVATE ​​ libstatic1 ​​)
  • set_target_properties:屬性控制編譯選項(若無給定,則採用全域預設值)

    ​​set_target_properties(my_dll ​​ PROPERTIES ​​ LINKER_LANGUAGE C ​​ RUNTIME_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ​​ LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ​​ OUTPUT_NAME "my_dll" ​​ PREFIX "" ​​)
  • include:執行另一個 CMake 腳本

    ​​include($ENV{IDF_PATH}/tools/cmake/project.cmake)
    
  • find_package