CMake是一個跨平臺的編譯工具,可以一次編寫,在不同平臺自動生成對應的Makefile文件,減少了手寫Makefile以及適配不同平臺時的耗時。
前言
之前大部分時候在windows端使用VS開發,因此對Makefile、CMake等工具接觸較少。最近嘗試從頭實現一個簡單的HTTP服務器,主要開發環境在Linux,因此借此契機熟悉一下CMake等構建工具的使用。
目錄結構
目前項目文件較少,使用了較簡單的目錄結構
┣━ src
┃ ┣━ CMakeLists.txt
┃ ┣━ HttpRequest.cpp
┃ ┣━ HttpResponse.cpp
┃ ┣━ HttpServer.cpp
┃ ...
┣━ include
┃ ┣━ HttpRequest.h
┃ ┣━ HttpResponse.h
┃ ┣━ HttpServer.h
┃ ...
┣━ cmake-build-debug
┃ ┣━...
┃ ┗━...
┣━ main.cpp
┣━ CMakeLists.txt
可以看到源文件和頭文件分別存儲在對應目錄中,根目錄下以main.cpp作為程序入口,最終構建目標及中間文件存放在cmake-build-debug這一獨立文件夾中。
CMakeLists編寫
為了使上述目錄結構能夠正確編譯鏈接,我們需要編寫CMakeLists.txt,同時CMake能夠一定程度上減少多文件多目錄時來回鏈接順序等頭疼的問題。在這個項目里,根目錄下和src目錄下各有一個CMakeLists.txt文件,這也是CMake的特點,可以將Makefile拆分,每個目錄各自進行編譯,最終鏈接起來。
根目錄下的CMakeLists.txt內容如下
cmake_minimum_required (VERSION 2.6)
add_definitions(-std=c++11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g -Wall -Wno-unused-variable -pthread")
project (Minihttpd)
include_directories(include)
add_subdirectory(src)
# 順序不可修改,先link_directories 再 add_executable 最后 target_link_libraries
link_directories(/usr/local/lib)
add_executable(Minihttpd main.cpp)
target_link_libraries(Minihttpd src)
target_link_libraries(Minihttpd -lconfig++)
接下來對每條語句進行簡單的解釋
- add_definations 指令可以用來手動設置某些宏的開閉,控制編譯選項,這里主要是標注使用c++11標準
- set指令能夠用前面的變量替代后面的字符串,這里實際上是對預定義的CMAKE_CXX_FLAGS變量進行了一個修改,來設置某些選項,主要鏈接pthread庫使用多線程
- project指令用來設置項目名(包括版本、所用語言等信息,這里缺省)
- include_directories指令用來指定尋找頭文件的路徑,這里把include目錄加入到頭文件搜索范圍內,使得項目內文件可以找到對應頭文件
- add_subdirectory指令用來把子目錄加入到構建列表中,最終構建結果存放在src變量中
之后的幾行是在項目需要引用其他動態鏈接庫,非常需要注意的地方
- add_executable指令用來指定項目最終構建的目標文件,以及所需要的所有源文件。可以看到這里只有main.cpp,為什么沒有包含src目錄下其他源文件?這里其實在下面使用 target_link_libraries 指令,以動態鏈接庫的形式引入進來。其順序是在子目錄中首先進行了部分構建,在src目錄下生成了相應的libsrc.a文件,最終鏈接到程序入口文件上,實現了構建。
- link_directories和target_link_libraries指令用來引入外部的動態鏈接庫。其中
- link_directories用來指定該動態鏈接庫所在目錄
- target_link_libraries用來把所需的動態鏈接庫引入到該項目中
這里非常需要注意的是幾條語句的順序,一定是
- link_directories 把動態鏈接庫所在目錄加入尋找列表中
- add_executable 指定最終構建目標名稱
- target_link_libraries 把需要的動態鏈接庫加入到項目中
這里的順序錯誤將導致鏈接失敗,出現找不到動態鏈接庫等各種問題(踩過的坑,心酸的淚)
子目錄下的CMakeLists.txt內容如下
aux_source_directory(. srcs)
add_library(src ${srcs})
這里的內容就非常簡單
- aux_source_directory指令把當前目錄下所有源文件加入到srcs變量中存儲
- add_library使用srcs變量中所有源文件進行構建,結果輸出為src。不同于add_executable,add_library的構建的最終目標為動態鏈接庫文件,而add_executable的構建結果為一個可執行文件。這里構建成動態鏈接庫文件也是為了在根目錄下構建時進行鏈接
擴展
在理解了多目錄下CMakeLists的編寫后,如果需要把源文件存放在多個不同目錄中,也可以以動態鏈接庫的形式分別進行構建、鏈接。而對于多級目錄,也可以依次逐級構建并鏈接。
對于需要引用的外部動態鏈接庫,也可以通過link_directories和target_link_libraries指令的配合進行引入。
同時本項目內使用了ninja作為構建工具,更方便了項目的構建,主要使用方法為
cd cmake-build-debug
cmake -G Ninja .. //cmake支持根據CMakeLists.txt自動化生成ninja構建所需要的ninja.build等文件
ninja //在該目錄下構建