CMakeLists 入門

前言
makefile文件的編寫實在是個繁瑣的事,于是,CMake出現(xiàn)了,使得這一切變得簡單,CMake通過CMakeLists.txt讀入所有源文件自動生成makefile,進而將源文件編譯成可執(zhí)行文件或庫文件

一、CMake常用的命令

# 設(shè)置cmake最低版本
cmake_minimum_required(VERSION 3.2)

# project命令用于指定cmake工程的名稱,實際上,它還可以指定cmake工程的版本號(VERSION關(guān)鍵字)、簡短的描述(DESCRIPTION關(guān)鍵字)、主頁URL(HOMEPAGE_URL關(guān)鍵字)和編譯工程使用的語言(LANGUAGES關(guān)鍵字)
# project(<PROJECT-NAME> [<language-name>...])
# project(<PROJECT-NAME> [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]] [DESCRIPTION <project-description-string>][HOMEPAGE_URL <url-string>] [LANGUAGES <language-name>...])
# ${PROJECT_SOURCE_DIR} 和 <PROJECT-NAME>_SOURCE_DIR:本CMakeLists.txt所在的文件夾路徑
# ${PROJECT_NAME}:本CMakeLists.txt的project名稱
project(xxx)
project(mytest VERSION 1.2.3.4)
project (mytest HOMEPAGE_URL “https://www.XXX(示例).com”)

# 獲取路徑下所有的.cpp/.c/.cc文件(不包括子目錄),并賦值給變量中
aux_source_directory(路徑 變量)

# GLOB_RECURSE 獲取目錄下的所有cpp文件(不包括子目錄),并賦值給SOURCES
file(
        GLOB SOURCES
        ${PROJECT_SOURCE_DIR}/*.c

)
# GLOB_RECURSE 獲取目錄下的所有cpp文件(包括子目錄),并賦值給NATIVE_SRC
file(
      GLOB_RECURSE NATIVE_SRC 
      ${PROJECT_SOURCE_DIR}/lib/*.cpp
)

# 給文件名/路徑名或其他字符串起別名,用${變量}獲取變量內(nèi)容
set(變量 文件名/路徑/...)

# 添加編譯選項FOO BAR
# add_definitions定義宏,但是這種定義方式無法給宏具體值 等價C語言中的#define  MG_ENABLE_OPENSSL
add_definitions(-DFOO -DBAR ...)

# add_compile_definitions定義宏,這種方式可以給宏具體值,但是這個指令只要高版本的cmake支持 等價C語言中 #define  MG_ENABLE_OPENSSL   1 
add_compile_definitions(MG_ENABLE_OPENSSL=1)

# 打印消息
message(消息)

# 編譯子文件夾的CMakeLists.txt
add_subdirectory(子文件夾名稱)

# 將.cpp/.c/.cc文件生成.a靜態(tài)庫
# 注意,庫文件名稱通常為libxxx.so,在這里只要寫xxx即可
add_library(庫文件名稱 STATIC 文件)

# 將.cpp/.c/.cc文件生成可執(zhí)行文件
add_executable(可執(zhí)行文件名稱 文件)

# 規(guī)定.h頭文件路徑
include_directories(路徑)

# 規(guī)定.so/.a庫文件路徑
link_directories(路徑)

# 設(shè)置編譯選項及默認值
option(TEST_DEBUG "option for debug" OFF)

# 對add_library或add_executable生成的文件進行鏈接操作
# 注意,庫文件名稱通常為libxxx.so,在這里只要寫xxx即可
target_link_libraries(庫文件名稱/可執(zhí)行文件名稱 鏈接的庫文件名稱)

通常一個CMakeLists.txt需按照下面的流程:

project(xxx)                                          #必須

add_subdirectory(子文件夾名稱)                         #父目錄必須,子目錄不必

add_library(庫文件名稱 STATIC 文件)                    #通常子目錄(二選一)
add_executable(可執(zhí)行文件名稱 文件)                     #通常父目錄(二選一)

include_directories(路徑)                              #必須
link_directories(路徑)                                 #必須

target_link_libraries(庫文件名稱/可執(zhí)行文件名稱 鏈接的庫文件名稱)       #必須

二、CMakeLists實例

示例1:只有一個源文件main.c

目錄結(jié)構(gòu)如下:

+
| 
+--- main.c
+--- CMakeLists.txt
|

代碼如下:

// main.c
#include <stdio.h>
int main()
{
printf("hello world");
return 0;
}
# CMakeLists
cmake_minimum_required(VERSION 3.0)
project(HELLO VERSION 1.0 LANGUAGES C CXX)
set(SOURCES main.c)
add_executable(hello ${SOURCES})

??警告
project設(shè)置VERSION,要求cmake的最低版本3.0

注意:
為了簡單起見,我們從一開始就采用cmake的 out-of-source 方式來構(gòu)建(即生成中間產(chǎn)物與源代碼分離),并始終堅持這種方法,這也就是此處為什么單獨創(chuàng)建一個目錄,然后在該目錄下執(zhí)行 cmake 的原因

在CMakeLists.txt目錄下執(zhí)行以下命令

mkdir build
cd build
cmake ..
make

即可生成可執(zhí)行程序 hello(.exe)
目錄結(jié)構(gòu)如下

+
| 
+--- main.c
+--- CMakeLists.txt
|
/--+ build/
   |
   +--- hello(exec)
  • project 會引入兩個變量HELLO_BINARY_DIR 和 HELLO_SOURCE_DIR,這兩個變量和PROJECT_BINARY_DIR 和 PROJECT_SOURCE_DIR等價
  • message(${PROJECT_SOURCE_DIR}) 打印變量的值
  • set 命令用來設(shè)置變量
  • add_exectuable 告訴工程生成一個可執(zhí)行文件。
  • add_library 則告訴生成一個庫文件
示例2:拆成3個文件 hello.h hello.c main.c

目錄結(jié)構(gòu)如下:

+
| 
+--- main.c
+--- hello.h
+--- hello.c
+--- CMakeLists.txt
|

代碼如下:

// main.c
#include "hello.h"
int main()
{
    hello("World");
    return 0;
}
// hello.h
#ifndef DBZHANG_HELLO_
#define DBZHANG_HELLO_
void hello(const char* name);
#endif //DBZHANG_HELLO_
// hello.c
#include <stdio.h>
#include "hello.h"

void hello(const char * name)
{
    printf ("Hello %s! \n", name);
}
# CMakeLists
cmake_minimum_required(VERSION 3.0)
project(HELLO VERSION 1.0 LANGUAGES C CXX)
set(SOURCES hello.c main.c)
add_executable(hello ${SOURCES})

執(zhí)行cmake的過程同上,目錄結(jié)構(gòu)

+
| 
+--- main.c
+--- hello.h
+--- hello.c
+--- CMakeLists.txt
|
/--+ build/
   |
   +--- hello(exec)
示例3:在示例2的基礎(chǔ)上,先將 hello.c 生成一個庫hellolib,再給main.c使用

我們只需修改下CMakeLists即可

# CMakeLists
cmake_minimum_required(VERSION 3.0)
project(HELLO VERSION 1.0 LANGUAGES C CXX)
set(LIB_SRC hello.c)
add_library(libhello ${LIB_SRC})
set(APP_SRC main.c)
add_executable(hello ${APP_SRC})
target_link_libraries(hello libhello)

執(zhí)行cmake的過程同上,目錄結(jié)構(gòu)如下

+
| 
+--- main.c
+--- hello.h
+--- hello.c
+--- CMakeLists.txt
|
/--+ build/
   |
   +--- liblibhello.a
   +--- hello(exec)
  • target_link_libraries 該指令的作用為將目標文件與庫文件進行鏈接。該指令的語法如下:
    target_link_libraries(<target> [item1] [item2] [...]
    [[debug|optimized|general] <item>] ...)
    上述指令中的<target>是指通過add_executable()和add_library()指令生成已經(jīng)創(chuàng)建的目標文件。而[item]表示庫文件沒有后綴的名字。默認情況下,庫依賴項是傳遞的。當(dāng)這個目標鏈接到另一個目標時,鏈接到這個目標的庫也會出現(xiàn)在另一個目標的連接線上
    target_link_libraries里庫文件的順序符合gcc鏈接順序的規(guī)則,即被依賴的庫放在依賴它的庫的后面,比如
    target_link_libraries(hello A B.a C.so)
    在上面的命令中,libA.so可能依賴于libB.a和libC.so,如果順序有錯,鏈接時會報錯。還有一點,B.a會告訴CMake優(yōu)先使用靜態(tài)鏈接庫libB.a,C.so會告訴CMake優(yōu)先使用動態(tài)鏈接庫libC.so,也可直接使用庫文件的相對路徑或絕對路徑。使用絕對路徑的好處 于,當(dāng)依賴的庫被更新時,make的時候也會重新鏈接

  • set_target_properties(...)是一個便捷功能,因為它允許設(shè)置多個目標的多個屬性

set_target_properties(libhello PROPERTIES OUTPUT_NAME "hello")

重命名libhello為hello

set_target_properties(Thirdlib PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/jniLibs/libThirdlib.so)

cmakelist 添加依賴庫

set_target_properties(test PROPERTIES LINKER_LANGUAGE CXX) // 指定C++
set_target_properties(test PROPERTIES LINKER_LANGUAGE C) // 指定C

該目標屬性用于指定編譯器的語言。即當(dāng)調(diào)用可執(zhí)行程序、共享庫和模塊時,用于指定編譯器鏈接語言(C or CXX),若是沒有設(shè)置,則默認具有最高鏈接器首選項值的語言

示例4:將源文件放置到不同的目錄

在示例2的基礎(chǔ)上,我們修改下目錄結(jié)構(gòu)
目錄結(jié)構(gòu)如下:

+
|
+--- CMakeLists.txt
/--+ src/
|  |
|  +--- main.c
|  +--- CMakeLists.txt
|
/--+ libhello/
|  |
|  +--- hello.h
|  +--- hello.c
|  +--- CMakeLists.txt

頂層的CMakeLists.txt

# CMakeLists
cmake_minimum_required(VERSION 3.0)
project(HELLO VERSION 1.0 LANGUAGES C CXX)
add_subdirectory(src)
add_subdirectory(libhello)

src的CMakeLists.txt

# src CMakeLists
include_directories(${PROJECT_SOURCE_DIR}/libhello)
set(APP_SRC main.c)
add_executable(hello ${APP_SRC})
target_link_libraries(hello libhello)

libhello的CMakeLists.txt

#libhello CMakeLists
set(LIB_SRC hello.c)
add_library(libhello ${LIB_SRC})

執(zhí)行cmake的過程同上,目錄結(jié)構(gòu)如下

+
|
+--- CMakeLists.txt
/--+ src/
   |
   +--- main.c
   +--- CMakeLists.txt

/--+ libhello/
   |
   +--- hello.h
   +--- hello.c
   +--- CMakeLists.txt

/--+ build/
   |
   / --+ src/
      | 
      +--- hello(exec)

   / --+ libhello/ 
      |
      +--- liblibhello.a

  • add_subdirectory (source_dir [binary_dir] [EXCLUDE_FROM_ALL])
    添加一個子目錄并構(gòu)建該子目錄

    • source_dir
      必選參數(shù)。該參數(shù)指定一個子目錄,子目錄下應(yīng)該包含CMakeLists.txt文件和代碼文件。子目錄可以是相對路徑也可以是絕對路徑,如果是相對路徑,則是相對當(dāng)前目錄的一個相對路徑。
    • binary_dir
      可選參數(shù)。該參數(shù)指定一個目錄,用于存放輸出文件??梢允窍鄬β窂揭部梢允墙^對路徑,如果是相對路徑,則是相對當(dāng)前輸出目錄的一個相對路徑。如果該參數(shù)沒有指定,則默認的輸出目錄使用source_dir。
    • EXCLUDE_FROM_ALL
      可選參數(shù)。當(dāng)指定了該參數(shù),則子目錄下的目標不會被父目錄下的目標文件包含進去,父目錄的CMakeLists.txt不會構(gòu)建子目錄的目標文件,必須在子目錄下顯式去構(gòu)建。例外情況:當(dāng)父目錄的目標依賴于子目錄的目標,則子目錄的目標仍然會被構(gòu)建出來以滿足依賴關(guān)系(例如使用了target_link_libraries)
  • include_directories ([AFTER|BEFORE] [SYSTEM] dir1 [dir2 ...])
    將指定目錄添加到編譯器的頭文件搜索路徑之下,指定的目錄被解釋成當(dāng)前源碼路徑的相對路徑

示例5:在示例4的基礎(chǔ)上,將可執(zhí)行文件和lib都放到對應(yīng)的bin目錄下

方法一:修改頂層CMakeLists.txt中的add_subdirectory方法

# 頂層CMakeLists
cmake_minimum_required(VERSION 3.0)
project(HELLO VERSION 1.0 LANGUAGES C CXX)
add_subdirectory(src ./bin)
add_subdirectory(libhello ./lib)

生成的可執(zhí)行文件在build/bin中,生成的lib文件在build/lib中

方法二:修改其他兩個文件CMakeLists.txt

# src CMakeLists
include_directories(${PROJECT_SOURCE_DIR}/libhello)
set(APP_SRC main.c)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
add_executable(hello ${APP_SRC})
target_link_libraries(hello libhello)
# libhello CMakeLists
set(LIB_SRC hello.c)
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
add_library(libhello ${LIB_SRC})
  • set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) 設(shè)置可執(zhí)行文件輸出路徑
  • set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib) 設(shè)置lib庫輸出路徑
示例6:在示例4的基礎(chǔ)上,編譯動態(tài)庫
add_library(libhello SHARED ${LIB_SRC})
# 頂層 CMakeLists
cmake_minimum_required(VERSION 3.0)
project(HELLO VERSION 1.0 LANGUAGES C CXX)
option(TEST_DEBUG "option for debug" OFF)
if (TEST_DEBUG)
add_definitions(-DTEST_DEBUG)
endif()
add_definitions(-DBUILD_SHARED)
add_subdirectory(src)
add_subdirectory(libhello)
# src CMakeLists
include_directories(${PROJECT_SOURCE_DIR}/libhello)
set(APP_SRC main.c)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
add_executable(hello ${APP_SRC})
target_link_libraries(hello libhello)
# libhello CMakeLists
set(LIB_SRC hello.c)
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)

if(BUILD_SHARED)
  add_library(libhello SHARED ${LIB_SRC})
else()
  add_library(libhello STATIC ${LIB_SRC})
endif()

我們在main.c中使用CMakeLists中定義的宏TEST_DEBUG

// main.c
#include "hello.h"
#include <stdio.h>
int main()
{
    hello("World");
#ifdef TEST_DEBUG
    printf ("DEBUG \n");
#endif
    return 0;
}

執(zhí)行cmake

cmake .. -DBUILD_SHARED = 1 #生成動態(tài)庫
cmake .. -DTEST_DEBUG=ON #main.c中的“DEBUG“會打印

參考文章:
https://blog.csdn.net/qq_38410730/article/details/102477162

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 什么是 CMake CMake是個一個開源的跨平臺自動化建構(gòu)系統(tǒng),用來管理軟件建置的程序,并不相依于某特定編譯器。...
    蘇州丸子閱讀 22,867評論 6 74
  • 最近在負責(zé)一個大型工程的CMake編譯系統(tǒng)管理,整理一些工作過程中積累下來的知識片段和技巧。CMake是一個跨平臺...
    啊呀喲嘿閱讀 9,272評論 0 2
  • CMake 入門實戰(zhàn) 從實例入手,講解 CMake 的常見用法。 什么是 CMake All problems i...
    Nothing_655f閱讀 674評論 0 0
  • JNI入門 [TOC] 說明 關(guān)于jni api的使用,引用的深入學(xué)習(xí),異常與使用優(yōu)化,請參見我的另一篇文章:JN...
    云佾風(fēng)徽閱讀 1,591評論 0 1
  • 1.cmake_minimum_required(VERSION 3.4.1) 指定cmake最低指示版本2.添...
    yuLiangC閱讀 717評論 0 0

友情鏈接更多精彩內(nèi)容