學計算機的,在寫程式碼的時候,IDE安裝好,環境按著教程配置好,就直接程式碼了,編輯器的具體原理只是一知半解,現在來系統學習一下,為了方便以後學習HElib!
make和cmake
寫程式大體步驟為:
1.用編輯器編寫原始碼,如.c檔案。
2.用編譯器編譯程式碼生成目標檔案,如.o。
3.用連結器連線目的碼生成可執行檔案,如.exe。
gcc
gcc是GNU Compiler Collection(就是GNU編譯器套件),也可以簡單認為是編譯器,它可以編譯很多種程式語言(括C、C++、Objective-C、Fortran、Java等等)。
gcc如何編譯參考:cmake 學習
當你的程式只有一個原始檔時,直接就可以用gcc命令編譯它。
但是當你的程式包含很多個原始檔時,用gcc命令逐個去編譯時,你就很容易混亂而且工作量大,一個一個編譯時就會特別麻煩,於是人們想到,為什麼不設計一種類似批處理的程式,來批處理編譯原始檔呢,於是就有了make工具。它是一個自動化編譯工具,你可以使用一條命令實現完全編譯。但是你需要編寫一個規則檔案,make依據它來批處理編譯,這個檔案就是makefile,所以編寫makefile檔案也是一個程式設計師所必備的技能。
對於一個大工程,編寫makefile實在是件複雜的事,於是人們又想,為什麼不設計一個工具,讀入所有原始檔之後,自動生成makefile呢,於是就出現了cmake工具,它能夠輸出各種各樣的makefile或者project檔案,從而幫助程式設計師減輕負擔。但是隨之而來也就是編寫cmakelist檔案,它是cmake所依據的規則。所以在程式設計的世界裡沒有捷徑可走,還是要腳踏實地的。
所以流程如下:
cmake使用
由上面可知,我們想要編譯,執行程式,只需要寫一個cmakelist檔案,用cmake去執行該檔案,生成project檔案和makefile檔案,然後再使用make,執行makefile檔案,進行編譯。
我這裡實驗環境實在Centos8.0下,首先先安裝,非常簡單:
yum install -y cmake
然後,接下來主要的就是cmakelist檔案的編寫,下面學習具體語法:
單個原始檔
1、準備一下原始檔 main.c .該程式的用途是計算一個數的指數冪
#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; }
2、編寫 CMakeLists.txt 檔案,並和main.c放在同一個目錄 demo下
# CMake 最低版本號要求 cmake_minimum_required (VERSION 2.8) # 專案資訊 project (demo) # 指定生成目標 add_executable(Demo main.c)
CMakeLists.txt 的語法比較簡單,由命令、註釋和空格組成,其中命令是不區分大小寫的。符號 #
後面的內容被認為是註釋。命令由命令名稱、小括號和引數組成,引數之間使用空格進行間隔。
對於上面的 CMakeLists.txt 檔案,依次出現了幾個命令:
cmake_minimum_required
:指定執行此配置檔案所需的 CMake 的最低版本;project
:引數值是 demo
,該命令表示專案的名稱是 demo 。add_executable
: 將名為 main.c 的原始檔編譯成一個名稱為 Demo 的可執行檔案。
3、編譯
在當前目錄執行 cmake .
,得到 Makefile 後再使用 make
命令編譯得到 Demo1 可執行檔案
4、執行
./Demo
多個原始檔
當在一個目錄下,有多個原始檔時,實驗目錄為demo1
1、編寫原始檔
上面的例子只有單個原始檔。現在假如把 power
函式單獨寫進一個名為 MathFunctions.c
的原始檔裡,使得這個工程變成如下的形式:
main.c
#include "MathFunctions.h" 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; }
MathFunctions.c
#include "MathFunctions.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; }
MathFunctions.h
#include <stdio.h> #include <stdlib.h> double power(double base, int exponent);
、
2、編寫CMakeLists.txt 檔案,並和原始檔放在同一個目錄 demo1下
# CMake 最低版本號要求 cmake_minimum_required (VERSION 2.8) # 專案資訊 project (demo1) # 指定生成目標 add_executable(Demo main.c MathFunctions.c)
唯一的改動只是在 add_executable
命令中增加了一個 MathFunctions.c
原始檔。這樣寫當然沒什麼問題,但是如果原始檔很多,把所有原始檔的名字都加進去將是一件煩人的工作。更省事的方法是使用 aux_source_directory
命令,該命令會查詢指定目錄下的所有原始檔,然後將結果存進指定變數名。其語法如下:
aux_source_directory(<dir> <variable>)
因此可以修改為:
# CMake 最低版本號要求 cmake_minimum_required (VERSION 2.8) # 專案資訊 project (demo1) # 查詢當前目錄下的所有原始檔 # 並將名稱儲存到 DIR_SRCS 變數 aux_source_directory(. DIR_SRCS) # 指定生成目標 add_executable(Demo ${DIR_SRCS})
這樣,CMake 會將當前目錄所有原始檔的檔名賦值給變數 DIR_SRCS
,再指示變數 DIR_SRCS
中的原始檔需要編譯成一個名稱為 Demo 的可執行檔案。
3、編譯和執行
多個目錄,多個原始檔
本實驗在demo2目錄下:
現在進一步將 MathFunctions.h 和 MathFunctions.c 檔案移動到 math 目錄下。
對於這種情況,需要分別在專案根目錄 demo2 和 math 目錄裡各編寫一個 CMakeLists.txt 檔案。為了方便,我們可以先將 math 目錄裡的檔案編譯成靜態庫再由 main 函式呼叫。
根目錄(demo2)中的 CMakeLists.txt :
# CMake 最低版本號要求 cmake_minimum_required (VERSION 2.8) # 專案資訊 project (demo2) # 查詢當前目錄下的所有原始檔 # 並將名稱儲存到 DIR_SRCS 變數 aux_source_directory(. DIR_SRCS) # 新增 math 子目錄 add_subdirectory(math) # 指定生成目標 add_executable(Demo main.c) # 新增連結庫 target_link_libraries(Demo MathFunctions)
該檔案新增了下面的內容: 第3行,使用命令 add_subdirectory
指明本專案包含一個子目錄 math,這樣 math 目錄下的 CMakeLists.txt 檔案和原始碼也會被處理 。第6行,使用命令 target_link_libraries
指明可執行檔案 main 需要連線一個名為 MathFunctions 的連結庫 。
子目錄(math)中的 CMakeLists.txt:
# 查詢當前目錄下的所有原始檔 # 並將名稱儲存到 DIR_LIB_SRCS 變數 aux_source_directory(. DIR_LIB_SRCS) # 生成連結庫 add_library (MathFunctions ${DIR_LIB_SRCS})
在該檔案中使用命令 add_library
將 src 目錄中的原始檔編譯為靜態連結庫。
編譯執行:
自定義編譯
本次實驗在demo3下:
CMake 允許為專案增加編譯選項,從而可以根據使用者的環境和需求選擇最合適的編譯方案。
例如,可以將 MathFunctions 庫設為一個可選的庫,如果 某選項 為 ON
,就使用該庫定義的數學函式來進行運算。否則就呼叫標準庫中的數學函式庫。
只需修改CMakeLists.txt 檔案:
1、主目錄下CMakeLists.txt 檔案:
# CMake 最低版本號要求 cmake_minimum_required (VERSION 2.8) # 專案資訊 project (demo3) # 加入一個配置標頭檔案,用於處理 CMake 對原始碼的設定 configure_file ( "${PROJECT_SOURCE_DIR}/config.h.in" "${PROJECT_BINARY_DIR}/config.h" ) # 是否使用自己的 MathFunctions 庫 option (USE_MYMATH "Use provided math implementation" ON) # 是否加入 MathFunctions 庫 if (USE_MYMATH) include_directories ("${PROJECT_SOURCE_DIR}/math") add_subdirectory (math) set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions) endif (USE_MYMATH) # 查詢當前目錄下的所有原始檔 # 並將名稱儲存到 DIR_SRCS 變數 aux_source_directory(. DIR_SRCS) # 指定生成目標 add_executable(Demo ${DIR_SRCS}) target_link_libraries (Demo ${EXTRA_LIBS})
其中:
- 第7行的
configure_file
命令用於加入一個配置標頭檔案 config.h ,這個檔案由 CMake 從 config.h.in 生成,通過這樣的機制,將可以通過預定義一些引數和變數來控制程式碼的生成。 - 第13行的
option
命令新增了一個USE_MYMATH
選項,並且預設值為ON
。 - 第17行根據
USE_MYMATH
變數的值來決定是否使用我們自己編寫的 MathFunctions 庫。
2、子目錄下CMakeLists.txt 檔案:
# 查詢當前目錄下的所有原始檔 # 並將名稱儲存到 DIR_LIB_SRCS 變數 aux_source_directory(. DIR_LIB_SRCS) # 生成連結庫 add_library (MathFunctions ${DIR_LIB_SRCS})
3、修改main.c
讓其根據 USE_MYMATH
的預定義值來決定是否呼叫標準庫還是 MathFunctions 庫
#include <stdio.h> #include <stdlib.h> #include "config.h" #ifdef USE_MYMATH #include "math/MathFunctions.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 USE_MYMATH printf("Now we use our own Math library. \n"); double result = power(base, exponent); #else printf("Now we use the standard library. \n"); double result = pow(base, exponent); #endif printf("%g ^ %d is %g\n", base, exponent, result); return 0; }
4、編寫config.h.in
上面的程式值得注意的是第2行,這裡引用了一個 config.h 檔案,這個檔案預定義了 USE_MYMATH
的值。但我們並不直接編寫這個檔案,為了方便從 CMakeLists.txt 中匯入配置,我們編寫一個 config.h.in 檔案,內容如下:
#cmakedefine USE_MYMATH
這樣 CMake 會自動根據 CMakeLists 配置檔案中的設定自動生成 config.h 檔案。
打包
配置生成各種平臺上的安裝包,包括二進位制安裝包和原始碼安裝包。為了完成這個任務,我們需要用到 CPack ,它同樣也是由 CMake 提供的一個工具,專門用於打包。
1、首先在頂層的 CMakeLists.txt 檔案尾部新增下面幾行:
# 構建一個 CPack 安裝包 include (InstallRequiredSystemLibraries) set (CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt") set (CPACK_PACKAGE_VERSION_MAJOR "${Demo_VERSION_MAJOR}") set (CPACK_PACKAGE_VERSION_MINOR "${Demo_VERSION_MINOR}") include (CPack)
上面的程式碼做了以下幾個工作:
- 匯入 InstallRequiredSystemLibraries 模組,以便之後匯入 CPack 模組;
- 設定一些 CPack 相關變數,包括版權資訊和版本資訊,其中版本資訊用了上一節定義的版本號;
- 匯入 CPack 模組。
2、接下來的工作是像往常一樣構建工程,並執行 cpack
命令。
- 生成二進位制安裝包:
cpack -C CPackConfig.cmake
這 3 個二進位制包檔案所包含的內容是完全相同的。我們可以執行其中一個。此時會出現一個由 CPack 自動生成的互動式安裝介面:
- 生成原始碼安裝包
cpack -C CPackSourceConfig.cmake
以上學習內容源自:CMake 入門實戰
感謝大佬分享!