用MinGW和CMake搭建便捷的C/C++開發環境(二)
七、CMake練習準備工作:在E盤新建一個文件夾cmakeproj,作為工程目錄,在cmakeproj文件夾中,建立src文件夾,用來存放源代碼;建立build文件夾,用來構建我的應用程序;在cmakeproj文件夾中,新建CMakeList.txt文件,我用工程根目錄中的CMakeList.txt文件做以下幾件事情:1、指定CMake的最低版本CMAKE_MINIMUM_REQUIRED(VERSION 2.8)2、設定項目名稱
PROJECT(CMakeProj)3、指定子目錄ADD_SUBDIRECTORY(src)這裡還應當了解兩個重要的變數${PROJECT_SOURCE_DIR}和${PROJECT_BINARY_DIR},他們分別是項目源代碼目錄和項目輸出目錄,可以用MESSAGE指令輸出。MESSAGE(STATUS "源碼目錄:" ${PROJECT_SOURCE_DIR})MESSAGE(STATUS "編譯目錄:" ${PROJECT_BINARY_DIR})
4、在src文件夾中,建立mylibs和myapps文件夾在mylibs中,建立mylib.cpp和mylib.h文件,代碼如下:
mylib.h//----------------------------------------------------------------------------------------------------#ifndef_MYLIB_H#define_MYLIB_HclassMyLib{public:voidDoSomething();};#endif//----------------------------------------------------------------------------------------------------mylib.cpp//----------------------------------------------------------------------------------------------------#include"mylib.h"#include<iostream>voidMyLib::DoSomething(){std::cout<<"hi~!我是一條來自MyLib中DoSomething的消息"<<std::endl;}//----------------------------------------------------------------------------------------------------
在mylibs文件夾中的CMakeLists.txt文件中寫CMake指令:ADD_LIBRARY(mylibs mylib)
ADD_LIBRARY指令的第一個參數為庫文件的名稱,這裡指定為mylibs,將來make之後,會生成一個名稱為libmylibs.a的庫文件,第二個參數為源碼,可以只寫文件名mylib,也可以寫mylib.h空格mylib.cpp,如果頭文件和源文件的名稱一致簡寫為mylib就可以了。5、在myapps文件夾中,建立myapp.cpp文件
myapp.cpp#include<iostream>#include"mylib.h"intmain(intargc,constchar*argv[]){MyLib.DoSomething();return0;}
在myapps文件夾中的CMakeLists.txt文件中寫CMake指令:告訴編譯器共享庫的位置:INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/src/mylibs)根據myapp.cpp源文件生稱名稱為exename的可執行文件:exename.exeADD_EXECUTABLE(exename myapp.cpp)鏈接可執行文件需要的動態庫文件libmylibs.aTARGET_LINK_LIBRARIES(exename mylibs)
6、構建在build文件夾中:cmd:cmake –G」MinGW Makefiles」 .. 在build文件夾中已經生成了我需要的Makefie,接下來cmd:mingw32-make在build文件夾的src文件夾中的myapps文件夾中,成功的生成了名稱為exename.exe的可執行文件,在build文件夾的src文件夾中的mylibs文件夾中生成了libmylib.a文件。
八、CMake練習的深入在上一步生成了exe文件後,還是有很多不如意的地方,例如exe文件生成的目錄並不是我想要的等等等等。還需要對我的構建進行改進。1、改變目標二進位的輸出目錄在工程根目錄中的CMakeList.txt文件中增加指令:SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)2、增加版本號SET(${PROJECT_NAME}_MAJOR_VERSION 0)SET(${PROJECT_NAME}_MINOR_VERSION 1)SET(${PROJECT_NAME}_PATCH_LEVEL 0)但我設置後,在windows中並沒有看到效果。去公司在linux下看看。3、生成共享庫修改src/mylibs/中的CMakeLists.txt文件ADD_LIBRARY(mylibs SHARED mylib)ADD_LIBRARY的參數形式為:ADD_LIBRARY (庫名稱 庫類型 源文件)只需要增加參數SHARED就可以了,在make之後,build/lib/中可生成mylibs.dll文件。如果想同時生成靜態庫和共享庫,如何實現?
九、使用其他庫文件建立一個目錄,用來存放一些常用的庫(e:/cpplibs)。(一)sqlite從最簡單的sqlite開始。在E盤建目錄cpplibs,在cpplibs文件夾中建立sqlite目錄,在sqlite中建立include和lib文件夾,分別存放sqlite3.h文件和libsqlite3.a文件。在myapps文件夾中的CMakeLists.txt文件中增加:INCLUDE_DIRECTORIES(d:/cpplibs/sqlite/include)LINK_DIRECTORIES(d:/cpplibs/sqlite/lib) TARGET_LINK_LIBRARIES(exename sqlite3 mylibs)告訴編譯器到哪裡去找sqlite的頭文件和庫,連接sqlite3的庫文件。運行cmake和mingw32-make,sqlite3的應用程序可以正常運行了。但是在上面到處寫類似d:/cpplibs/sqlite/include這樣的路徑,如果cpplibs名稱改變了,那就太致命了。在項目根目錄的CMakeLists.txt文件中定義一個變數:SET(SQLITE3_BASE d:/cpplibs/sqlite)將上面兩行CMake指令修改為:INCLUDE_DIRECTORIES(${SQLITE3_BASE}/include)LINK_DIRECTORIES(${SQLITE3_BASE}/lib)這樣看起來好多了。再次編譯時,只要修改一下變數SQLITE3_BASE的值就可以了。
(二)MySQL++1、定義兩個變數MYSQL_BASE和MYSQLPP_BASE,分別存放mysql和mysql++的位置,在myapps文件夾中的CMakeLists.txt文件中增加:SET(MYSQL_BASE "C:/Program Files/MySQL/MySQL Server 5.1") SET(MYSQLPP_BASE "E:/MySQL++")因為CMakeLists.txt的語法是以空格區分參數是否結束的,所以路徑加了引號,在CMake的語法中,加不加引號其實都沒有關係,但如果值中有空格或分號「;」,還是需要加引號。2、源碼目錄(這裡是myapps)的CMakeLists.txt文件修改為:
#INCLUDE_DIRECTORIES#告訴編譯器共享庫的位置:#sqlite mysql mysql++INCLUDE_DIRECTORIES("${SQLITE3_BASE}/include" "${MYSQL_BASE}/include" "${MYSQLPP_BASE}/include")
#LINK_DIRECTORIES#告訴外部依賴庫的搜索路徑#sqlite mysql mysql++LINK_DIRECTORIES("${SQLITE3_BASE}/lib" "${MYSQLPP_BASE}/lib" "${MYSQL_BASE}/lib")
最後別忘了告訴編譯器mysqlpp的依賴項TARGET_LINK_LIBRARIES(${PROJECT_NAME} "${MYSQLPP_BASE}/lib/libmysqlpp.a")
開始寫一些代碼試試看,已經可以很順利的make了。
#include<cstdlib>#include<cstdio>#include<iostream>#include<sqlite3.h>#include<mysql++.h>#include"mylib.h"usingnamespacestd;//sqlitecallbackstaticintcallback(void*NotUsed,intargc,char**argv,char**azColName){inti;for(i=0;i<argc;i++){printf("%s=%s
",azColName[i],argv[i]?argv[i]:"NULL");}printf("
");return0;}//endsqlitecallbackintmain(intargc,constchar*argv[]){//mylibMyLib().DoSomething();//sqlitesqlite3*db;char*zErrMsg=0;intrc;charsql[1024]="";sprintf(sql,"SELECT*FROMtable1;");rc=sqlite3_open("test.db",&db);if(rc){fprintf(stderr,"Can"topendatabase:%s
",sqlite3_errmsg(db));sqlite3_close(db);system("PAUSE");exit(1);}rc=sqlite3_exec(db,sql,callback,0,&zErrMsg);if(rc!=SQLITE_OK){fprintf(stderr,"SQLerror:%s
",zErrMsg);}sqlite3_close(db);//endsqlite//mysqlmysqlpp::Connectioncon(false);con.set_option(newmysqlpp::SetCharsetNameOption("gbk"));cout<<"請輸入資料庫(root用戶)連接密碼:";stringpwd;getline(cin,pwd);if(!con.connect("mytable","localhost","root",pwd.c_str())){cout<<"無法連接,請檢查密碼是否正確!"<<endl;return-1;}else{cout<<"shit.終於連上了。"<<endl;mysqlpp::Queryquery=con.query("selectmycolfromfirsttable");if(mysqlpp::StoreQueryResultres=query.store()){cout<<"Wehave:"<<endl;mysqlpp::StoreQueryResult::const_iteratorit;for(it=res.begin();it!=res.end();++it){mysqlpp::Rowrow=*it;cout<<" "<<row["mycol"]<<endl;//或者使用列索引//cout<<" "<<row[0]<<endl;}}else{cerr<<"Failedtogetmycollist:"<<query.error()<<endl;return1;}}//endmysqlsystem("PAUSE");returnEXIT_SUCCESS;}
(三)wxWidgets在myapps的CMakeLists.txt文件中:#wxWidgets#MinGW 對庫的順序是有要求的,這一點很重要FIND_PACKAGE(wxWidgets REQUIRED)INCLUDE(${wxWidgets_USE_FILE})
之後:TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${wxWidgets_LIBRARIES})
下面修改myapp.cpp的代碼,寫一個wxWidgets的hello world,wxFrame窗口已經呈現在眼前。
myapp.cpp的代碼就是wxWidgets官方網站的helloword:
/**hworld.cpp*/#include"wx/wx.h"classMyApp:publicwxApp{virtualboolOnInit();};classMyFrame:publicwxFrame{public:MyFrame(constwxString&title,constwxPoint&pos,constwxSize&size);voidOnQuit(wxCommandEvent&event);voidOnAbout(wxCommandEvent&event);DECLARE_EVENT_TABLE()};enum{ID_Quit=1,ID_About,};BEGIN_EVENT_TABLE(MyFrame,wxFrame)EVT_MENU(ID_Quit,MyFrame::OnQuit)EVT_MENU(ID_About,MyFrame::OnAbout)END_EVENT_TABLE()IMPLEMENT_APP(MyApp)boolMyApp::OnInit(){MyFrame*frame=newMyFrame(_("HelloWorld"),wxPoint(50,50),wxSize(450,340));frame->Show(true);SetTopWindow(frame);returntrue;}MyFrame::MyFrame(constwxString&title,constwxPoint&pos,constwxSize&size):wxFrame(NULL,-1,title,pos,size){wxMenu*menuFile=newwxMenu;menuFile->Append(ID_About,_("&About..."));menuFile->AppendSeparator();menuFile->Append(ID_Quit,_("E&xit"));wxMenuBar*menuBar=newwxMenuBar;menuBar->Append(menuFile,_("&File"));SetMenuBar(menuBar);CreateStatusBar();SetStatusText(_("WelcometowxWidgets!"));}voidMyFrame::OnQuit(wxCommandEvent&WXUNUSED(event)){Close(TRUE);}voidMyFrame::OnAbout(wxCommandEvent&WXUNUSED(event)){wxMessageBox(_("ThisisawxWidgetsHelloworldsample"),_("AboutHelloWorld"),wxOK|wxICON_INFORMATION,this);}
編譯,運行。
總結:
以前喜歡用vc,vs,但vs越來越龐大,每一次的安裝都有如夢魘,相信很多朋友和我一樣都有同樣的感受。為了有一個輕便的環境,我逐漸轉移到Devc++,再到CodeBlocks,但總是會遇到這樣或那樣的問題沒有辦法解決。我知道這並不是IDE的問題,而是我不知如何解決。 當然我並不是反對使用IDE,我本人也很依賴IDE。人類之所以發明工具,是因為工具可以提高生產效率。上面提到的這些IDE都是極品,尤其Visual studio,是我認為最強大的開發工具,確切的說是C#最強大的開發工具,我甚至一度認為沒有其他的開發工具可以超越它,即便如此,我還是想知道在vs強大的背後到底是什麼?現在我似乎知道了一些:-)。
學習和使用CMake,開始入手時比較困難,但我才經過總共不超過20個小時的學習,已經基本可以開始用CMake來完成大多數的日常工作了。但是還有一些迷惑,總不能不停的在命令行中敲命令吧,剛開始熟悉命令時多敲幾次還過得去,命令已經非常熟悉了,而且使用頻率也很高,不停的敲那也太麻煩了,在第三篇學習記錄中解決這個問題,地址在這裡:
http://www.cnblogs.com/ode/archive/2011/08/04/2152251.html
參考資料:1、CMake文檔2、CMake實踐.pdf3、http://bbs.osgchina.org/viewthread.php?tid=1189 http://bbs.osgchina.org/viewthread.php?tid=1229
推薦閱讀:
※如何安裝opencv_contrib及解決其安裝編譯問題
※使用 CMake 不用路徑地調用 libclang
※vs2017怎麼用內置CMAKE編譯opencv??
※macOS Sierra10.12.6下安裝OpenCV3.3.0
※解決 Windows 下 Python 安裝 Dlib 的問題:Cmake 找不到 boost