新手寫CMakeLists.txt簡直就是實力勸退,各種命令讓很多人頭大,如何寫一個最基礎的CMakeLists.txt呢?本文從一個實例出發,教你編寫的基本流程。
本文附實例的源碼地址。
CMakeLists.txt的基本結構
編寫CMakeLists.txt
最常用的功能就是調用其他的.h頭文件和.so/.a庫文件,將.cpp/.c/.cc文件編譯成可執行文件或者新的庫文件。
命令的官方網站:CMake Reference Documentation
最常用的命令如下(僅供後期查詢,初期不需要細看):
# 本CMakeLists.txt的project名稱
# 會自動創建兩個變量,PROJECT_SOURCE_DIR和PROJECT_NAME
# ${PROJECT_SOURCE_DIR}:本CMakeLists.txt所在的文件夾路徑
# ${PROJECT_NAME}:本CMakeLists.txt的project名稱
project(xxx)
# 獲取路徑下所有的.cpp/.c/.cc文件,並賦值給變量中
aux_source_directory(路徑 變量)
# 給文件名/路徑名或其他字符串起別名,用${變量}獲取變量內容
set(變量 文件名/路徑/...)
# 添加編譯選項
add_definitions(編譯選項)
# 打印消息
message(消息)
# 編譯子文件夾的CMakeLists.txt
add_subdirectory(子文件夾名稱)
# 將.cpp/.c/.cc文件生成.a靜態庫
# 注意,庫文件名稱通常爲libxxx.so,在這裏只要寫xxx即可
add_library(庫文件名稱 STATIC 文件)
# 將.cpp/.c/.cc文件生成可執行文件
add_executable(可執行文件名稱 文件)
# 規定.h頭文件路徑
include_directories(路徑)
# 規定.so/.a庫文件路徑
link_directories(路徑)
# 對add_library或add_executable生成的文件進行鏈接操作
# 注意,庫文件名稱通常爲libxxx.so,在這裏只要寫xxx即可
target_link_libraries(庫文件名稱/可執行文件名稱 鏈接的庫文件名稱)
通常一個CMakeLists.txt需按照下面的流程:
project(xxx) #必須
add_subdirectory(子文件夾名稱) #父目錄必須,子目錄不必
add_library(庫文件名稱 STATIC 文件) #通常子目錄(二選一)
add_executable(可執行文件名稱 文件) #通常父目錄(二選一)
include_directories(路徑) #必須
link_directories(路徑) #必須
target_link_libraries(庫文件名稱/可執行文件名稱 鏈接的庫文件名稱) #必須
除了這些之外,就是些set變量的語句,if判斷的語句,或者其他編譯選項的語句,但基本結構都是這樣的。
實例
我以自己曾經寫的一段實際代碼爲例,來講解究竟該怎麼寫CMakeLists
。
實例地址:
碼雲:https://gitee.com/yngzMiao/protobuf-parser-tool
GitHub:https://github.com/yngzMiao/protobuf-parser-tool
實例的功能是生成和解析proto
文件,分爲C++
和python
版本。其中,C++
版本就是採用CMakeLists.txt
編寫的,目錄結構如下:
|---example_person.cpp
|---proto_pb2
|--Person.pb.cc
|--Person.pb.h
|---proto_buf
|---General_buf_read.h
|---General_buf_write.h
|---protobuf
|---bin
|---...
|---include
|---...
|---lib
|---...
目錄結構含義:
- protobuf:
Google
提供的相關解析庫和頭文件,被proto_pb2
文件夾內引用; - proto_pb2:封裝的
Person
結構和Person
相關的處理函數,被proto_buf
文件夾內引用; - proto_buf:封裝的
read
和write
函數,被example_persom.cpp
文件引用。
也就是說:
example_person.cpp
–>proto_buf
文件夾–>proto_pb2
文件夾–>protobuf
文件夾
步驟
CMakeLists.txt的創建
在需要進行編譯的文件夾內編寫CMakeLists.txt
,即含有.cpp/.c/.cc
的文件夾內:
即目錄結構如下:
|---example_person.cpp
|---CMakeLists.txt
|---proto_pb2
|--Person.pb.cc
|--Person.pb.h
|--CMakeLists.txt
|---proto_buf
|---General_buf_read.h
|---General_buf_write.h
|---protobuf
|---bin
|---...
|---include
|---...
|---lib
|---...
CMakeLists.txt的編寫
本項目的CMakeLists.txt
的文件數量是2個,目錄層次結構爲上下層關係。通常的解決方案,就是將下層目錄編譯成一個靜態庫文件,讓上層目錄直接讀取和調用,而上層目錄就直接生成一個可執行文件。
上層CMakeLists.txt的內容爲:
cmake_minimum_required(VERSION 3.0)
project(example_person)
# 如果代碼需要支持C++11,就直接加上這句
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
# 如果想要生成的可執行文件擁有符號表,可以gdb調試,就直接加上這句
add_definitions("-Wall -g")
# 設置變量,下面的代碼都可以用到
set(GOOGLE_PROTOBUF_DIR ${PROJECT_SOURCE_DIR}/protobuf)
set(PROTO_PB_DIR ${PROJECT_SOURCE_DIR}/proto_pb2)
set(PROTO_BUF_DIR ${PROJECT_SOURCE_DIR}/proto_buf)
# 編譯子文件夾的CMakeLists.txt
add_subdirectory(proto_pb2)
# 規定.h頭文件路徑
include_directories(${PROJECT_SOURCE_DIR}
${PROTO_PB_DIR} ${PROTO_BUF_DIR}
)
# 生成可執行文件
add_executable(${PROJECT_NAME} example_person.cpp )
# 鏈接操作
target_link_libraries(${PROJECT_NAME}
general_pb2)
如果是初學者,這一段可能看不到兩個地方,第一是鏈接操作的general_pb2
,第二是按照上文的CMakeLists.txt
的流程,並沒有規定link_directories
的庫文件地址啊,這是什麼道理?
這兩個其實是一個道理,add_subdirectory起到的作用!
當運行到add_subdirectory
這一句時,會先將子文件夾進行編譯,而libgeneral_pb2.a
是在子文件夾中生成出來的庫文件。子文件夾運行完後,父文件夾就已經知道了libgeneral_pb2.a
這個庫,因而不需要link_directories
了。
同時,另一方面,在add_subdirector之前set的各個變量,在子文件夾中是可以調用的!
下層CMakeLists.txt的內容爲:
project(general_pb2)
aux_source_directory(${PROJECT_SOURCE_DIR} PB_FILES)
add_library(${PROJECT_NAME} STATIC ${PB_FILES})
include_directories(${PROJECT_SOURCE_DIR}
${GOOGLE_PROTOBUF_DIR}/include
)
link_directories(${GOOGLE_PROTOBUF_DIR}/lib/)
target_link_libraries(${PROJECT_NAME}
protobuf
)
在這裏,GOOGLE_PROTOBUF_DIR
是上層CMakeLists.txt
中定義的,libprotobuf.a
是在${GOOGLE_PROTOBUF_DIR}/lib/
目錄下的。
顯然可見,這就是一個標準的CMakeLixts.txt
的流程。
CMakeLists.txt的編譯
一般CMakeLists.txt是,在最頂層創建build文件夾,然後編譯。即:
mkdir build && cd build
cmake ..
make
最終生成可執行文件example_person
。
可以通過以下命令來運行該可執行文件:
./example_person