CMake 簡要教程

CMake 簡要教程

在 C/C++ 領(lǐng)域潦俺, 一直很困擾我的是它沒有一個令我覺得很 “用戶友好” 的 “編譯/構(gòu)建” 系統(tǒng)(很可能是有,只是我不知道而已)渡紫。 一般情況下横媚,如果只是寫一個 demo,我就直接命令行 gcc 編譯了一罩;如果是小工程杨幼,就手寫一個 Makefile 編譯了。

記得剛接觸 OpenCV 的時候聂渊,想自己編譯一份源碼差购,那真是的一場 “戰(zhàn)爭”。各種編譯汉嗽,各種報錯欲逃。從那時候起,就想著要好好學(xué)習一下 CMake 了饼暑,但是每次都是看一下就忘稳析,看一下就忘。

最近弓叛,因為迫不得已要聯(lián)調(diào)一些 JavaC++ 的代碼彰居,不能連怎么編譯人家的代碼都不會,并且想要在 Java 里調(diào)用 C++ 還涉及到庫的連接等一堆問題撰筷。所以陈惰,決定好好學(xué)習一下 CMake

在往下面寫之前先強調(diào)幾個事情:

  1. 不是迫不得已毕籽,不要搞什么 JNI (就是別沒事要在 Java 里調(diào) C++抬闯,反之亦然);
  2. 不是迫不得已关筒,不要寫 C++ 了 (我是 C++ 勸退黨)溶握;
  3. 不是迫不得己,也不要學(xué)什么 CMake 平委;

如果你還在看奈虾,那肯定就是迫不得已了,那沒招了廉赔,好好往下看吧。本文將會按照以下幾個步驟來講解怎么使用 CMake

  1. 最最最簡單的一個 CMakeLists.txt 怎么寫以及怎么使用它匾鸥;
  2. 如何使用 CMake 進行工程管理和構(gòu)建 (每個子目錄都要寫一個 CMakeLists.txt)蜡塌;
  3. 如何構(gòu)建靜態(tài)庫和動態(tài)庫;
  4. 如何使用別人構(gòu)建好的庫勿负;

最后馏艾,本文默認讀者具有一定的命令行使用經(jīng)驗劳曹。

最簡單的例子

打開命令行:

// cd 到一處你準備放置本文示例的目錄下
mkdir cmake_practice && cd cmake_practice
mkdir simple_example && cd simple_example

// 創(chuàng)建兩個文件
touch main.c
touch CMakeLists.txt

// 創(chuàng)建一個目標文件夾 (所有的構(gòu)建結(jié)果和中間文件會在這里面, 不污染源嗎目錄)
// 這是 cmake 推薦的 build out-of-source, 全文都采取這種方式
mkdir build

往建好的兩個文件里填入如下內(nèi)容:

// main.c

#include<stdio.h>

int main(int argc, char *argv[]) {
    printf("Hello world (build from cmake)");
    return 0;
}
// CMakeLists.txt

cmake_minimum_required (VERSION 3.8)

project (HelloWorld)
add_executable (HelloWorld main.c)

上面這一小段一共有三個命令,逐一解釋一下:

  • cmake_minimum_required : 這個顧名思義了琅摩,最小的 cmake 版本要求铁孵;
  • project : 給項目起一個名字,這個命令會隱式的定義兩個變量房资,PROJECT_SOURCE_DIRPROJECT_BINARY_DIR蜕劝;
  • add_executable : 定義可執(zhí)行文件的名字 (第一個參數(shù)) 和 用于編譯得到這個可執(zhí)行文件的依賴文件 (第二個參數(shù));

再回到命令行轰异,cd 到剛才建好的 build 文件夾下面:

cmake .. && make

如此岖沛,我們就完成了使用 CMake 構(gòu)建一個可執(zhí)行文件的最簡單例子。在實際生活中搭独,如果只是需要編譯一個文件我們不需要使用如此復(fù)雜的步驟婴削,直接命令行 gcc 即可,這里僅僅是為了演示 CMake 的使用牙肝。

工程化

一般來說唉俗,當我們的文件比較多的時候,我們會根據(jù)某種邏輯配椭,把不同的源碼文件放在不同的子文件夾中進行管理虫溜。要到達這個目標,我們需要在每一個子文件夾下寫一個 CMakeLists.txt 這個文件負責告訴 CMake 如何處理這個文件夾下的文件颂郎。

繼續(xù)拿上面的例子為例吼渡,但是這一次,我需要把所有的源碼文件放大一個 src 目錄下乓序。

打開命令行:

// 假設(shè)我們在上面設(shè)置好的 "cmake_practice" 目錄下
mkdir work_with_sub_dir && cd work_with_sub_dir
mkdir src && cd src

touch main.c
touch CMakeLists.txt

cd ..
mkdir build
touch CMakeLists.txt

然后分別在新建的三個文件中填入如下內(nèi)容:

// work_with_sub_dir 目錄下的 CMakeLists.txt
cmake_minimum_required (VERSION 3.8)

project (WorkWithSubDir)
add_subdirectory(src bin)

這里有一個新的命令:

add_subdirectory : 這個命令的意思是寺酪,在構(gòu)建的目標文件夾下,用 bin 作為目錄明替換掉原來的 src

// src 目錄下的 CMakeLists.txt
cmake_minimum_required (VERSION 3.8)
add_executable (WorkWithSubDir main.c)
// src 目錄下的 main.c
#include<stdio.h>

int main() {
    printf("Hello world from main.c in src\n");
    return 0;
}

回到命令行切換到 build 目錄下:

cmake .. && make
bin/WorkWithSubDir

應(yīng)該可以看到 Hello world from main.c in src 在控制臺輸出替劈。需要在多層目錄下構(gòu)建時寄雀,重點就在于為每一個子目錄寫一個 CMakeLists.txt 文件,告訴 cmake 如何處理該文件下的文件陨献。

構(gòu)建庫以及庫安裝

首先新建一個目錄盒犹,按照如下的結(jié)構(gòu)新建文件:

目錄結(jié)構(gòu)

分別填入內(nèi)容:

// CMakeLists.txt

# define minimum version of cmake
cmake_minimum_required (VERSION 3.8)

PROJECT(create_lib)
ADD_SUBDIRECTORY(lib)
// hello.c
#include "hello.h"

void HelloFunc() { 
    printf("Hello World\n"); 
}
// hello.h
#ifndef HELLO_H 
#define HELLO_H 
#include <stdio.h> 
void HelloFunc(); 
#endif

前面的內(nèi)容都很尋常,唯一比較復(fù)雜的是 lib/CMakeLists.txt

// lib/CMakeLists.txt
# define minimum version of cmake
cmake_minimum_required (VERSION 3.8)

SET(LIBHELLO_SRC hello.c)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})

ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")

INSTALL(
    TARGETS hello hello_static
    LIBRARY DESTINATION lib
    ARCHIVE DESTINATION lib
)
INSTALL(FILES hello.h DESTINATION include/hello)

一個一個地看看這些命令:

  • SET(LIBHELLO_SRC hello.c), 設(shè)置一個變量
  • ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC}), 編譯一個動態(tài)庫
  • ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC}), 編譯一個靜態(tài)庫眨业,第一個參數(shù)是名字急膀,由于不能重名所以這樣命名
  • SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello”), 因為不管動態(tài)還是靜態(tài)庫 我們期望他們的名字應(yīng)該是一樣的,這里把名字設(shè)置成一樣都是 “hello”
  • INSTALL(FILES hello.h DESTINATION include/hello) 安裝一系列的頭文件
  • INSTALL(TARGETS hello hello_static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib), 安裝庫文件

編譯完成了龄捡,下面關(guān)心一下怎么安裝卓嫂。

安裝的主要作用是,把一些需要的頭文件和編譯好的庫文件放到系統(tǒng)特定的目錄中 (比如 /usr/local/lib, /usr/local/include 等)聘殖。想到達到這一步需要知道兩個事情:

  • cmake 的 -DCMAKE_INSTALL_PREFIX 標志 : 這個標志主要用于制定安裝的路徑前綴晨雳;
  • cmake 的 INSTALL 命令 : 這個命令用來告訴 cmake 怎么安裝各種不同的文件或者庫 (怎么使用看上面的示例)行瑞;

繼續(xù)用上面的例子,命令行 cdbuild 文件下:

cmake DCMAKE_INSTALL_PREFIX=../install ..
cmake .. && make && make install

因為我不想污染系統(tǒng)環(huán)境餐禁,所以就安裝在了當前的工程的根目錄下血久,執(zhí)行完上面的命令,應(yīng)該能看到如下的目錄結(jié)構(gòu):

目錄結(jié)構(gòu)

使用編譯好的庫

終于來到了最后一步帮非,我們繼續(xù)上面的例子氧吐,我們已經(jīng)編譯了一個靜態(tài)一個動態(tài)庫并且將相關(guān)的頭文件安裝在了一些特定的目錄里面。下面我們來看看如何調(diào)用已經(jīng)編譯好的庫喜鼓。

建立如下所示的文件:

目錄結(jié)構(gòu)

填入如下的內(nèi)容:

// CMakeLists.txt
# define minimum version of cmake
cmake_minimum_required (VERSION 3.8)

PROJECT(use_lib)
ADD_SUBDIRECTORY(src bin)
// src/CMakeLists.txt
# define minimum version of cmake
cmake_minimum_required (VERSION 3.8)

ADD_EXECUTABLE(main main.c)
INCLUDE_DIRECTORIES(<where you install the header files>/create_lib/install/include/hello)

FIND_LIBRARY(HELLO_LIB hello <where you install your libraries file>/create_lib/install/lib)
TARGET_LINK_LIBRARIES(main ${HELLO_LIB})
#include <hello.h>

int main() {
    HelloFunc();
    return 0;
}

這里重點是兩個命令:

  • INCLUDE_DIRECTORIES : 如果你的頭文件不是安裝在系統(tǒng)必須搜尋的路徑副砍,需要這個命令告訴 cmake 去找到你的頭文件
  • FIND_LIBRARY : 如果你的庫文件不是安裝在系統(tǒng)必須搜尋的路徑,需要這個命令告訴 cmake 去找到你的庫文件
  • TARGET_LINK_LIBRARIES : 將找到的庫和可執(zhí)行文件 (或者其他的庫) 連接到一起

最后 cd build && cmake .. && make 應(yīng)該可以看到調(diào)用了之前寫的 HelloFunc() 函數(shù)庄岖。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末豁翎,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子隅忿,更是在濱河造成了極大的恐慌心剥,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件背桐,死亡現(xiàn)場離奇詭異优烧,居然都是意外死亡,警方通過查閱死者的電腦和手機链峭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進店門畦娄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人弊仪,你說我怎么就攤上這事熙卡。” “怎么了励饵?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵驳癌,是天一觀的道長。 經(jīng)常有香客問我役听,道長颓鲜,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任典予,我火速辦了婚禮甜滨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘瘤袖。我一直安慰自己艳吠,他們只是感情好,可當我...
    茶點故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布孽椰。 她就那樣靜靜地躺著昭娩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪黍匾。 梳的紋絲不亂的頭發(fā)上栏渺,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天,我揣著相機與錄音锐涯,去河邊找鬼磕诊。 笑死,一個胖子當著我的面吹牛纹腌,可吹牛的內(nèi)容都是我干的霎终。 我是一名探鬼主播,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼升薯,長吁一口氣:“原來是場噩夢啊……” “哼莱褒!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起涎劈,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤广凸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蛛枚,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谅海,經(jīng)...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年蹦浦,在試婚紗的時候發(fā)現(xiàn)自己被綠了扭吁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,427評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡盲镶,死狀恐怖侥袜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情徒河,我是刑警寧澤系馆,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站顽照,受9級特大地震影響由蘑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜代兵,卻給世界環(huán)境...
    茶點故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一尼酿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧植影,春花似錦裳擎、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽羡微。三九已至,卻和暖如春惶我,著一層夾襖步出監(jiān)牢的瞬間妈倔,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工绸贡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留盯蝴,地道東北人。 一個月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓听怕,卻偏偏與公主長得像捧挺,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子尿瞭,可洞房花燭夜當晚...
    茶點故事閱讀 45,440評論 2 359

推薦閱讀更多精彩內(nèi)容

  • CMake是我非常喜歡且一直使用的工具闽烙。它不但能幫助我跨平臺、跨編譯器筷厘,而且最酷的是鸣峭,它幫我節(jié)約了太多的存儲空間。...
    行之與亦安閱讀 99,966評論 5 56
  • 向您的項目添加 C 和 C++ 代碼 本文內(nèi)容 下載 NDK 和構(gòu)建工具 創(chuàng)建支持 C/C++ 的新項目 構(gòu)建和運...
    會飛的大象_閱讀 3,792評論 0 3
  • 注:首發(fā)地址 1. 前言 當在做 Android NDK 開發(fā)時酥艳,如果不熟悉用 CMake 來構(gòu)建摊溶,讀不懂 CMa...
    cfanr閱讀 24,425評論 1 53
  • CMake學(xué)習 本篇分享一下有關(guān)CMake的一些學(xué)習心得以及相關(guān)使用。 本文目錄如下: [1充石、CMake介紹] [...
    AlphaGL閱讀 12,252評論 11 79
  • 簡答的思維導(dǎo)圖的讀書記錄
    陽光半沐閱讀 258評論 0 0