centos下cmake安裝與學習

你或許聽過好幾種 Make 工具,例如 GNU Make ,QT 的 qmake ,微軟的 MS nmake,BSD Make(pmake),Makepp,等等。這些 Make 工具遵循着不同的規範和標準,所執行的 Makefile 格式也千差萬別。這樣就帶來了一個嚴峻的問題:如果軟件想跨平臺,必須要保證能夠在不同平臺編譯。而如果使用上面的 Make 工具,就得爲每一種標準寫一次 Makefile ,這將是一件讓人抓狂的工作。

CMake就是針對上面問題所設計的工具:它首先允許開發者編寫一種平臺無關的 CMakeList.txt 文件來定製整個編譯流程,然後再根據目標用戶的平臺進一步生成所需的本地化 Makefile 和工程文件,如 Unix 的 Makefile 或 Windows 的 Visual Studio 工程。從而做到“Write once, run everywhere”。

當前centos版本

cat /etc/redhat-release
CentOS Linux release 7.6.1810 (Core)

安裝

centso安裝

1、下載cmake
官網

2、解壓

# tar -xvf cmake-3.17.3.tar.gz
# cd cmake-3.17.3/
# ./configure

在這裏插入圖片描述

# gmake
# gmake install

在這裏插入圖片描述

# cmake -version

在這裏插入圖片描述

學習

簡單樣例

首先讓我們從最簡單的代碼入手,先來體驗下cmake是如何操作的。編寫main.c,如下,

#include <stdio.h>

int main(void)
{
	printf("Hello World\n");

	return 0;
}

然後在main.c相同目錄下編寫CMakeLists.txt,內容如下,

cmake_minimum_required (VERSION 2.8)

# 設置C++標準爲 C++ 11
set(CMAKE_CXX_STANDARD 11)

project (demo)

add_executable(main main.c)

  • 第一行意思是表示cmake的最低版本要求是2.8,我們安裝的是3.10.2;
  • 第二行是表示本工程信息,也就是工程名叫demo;
  • 第三行:設置c++標準爲c++ 11
  • 第四行比較關鍵,表示最終要生成的elf文件的名字叫main,使用的源文件是main.c
    在終端下切到main.c所在的目錄下,然後輸入以下命令運行cmake,
    cmake .
    在這裏插入圖片描述
    再來看看目錄下的文件,
    在這裏插入圖片描述
    可以看到成功生成了Makefile,還有一些cmake運行時自動生成的文件。
    然後在終端下輸入make並回車,
    在這裏插入圖片描述

運行成功!

PS: 如果想重新生成main,輸入make clean就可以刪除main這個elf文件。

同一目錄下多個源文件

接下來進入稍微複雜的例子:在同一個目錄下有多個源文件。
在之前的目錄下添加2個文件,testFunc.c和testFunc.h。添加完後整體文件結構如下,
在這裏插入圖片描述
testFunc.c內容如下,

/*
** testFunc.c
*/

#include <stdio.h>
#include "testFunc.h"

void func(int data)
{
	printf("data is %d\n", data);
}

testFunc.h內容如下,

/*
** testFunc.h
*/

#ifndef _TEST_FUNC_H_
#define _TEST_FUNC_H_

void func(int data);

#endif

修改main.c,調用testFunc.h裏聲明的函數func(),

#include <stdio.h>

#include "testFunc.h"

int main(void)
{
	func(100);

	return 0;
}

修改CMakeLists.txt,在add_executable的參數裏把testFunc.c加進來

cmake_minimum_required (VERSION 2.8)

project (demo)

add_executable(main main.c testFunc.c)

然後重新執行cmake生成Makefile並運行make,
在這裏插入圖片描述
然後運行重新生成的elf文件main,
在這裏插入圖片描述
可以類推,如果在同一目錄下有多個源文件,那麼只要在add_executable裏把所有源文件都添加進去就可以了。但是如果有一百個源文件,再這樣做就有點坑了,無法體現cmake的優越性,cmake提供了一個命令可以把指定目錄下所有的源文件存儲在一個變量中,這個命令就是 aux_source_directory(dir var)。
第一個參數dir是指定目錄,第二個參數var是用於存放源文件列表的變量。

我們在main.c所在目錄下再添加2個文件,testFunc1.c和testFunc1.h。添加完後整體文件結構如下,
在這裏插入圖片描述
testFunc1.c如下,

/*
** testFunc1.c
*/

#include <stdio.h>
#include "testFunc1.h"

void func1(int data)
{
	printf("data is %d\n", data);
}

testFunc1.h如下,

/*
** testFunc1.h
*/

#ifndef _TEST_FUNC1_H_
#define _TEST_FUNC1_H_

void func1(int data);

#endif

再修改main.c,調用testFunc1.h裏聲明的函數func1(),

#include <stdio.h>

#include "testFunc.h"
#include "testFunc1.h"

int main(void)
{
	func(100);
	func1(200);

	return 0;
}

修改CMakeLists.txt,

cmake_minimum_required (VERSION 2.8)

project (demo)

aux_source_directory(. SRC_LIST)

add_executable(main ${SRC_LIST})

使用aux_source_directory把當前目錄下的源文件存列表存放到變量SRC_LIST裏,然後在add_executable裏調用SRC_LIST(注意調用變量時的寫法)。
再次執行cmake和make,並運行main,
在這裏插入圖片描述
可以看到運行成功了。

aux_source_directory()也存在弊端,它會把指定目錄下的所有源文件都加進來,可能會加入一些我們不需要的文件,此時我們可以使用set命令去新建變量來存放需要的源文件,如下,

cmake_minimum_required (VERSION 2.8)

project (demo)

set( SRC_LIST
	 ./main.c
	 ./testFunc1.c
	 ./testFunc.c)

add_executable(main ${SRC_LIST})

不同目錄下多個源文件

一般來說,當程序文件比較多時,我們會進行分類管理,把代碼根據功能放在不同的目錄下,這樣方便查找。那麼這種情況下如何編寫CMakeLists.txt呢?
我們把之前的源文件整理一下(新建2個目錄test_func和test_func1),整理好後整體文件結構如下,
在這裏插入圖片描述

一般來說,當程序文件比較多時,我們會進行分類管理,把代碼根據功能放在不同的目錄下,這樣方便查找。那麼這種情況下如何編寫CMakeLists.txt呢?
我們把之前的源文件整理一下(新建2個目錄test_func和test_func1),整理好後整體文件結構如下,

cmake_minimum_required (VERSION 2.8)

project (demo)

include_directories (test_func test_func1)

aux_source_directory (test_func SRC_LIST)
aux_source_directory (test_func1 SRC_LIST1)

add_executable (main main.c ${SRC_LIST} ${SRC_LIST1})

這裏出現了一個新的命令:include_directories。該命令是用來向工程添加多個指定頭文件的搜索路徑,路徑之間用空格分隔。
因爲main.c裏include了testFunc.h和testFunc1.h,如果沒有這個命令來指定頭文件所在位置,就會無法編譯。當然,也可以在main.c裏使用include來指定路徑【就算制定了,CMakeLists.txt還是需要改的】,如下

#include "test_func/testFunc.h"
#include "test_func1/testFunc1.h"

只是這種寫法不好看。
另外,我們使用了2次aux_source_directory,因爲源文件分佈在2個目錄下,所以添加2次

正規一點的組織結構

正規一點來說,一般會把源文件放到src目錄下,把頭文件放入到include文件下,生成的對象文件放入到build目錄下,最終輸出的elf文件會放到bin目錄下,這樣整個結構更加清晰。讓我們把前面的文件再次重新組織下,
在這裏插入圖片描述
我們在最外層目錄下新建一個CMakeLists.txt,內容如下,

cmake_minimum_required (VERSION 2.8)

project (demo)

add_subdirectory (src)

這裏出現一個新的命令add_subdirectory(),這個命令可以向當前工程添加存放源文件的子目錄,並可以指定中間二進制和目標二進制的存放位置,具體用法可以百度。
這裏指定src目錄下存放了源文件,當執行cmake時,就會進入src目錄下去找src目錄下的CMakeLists.txt,所以在src目錄下也建立一個CMakeLists.txt,內容如下,

aux_source_directory (. SRC_LIST)

include_directories (../include)

add_executable (main ${SRC_LIST})

set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

這裏又出現一個新的命令set,是用於定義變量的,EXECUTABLE_OUT_PATH和PROJECT_SOURCE_DIR是CMake自帶的預定義變量,其意義如下,

  • EXECUTABLE_OUTPUT_PATH :目標二進制可執行文件的存放位置
  • PROJECT_SOURCE_DIR:工程的根目錄

所以,這裏set的意思是把存放elf文件的位置設置爲工程根目錄下的bin目錄。(cmake有很多預定義變量,詳細的可以網上搜索一下)

添加好以上這2個CMakeLists.txt後,整體文件結構如下,
在這裏插入圖片描述
下面來運行cmake,不過這次先讓我們切到build目錄下,然後輸入以下命令,
cmake ..
Makefile會在build目錄下生成,然後在build目錄下運行make,
在這裏插入圖片描述
運行ok,我們再切到bin目錄下,發現main已經生成,並運行測試,
在這裏插入圖片描述
測試OK!

這裏解釋一下爲什麼在build目錄下運行cmake?從前面幾個case中可以看到,如果不這樣做,cmake運行時生成的附帶文件就會跟源碼文件混在一起,這樣會對程序的目錄結構造成污染,而在build目錄下運行cmake,生成的附帶文件就只會待在build目錄下,如果我們不想要這些文件了就可以直接清空build目錄,非常方便。

另外一種寫法:
前面的工程使用了2個CMakeLists.txt,這種寫法是爲了處理需要生成多個elf文件的情況,最外層的CMakeLists.txt用於掌控全局,使用add_subdirectory來添加要生成elf文件的源碼目錄。

如果只生成一個elf文件,那麼上面的例子可以只使用一個CMakeLists.txt,可以把最外層的CMakeLists.txt內容改成如下

cmake_minimum_required (VERSION 2.8)

project (demo)

aux_source_directory (src SRC_LIST)

include_directories (include)

add_executable (main ${SRC_LIST})

set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

同時,還要把src目錄下的CMakeLists.txt刪除。

動態庫和靜態庫的編譯控制

有時我們只需要編譯出動態庫,靜態庫,然後等着讓其它程序去使用。讓我們看下這種情況該如何使用cmake。首先按照如下重新組織文件,只留下testFunc.h和TestFunc.c,
在這裏插入圖片描述
我們會在build目錄下運行cmake,並把生成的庫文件存放到lib目錄下。
最外層的CMakeLists.txt內容如下,

cmake_minimum_required (VERSION 2.8)

project (demo)

add_subdirectory (lib_testFunc)

lib_testFunc目錄下的CMakeLists.txt如下,

aux_source_directory (. SRC_LIST)

add_library (testFunc_shared SHARED ${SRC_LIST})
add_library (testFunc_static STATIC ${SRC_LIST})

set_target_properties (testFunc_shared PROPERTIES OUTPUT_NAME "testFunc")
set_target_properties (testFunc_static PROPERTIES OUTPUT_NAME "testFunc")

set (LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)

這裏又出現了新的命令和預定義變量,

  • add_library: 生成動態庫或靜態庫(第1個參數指定庫的名字;第2個參數決定是動態還是靜態,如果沒有就默認靜態;第3個參數指定生成庫的源文件)
  • set_target_properties: 設置輸出的名稱,還有其它功能,如設置庫的版本號等等
  • LIBRARY_OUTPUT_PATH: 庫文件的默認輸出路徑,這裏設置爲工程目錄下的lib目錄

好了,讓我們進入build目錄下運行cmake …,成功後再運行make,
在這裏插入圖片描述

cd到lib目錄下進行查看,發現已經成功生成了動態庫和靜態庫,
在這裏插入圖片描述

ps:可以看出前面使用set_target_properties重新定義了庫的輸出名字,如果不用set_target_properties也可以,那麼庫的名字就是add_library裏定義的名字,只是我們連續2次使用add_library指定庫名字時,這個名字不能相同,而set_target_properties可以把名字設置爲相同,只是最終生成的庫文件後綴不同,這樣相對來說會好看點。


語法

add_library(<name> [STATIC | SHARED | MODULE]
            [EXCLUDE_FROM_ALL]
            [source1] [source2] [...])
  • <name>表示庫文件的名字,該庫文件會根據命令裏列出的源文件來創建。
  • [STATIC | SHARED | MODULE]庫文件的類型:
    • SHARED,動態庫
    • STATIC,靜態庫
    • MODULE,在使用 dyld 的系統有效,如果不支持 dyld,則被當作 SHARED 對待。
  • EXCLUDE_FROM_ALL:這個庫不會被默認構建,除非有其他的組件依賴或者手工構建。

對庫進行鏈接

既然我們已經生成了庫,那麼就進行鏈接測試下。把build裏的文件都刪除,然後在在工程目錄下新建src目錄和bin目錄,在src目錄下添加一個main.c和一個CMakeLists.txt,整體結構如下,
在這裏插入圖片描述
main.c內容如下,

#include <stdio.h>

#include "testFunc.h"

int main(void)
{
    func(100);
    
    return 0;
}

修改工程目錄下的CMakeLists.txt,如下,

cmake_minimum_required (VERSION 2.8)

project (demo)



add_subdirectory (src)

src目錄下的CMakeLists.txt如下,

aux_source_directory (. SRC_LIST)

# find testFunc.h
include_directories (../lib_testFunc)

link_directories (${PROJECT_SOURCE_DIR}/lib)

add_executable (main ${SRC_LIST})

target_link_libraries (main testFunc)

set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

這裏出現2個新的命令,

*link_directories: 添加非標準的共享庫搜索路徑
*target_link_libraries: 把目標文件與庫文件進行鏈接

cd到build目錄下,然後運行cmake …,成功後再運行make

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章