CMake 使用基礎

最近學習 opencv、ffmpeg,這些優秀的庫都是使用 c/c++ 來編寫的,這些項目都是由多個項目組成的,若是沒有一個很好的工具去管理它們之間的關係,這對於每一個開發者來講,若是沒有原做者的幫助都是不可能完成搭建的。所以就引入了 CMake ,咱們在下載它們的源碼時也會帶着 CMakeLists.txt 文檔,這樣開發者只要使用 CMake 工具就能夠完成源碼的構建工做。c++

那麼下面就分幾個步驟講述通常用法git

單個源文件構建

咱們須要將源文件 main.c 構建成可執行文件,只須要3行配置就能夠完成。這裏的 main.c 是求數的次方,這兩個數由運行時手動給出。其代碼以下:github

#include <stdio.h>
#include <stdlib.h>
/**
 * power - Calculate the power of number.
 * @param base: Base value.
 * @param exponent: Exponent value.
 *
 * @return base raised to the power exponent.
 */
double power(double base, int exponent)
{
    int result = base;
    int i;
    
    if (exponent == 0) {
        return 1;
    }
    
    for(i = 1; i < exponent; ++i){
        result = result * base;
    }
    return result;
}

int main(int argc, char *argv[])
{
    if (argc < 3){
        printf("Usage: %s base exponent \n", argv[0]);
        return 1;
    }
    double base = atof(argv[1]);
    int exponent = atoi(argv[2]);
    double result = power(base, exponent);
    printf("%g ^ %d is %g\n", base, exponent, result);
    return 0;
}
複製代碼

接着要確保源文件和 CMakeLists.txt 文檔在同級目錄, 以下圖所示。bash

CMakeLists.txt 配置文件函數

# 支持 cmake 最小版本爲 2.6
cmake_minimum_required(VERSION 2.6)

# 項目名稱
project(Demo1)

# 指定源代碼,生成可執行文件 Demo1
add_executable(Demo1 main.c)

複製代碼

經過這幾行配置就能夠生成一個可執行文件 Demo1, 你須要使用以下命令構建工具

// 在 CMakeLists.txt 統計目錄執行
1. cmake .
2. make 

複製代碼

多目錄源文件構建

多數狀況下,咱們的代碼都是由不少目錄構成。咱們須要在構建的子目錄下也加入 CMakeLists.txt 文檔來描述源碼的關係。例如:咱們將 main.c 中的 power 函數抽離到一個新目錄 math 下,以下圖所示:學習

math/CMakeLists.txt 內容如測試

# 查找全部源文件, 將其存放到 SRC_DIR 變量中.
aux_source_directory(. SRC_DIR)

# 將源文件生成連接庫
add_library(MathFunctions ${SRC_DIR})
複製代碼

主目錄下的 CMakeLists.txt 加入如下幾行ui

# 將源文件下 math 目錄頭文件添加到編譯器頭文件搜索目錄.
include_directories ("${PROJECT_SOURCE_DIR}/math")

# 將 math 目錄加入到 cmake 構建. 會執行子目錄中的 CMakeLists.txt
add_subdirectory(math)

# 指定源代碼,生成可執行文件 Demo2
add_executable(Demo2 main.c)

# 將 MathFunctions 連接到 Demo1
target_link_libraries(Demo2 MathFunctions)
複製代碼

最後就是執行 cmake . 和 make 命令便可生成可執行文件。spa

自定義構建配置

在某些狀況下,咱們須要對源碼進行控制,好比咱們上面的代碼中,求數的次方。咱們也可使用庫函數來實現。咱們能夠在構建的時候進行控制。那麼咱們改進下主目錄下的 CMakeLists.txt 文件。

# 引入頭文件,對源碼進行配置, 從 config.h.in 配置的變量會輸出到 config.h 中
# 這點就是利用宏定義 #define 
configure_file(
    "${PROJECT_SOURCE_DIR}/config.h.in"
    "${PROJECT_BINARY_DIR}/config.h"
)

# 可選配置,可對變量 MY_MATH_FLAG 進行設置開關 ON/OFF
option(MY_MATH_FLAG 
    "是否使用自定義函數實現開平方計算"
    OFF
)

if (MY_MATH_FLAG)
    # 將源文件下 math 目錄頭文件添加到編譯器頭文件搜索目錄.
    include_directories ("${PROJECT_SOURCE_DIR}/math")
    # 將 math 目錄加入到 cmake 構建. 會執行子目錄中的 CMakeLists.txt
    add_subdirectory(math)
    set(EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
endif(MY_MATH_FLAG)


# 指定源代碼,生成可執行文件 Demo3
# 這裏也可使用 aux_source_directory(. SRC_DIR) 來替換,這樣的好處就是不用寫多個源文件
add_executable(Demo3 main.c)

# 將 MathFunctions 連接到 Demo3
target_link_libraries(Demo3 ${EXTRA_LIBS})

複製代碼

咱們可使用 ccmake 命令來進行編譯,經過它能夠指定 MY_MATH_FLAG 變量 ON/OFF 狀態。而後最終就會決定 config.h 中生成的的內容,若是 OFF 會生成

/* #undef MY_MATH_FLAG */

複製代碼

爲 ON 會生成

#define MY_MATH_FLAG

複製代碼

有了 config.h,咱們就能夠在源碼中進行條件編譯

#include <stdio.h>
#include <stdlib.h>
#include "config.h"

// MY_MATH_FLAG 條件編譯
#ifdef MY_MATH_FLAG
    #include "CalcPower.h"
#else
    #include <math.h>
#endif

int main(int argc, char *argv[])
{
    if (argc < 3){
        printf("Usage: %s base exponent \n", argv[0]);
        return 1;
    }
    double base = atof(argv[1]);
    int exponent = atoi(argv[2]);

    #ifdef MY_MATH_FLAG
        double result = power(base, exponent);
        printf("使用自定義函數實現平方計算: %g ^ %d is %g\n", base, exponent, result);
    #else
        double result = pow(base, exponent);
        printf("使用系統函數實現平方計算: %g ^ %d is %g\n", base, exponent, result);
    #endif
        printf("%g ^ %d is %g\n", base, exponent, result);
    
    return 0;
}

複製代碼

添加測試

一些狀況下,咱們須要構建完成後進行測試,咱們可使用 make install 來實現,改造一下主目錄下的 CMakeLists.txt 文件

# 引入頭文件,對源碼進行配置
configure_file(
    "${PROJECT_SOURCE_DIR}/config.h.in"
    "${PROJECT_BINARY_DIR}/config.h"
)

# 可選配置,可對變量 MY_MATH_FLAG 進行設置開關 ON/OFF
option(MY_MATH_FLAG 
    "是否使用自定義函數實現開平方計算"
    ON
)

if (MY_MATH_FLAG)
    # 將源文件下 math 目錄頭文件添加到編譯器頭文件搜索目錄.
    include_directories ("${PROJECT_SOURCE_DIR}/math")
    # 將 math 目錄加入到 cmake 構建. 會執行子目錄中的 CMakeLists.txt
    add_subdirectory(math)
    set(EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
endif(MY_MATH_FLAG)


# 指定源代碼,生成可執行文件 Demo1
# 這裏也可使用 aux_source_directory(. SRC_DIR) 來替換,這樣的好處就是不用寫多個源文件
add_executable(Demo4 main.c)

# 將 MathFunctions 連接到 Demo1
target_link_libraries(Demo4 ${EXTRA_LIBS})

# 指定 Demo4 安裝路徑
install (TARGETS Demo4 DESTINATION bin)
install (FILES "${PROJECT_BINARY_DIR}/config.h"        
         DESTINATION include)


# 添加測試
enable_testing()
#[[
add_test (test_run5_2 Demo4 5 2)
set_tests_properties (test_run5_2 PROPERTIES PASS_REGULAR_EXPRESSION "is 25")

add_test (test_run2_2 Demo4 2 2)
set_tests_properties (test_run2_2 PROPERTIES PASS_REGULAR_EXPRESSION "is 4")

add_test (test_run3_2 Demo4 3 2)
set_tests_properties (test_run3_2 PROPERTIES PASS_REGULAR_EXPRESSION "is 9")

add_test (test_run10_2 Demo4 10 2)
set_tests_properties (test_run10_2 PROPERTIES PASS_REGULAR_EXPRESSION "is 100")
]]

# 使用宏定義簡化測試配置的書寫
macro(do_test arg1 arg2 result)
    add_test(test_run${arg1}_${arg2} Demo4 ${arg1} ${arg2})
    set_tests_properties(test_run${arg1}_${arg2} PROPERTIES PASS_REGULAR_EXPRESSION ${result}
    )    
endmacro(do_test arg1 arg2 result)

do_test(5 2 "is 25")
do_test(2 2 "is 4")
do_test(3 2 "is 9")
do_test(10 2 "is 100")

複製代碼

先記錄到這裏,後續再補充。有須要代碼的能夠 點擊這裏, 代碼都經過按 tag 區分不一樣的分之。能夠切換到對應的分之查看。

相關文章
相關標籤/搜索