前言
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)
- source_dir
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