公司引擎是用cmake根據(jù)目標(biāo)平臺(tái)來(lái)構(gòu)建工程的,剛接觸的時(shí)候深深體會(huì)到cmake的方便:如果目標(biāo)平臺(tái)是windows,它可以幫你自動(dòng)構(gòu)建出vs工程;如果是安卓,自動(dòng)構(gòu)建出eclipse工程,如果是IOS,自動(dòng)構(gòu)建出xcode工程。想想以前用vs建工程的時(shí)候,如果要引入第三方庫(kù),必須要手動(dòng)配置第三方庫(kù)路徑,如果引入的庫(kù)少,那還沒(méi)什么,如果多的話(huà)就悲劇了,配個(gè)環(huán)境都要半天。再想想以前在linux平臺(tái)下手動(dòng)寫(xiě)Makefile的時(shí)候,如果工程比較小,模塊少還好辦,如果工程大,模塊多,各種寫(xiě)依賴(lài)關(guān)系都要讓你抓狂。有了cmake這個(gè)工具,我們完全可以靠它來(lái)幫我們構(gòu)建vs工程,寫(xiě)Makefile文件。既然cmake構(gòu)建工程這么方便,當(dāng)然需要拿來(lái)用,可是對(duì)于我這個(gè)小白來(lái)說(shuō),怎么學(xué)呢?果斷谷歌,百度,不過(guò)并沒(méi)有找到比較有價(jià)值的學(xué)習(xí)資料,很多人都是貼出了cmake的源文件CMakeLists.txt,然后對(duì)文件中的每行都講了下作用。看完這些,我依然不知道為什么要這么寫(xiě),為什么這行要這樣寫(xiě),那行要那樣寫(xiě)?后來(lái)才反應(yīng)過(guò)來(lái),cmake官網(wǎng)肯定有講啊,雖然是英文的,雖然自己英文比較挫,沒(méi)辦法,誰(shuí)叫沒(méi)有其它資料呢(其實(shí)官網(wǎng)講的才說(shuō)最權(quán)威的,不要因?yàn)槭怯⑽木臀窇郑炊嗔似鋵?shí)英文也沒(méi)那么難,很多人自認(rèn)為英文不行或者看英文吃力就去網(wǎng)上找各種中文資料,結(jié)果可能花費(fèi)了大量時(shí)間在找資料上,到最后啥都沒(méi)學(xué)到)。本文主要通過(guò)講解cmake中一些比較簡(jiǎn)單的命令來(lái)構(gòu)建自己的工程,作為初學(xué)者的入門(mén)。
1、兩行命令幫你構(gòu)建輸出hello world的vs工程
為了自動(dòng)構(gòu)建工程,需要在源文件所在的最上層目錄寫(xiě)一個(gè)CMakeLists.txt文件,它是cmake的源文件,也可以看作是cmake的腳本文件,這個(gè)文件描述了cmake怎樣幫我們自動(dòng)構(gòu)建工程。現(xiàn)在我們有一個(gè)hello.cpp文件,需要用這個(gè)文件來(lái)構(gòu)建一個(gè)vs工程,手動(dòng)的方法就是打開(kāi)vs,新建一個(gè)工程hello,然后把hello.cpp添加到hello工程里面。而有了cmake,只需要在CMakeLists.txt寫(xiě)兩行命令,第一行給自己工程命個(gè)名hello,第二行hello工程需要的源文件hello.cpp。然后通過(guò)下面幾個(gè)步驟,就可以生成一個(gè)vs工程了,生成其它工程的步驟相同,只是在選擇目標(biāo)工程的時(shí)候不同。
1.1 編寫(xiě)CMakeLists.txt文件和hello.cpp文件
CMakeLists.txt
PRoject(hello)add_executable(hello hello.cpp)hello.cpp
#include <stdio.h>int main (int argc, char *argv[]){ printf("hello world!"); return 0;}
1.2 設(shè)置路徑
1.3 設(shè)置目標(biāo)工程為vs工程
1.4 產(chǎn)生vs工程
1.5 打開(kāi)vs工程,編譯運(yùn)行程序
2 添加子模塊
對(duì)于比較大的工程來(lái)說(shuō),包含多個(gè)子模塊是很常見(jiàn)的事,因?yàn)橥ǔC總€(gè)人只是負(fù)責(zé)他自己的模塊。那么怎樣將各個(gè)模塊加入到主工程中呢?首先我們需要使用cmake來(lái)創(chuàng)建各個(gè)子模塊的工程,然后再將這些模塊加入到整個(gè)工程中。假設(shè)現(xiàn)在我們有一個(gè)子模塊myhello,它提供了一個(gè)函數(shù)PrintHelloWorld來(lái)打印hello world!,主模塊hello調(diào)用這個(gè)函數(shù)來(lái)打印。首先我們?cè)趆ello.cpp所在目錄創(chuàng)建myhello文件夾,將myhello.cpp和myhello.h放到里面,然后在這個(gè)文件夾中創(chuàng)建CMakeLists.txt。這三個(gè)文件的具體內(nèi)容如下:
myhello/myhello.h:
void PrintHelloWorld();myhello/myhello.cpp
#include <stdio.h>void PrintHelloWorld(){ printf("hello world!");}myhello/CMakeLists.txt
add_library(myhello myhello.cpp)這個(gè)CMakeLists.txt主要是告訴cmake,為myhello創(chuàng)建一個(gè)庫(kù)工程。
hello.cpp
#include "myhello/myhello.h"int main (int argc, char *argv[]){ PrintHelloWorld(); return 0;}CMakeLists.txt
cmake_minimum_required(VERSION 3.5)project(hello)add_subdirectory(myhello)set (EXTRA_LIBS ${EXTRA_LIBS} myhello)add_executable(hello hello.cpp)target_link_libraries (hello ${EXTRA_LIBS})add_subdirectory將myhello子工程加入到主工程,target_link_libraries將子模myhello鏈接到hello中。然后重新cmake下,打開(kāi)vs就可以編譯運(yùn)行啦。
3 添加可配置的頭文件
cmake可以通過(guò)可配置的頭文件來(lái)產(chǎn)生實(shí)際的頭文件,如下面的可配置頭文件hello.h.in,里面@@引用的變量可以通過(guò)CMakeLists.txt來(lái)設(shè)置,最后通過(guò)cmake來(lái)替換hello.h.in文件中的變量并生成hello.h內(nèi)容。
hello.h.in
#define VERSION_MAJOR @VERSION_MAJOR@#define VERSION_MINOR @VERSION_MINOR@CMakeLists.txt
cmake_minimum_required(VERSION 3.5)project(hello)include_directories("${PROJECT_BINARY_DIR}")set(VERSION_MAJOR 1)set(VERSION_MINOR 0)configure_file( "${PROJECT_SOURCE_DIR}/hello.h.in" "${PROJECT_BINARY_DIR}/hello.h")add_subdirectory(myhello)set (EXTRA_LIBS ${EXTRA_LIBS} myhello)add_executable(hello hello.cpp)target_link_libraries (hello ${EXTRA_LIBS})上面加紅的命令主要用來(lái)設(shè)置hello.h.in中的兩個(gè)變量,并且讓cmake生成hello.h文件。生成的hello.h如下:
hello.h
#define VERSION_MAJOR 1#define VERSION_MINOR 0再修改下hello.cpp文件使用這兩個(gè)變量,
hello.cpp
#include "myhello/myhello.h"#include "hello.h"#include <stdio.h>int main (int argc, char *argv[]){ printf("version:%d.%d/n", VERSION_MAJOR, VERSION_MINOR); PrintHelloWorld(); return 0;}打開(kāi)vs工程,編譯運(yùn)行輸出者兩個(gè)變量的值。這樣就可以通過(guò)在CMakeLists.txt中設(shè)置變量的內(nèi)容來(lái)動(dòng)態(tài)修改.h文件,增加了代碼的靈活性。
4 檢測(cè)系統(tǒng)是否有支持工程需要的函數(shù)
對(duì)于跨平臺(tái)的工程來(lái)說(shuō),檢查系統(tǒng)是否支持某些特性是很有必要的,這樣程序中就可以通過(guò)系統(tǒng)的特性來(lái)選擇具體執(zhí)行哪些代碼。其中檢查是否支持某些函數(shù)是我們經(jīng)常要做的事情,如epoll函數(shù),可能有的linux系統(tǒng)就不支持,對(duì)于不支持的系統(tǒng)我們只能用poll來(lái)替代等。在cmake中檢查系統(tǒng)是否支持某個(gè)函數(shù)也很簡(jiǎn)單,先包含一個(gè)CheckFunctionExists庫(kù),然后使用check_function_exists來(lái)判斷就行了。
CMakeLists.txt
cmake_minimum_required(VERSION 3.5)project(hello)include (CheckFunctionExists)check_function_exists (printf HAVE_PRINTF)include_directories("${PROJECT_BINARY_DIR}")set(VERSION_MAJOR 1)set(VERSION_MINOR 0)configure_file( "${PROJECT_SOURCE_DIR}/hello.h.in" "${PROJECT_BINARY_DIR}/hello.h")add_subdirectory(myhello)set (EXTRA_LIBS ${EXTRA_LIBS} myhello)add_executable(hello hello.cpp)target_link_libraries (hello ${EXTRA_LIBS})在配置的頭文件hello.h.in中加入#cmakedefine HAVE_PRINTF,這樣如果系統(tǒng)中有printf函數(shù),最終生成的hello.h中會(huì)定義HAVE_PRINTF這個(gè)宏,否則不會(huì)生成這個(gè)宏,在hello.cpp文件中可以根據(jù)這個(gè)宏來(lái)是否定義來(lái)判斷是否應(yīng)該使用printf函數(shù)。
hello.h.in
#define VERSION_MAJOR @VERSION_MAJOR@#define VERSION_MINOR @VERSION_MINOR@#cmakedefine HAVE_PRINTFhello.cpp
#include "myhello/myhello.h"#include "hello.h"#include <stdio.h>int main (int argc, char *argv[]){#ifdef HAVE_PRINTF printf("version:%d.%d/n", VERSION_MAJOR, VERSION_MINOR);#endif PrintHelloWorld(); return 0;}運(yùn)行結(jié)果:
5 配置可選項(xiàng)
有時(shí)候代碼可能包含了所有平臺(tái)的模塊代碼,但是對(duì)于特定的目標(biāo)平臺(tái),只需要配置該平臺(tái)需要模塊的代碼,而不需要配置其它平臺(tái)模塊的代碼。這種需求可以通過(guò)cmake的配置可選項(xiàng)來(lái)完成,配置可選項(xiàng)就是cmake在生成工程的時(shí)候提示你一些選項(xiàng),根據(jù)你的選項(xiàng)來(lái)具體選擇需要添加到工程中的模塊代碼。例如我現(xiàn)在需要提高是否使用myhello模塊的選項(xiàng),可以在CMakeLists.txt中加option命令來(lái)實(shí)現(xiàn),代碼如下:
cmake_minimum_required(VERSION 3.5)project(hello)include (CheckFunctionExists)check_function_exists (printf HAVE_PRINTF)include_directories("${PROJECT_BINARY_DIR}")set(VERSION_MAJOR 1)set(VERSION_MINOR 0)option (USE_MYHELLO "Use myhello" ON) configure_file( "${PROJECT_SOURCE_DIR}/hello.h.in" "${PROJECT_BINARY_DIR}/hello.h")add_subdirectory(myhello)set (EXTRA_LIBS ${EXTRA_LIBS} myhello)add_executable(hello hello.cpp)target_link_libraries (hello ${EXTRA_LIBS})并且在hello.h.in中添加由cmake根據(jù)選項(xiàng)來(lái)定義USE_MYHELLO宏。
#define VERSION_MAJOR @VERSION_MAJOR@#define VERSION_MINOR @VERSION_MINOR@#cmakedefine HAVE_PRINTF#cmakedefine USE_MYHELLO這樣在運(yùn)行cmake的時(shí)候,會(huì)提示我們一些選項(xiàng)來(lái)進(jìn)行選擇:
通過(guò)USE_MYHELLO是否被選擇,cmake來(lái)確定是否要在hello.h中定義USE_MYHELLO宏,最終我們可以在hello.cpp中判斷USE_MYHELLO宏是否定義來(lái)是否使用myhello模塊中的PrintHelloWorld函數(shù)。
hello.cpp
#include "myhello/myhello.h"#include "hello.h"#include <stdio.h>int main (int argc, char *argv[]){#ifdef HAVE_PRINTF printf("version:%d.%d/n", VERSION_MAJOR, VERSION_MINOR);#endif#ifdef USE_MYHELLO PrintHelloWorld();#else printf("xx hello world!");#endif return 0;}最后通過(guò)選中或者不選中USE_MYHELLO選擇,得到的結(jié)果會(huì)不同。
選中結(jié)果
沒(méi)選中結(jié)果:
6 總結(jié)
本文主要介紹了下cmake的比較常用的一些命令:project、include、include_directories、set、option、configure_file、add_subdirectory、add_executable、target_link_libraries、add_library,算是一個(gè)入門(mén)吧。需要用好cmake,熟悉cmake的命令和多寫(xiě)cmake腳本是必須的,具體每個(gè)命令的介紹看以參考官方文檔:https://cmake.org/cmake/help/v3.5/manual/cmake-commands.7.html,腳步的編寫(xiě)語(yǔ)法可以參考官網(wǎng)文檔:https://cmake.org/cmake/help/v3.5/manual/cmake-language.7.html。以后大點(diǎn)的工程創(chuàng)建完全可以交給cmake來(lái)完成,同時(shí)也是熟悉cmake的過(guò)程。
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注