//聲明要求的cmake最低版本,終端輸入cmake -version可查看cmake的版本
cmake_minimum_required( VERSION 2.8 )
//聲明cmake工程名字
project(slam)
//設置使用g++編譯器,這是添加變量的用法set(KEY VALUE)接收兩個參數,用來聲明變量。在camke語法中使用${KEY}這種寫法來取到VALUE
set( CMAKE_CXX_COMPILER "g++")
//設置cmake編譯模式有debug和release兩種PROJECT_SOURCE_DIR項目根目錄也就是是CmakeLists.txt的絕對路徑
set( CMAKE_BUILD_TYPE "Release" )
//設定生成的可執行二進制文件存放的存放目錄
set( EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
//設定生成的庫文件的存放目錄
set( LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
//參數CMAKE_CXX_FLAGS含義是: set compiler for c++ language
//添加c++11標准支持,*.CPP文件編譯選項,-march=native指定目標程序的cpu架構來進行程序優化
//native就是相當於自檢測cpu,-march是gcc優化選項,后面的-O3是用來調節編譯時的優化程度的,最高為-O3,最低為-O0即不做優化
//-Ox這個參數只有在CMake -DCMAKE_BUILD_TYPE=release時有效
//因為debug版的項目生成的可執行文件需要有調試信息並且不需要進行優化,而release版的不需要調試信息但需要優化
set( CMAKE_CXX_FLAGS “-std=c++11 -march=native -O3”)
//調試手段message打印信息,類似於echo/printf,主要用於查cmake文件的語法錯誤
set(use_test ${SOURCES_DIRECTORY}/user_accounts.cpp)
message("use_test : ${use_test}")
//在CMakeLists.txt中指定安裝位置, 在編譯終端指定安裝位置:cmake -DCMAKE_INSTALL_PREFIX=/usr ..
set(CMAKE_INSTALL_PREFIX < install_path >)
//增加子文件夾,也就是進入源代碼文件夾繼續構建
add_subdirectory(${PROJECT_SOURCE_DIR}/src)
//添加依賴,去尋找該庫的頭文件位置、庫文件位置以及庫文件名稱,並將其設為變量,返回提供給CMakeLists.txt其他部分使用。
//cmake_modules.cmake文件是把CMakeLists.txt里用來尋找特定庫的內容分離出來,如果提示沒有找到第三方依賴庫可以嘗試安裝或者暴力指定路徑
// 尋找OpenCV庫
find_package( OpenCV REQUIRED )
//在CMakeLists.txt中使用第三方庫的三部曲:find_package、include_directories、target_link_libraries
include_directories(${OpenCV_INCLUDE_DIRS})// 去哪里找頭文件
link_directories()// 去哪里找庫文件(.so/.lib/.ddl等)
target_link_libraries( ${OpenCV_LIBRARIES})// 需要鏈接的庫文件
message("OpenCV_INCLUDE_DIRS: \n" ${OpenCV_INCLUDE_DIRS})
message("OpenCV_LIBS: \n" ${OpenCV_LIBS})
// find_package(Eigen3 REQUIRED), 假如找不到Eigen3庫,我們就設置變量來指定Eigen3的頭文件位置
set(Eigen3_DIR /usr/lib/cmake/eigen3/Eigen3Config.cmake)
include_directories(/usr/local/include/eigen3)
CMake
說明
cmake的定義是什么 ?-----高級編譯配置工具
當多個人用不同的語言或者編譯器開發一個項目,最終要輸出一個可執行文件或者共享庫(dll,so等等)這時候神器就出現了-----CMake!
所有操作都是通過編譯CMakeLists.txt來完成的—簡單
官 方網站是 www.cmake.org,可以通過訪問官方網站獲得更多關於 cmake 的信息
學習CMake的目的,為將來處理大型的C/C++/JAVA項目做准備
CMake安裝
1、絕大多數的linux系統已經安裝了CMake
2、Windows或某些沒有安裝過的linux系統,去http://www.cmake.org/HTML/Download.html 可以下載安裝
CMake一個HelloWord
1、步驟一,寫一個HelloWord
#main.cpp
#include <iostream>
int main(){
std::cout << "hello word" << std::endl;
}
2、步驟二,寫CMakeLists.txt
#CMakeLists.txt
PROJECT (HELLO)
SET(SRC_LIST main.cpp)
MESSAGE(STATUS "This is BINARY dir " ${HELLO_BINARY_DIR})
MESSAGE(STATUS "This is SOURCE dir "${HELLO_SOURCE_DIR})
ADD_EXECUTABLE(hello ${SRC_LIST})
3、步驟三、使用cmake,生成makefile文件
cmake .
輸出:
[root@localhost cmake]# cmake .
CMake Warning (dev) in CMakeLists.txt:
Syntax Warning in cmake code at
/root/cmake/CMakeLists.txt:7:37
Argument not separated from preceding token by whitespace.
This warning is for project developers. Use -Wno-dev to suppress it.
-- The C compiler identification is GNU 10.2.1
-- The CXX compiler identification is GNU 10.2.1
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- This is BINARY dir /root/cmake
-- This is SOURCE dir /root/cmake
-- Configuring done
-- Generating done
-- Build files have been written to: /root/cmake
目錄下就生成了這些文件-CMakeFiles, CMakeCache.txt, cmake_install.cmake 等文件,並且生成了Makefile.
現在不需要理會這些文件的作用,以后你也可以不去理會。最關鍵的是,它自動生成了Makefile.
4、使用make命令編譯
root@localhost cmake]# make
Scanning dependencies of target hello
[100%] Building CXX object CMakeFiles/hello.dir/main.cpp.o
Linking CXX executable hello
[100%] Built target hello
5、最終生成了Hello的可執行程序
CMake一個HelloWord-的語法介紹
PROJECT關鍵字
可以用來指定工程的名字和支持的語言,默認支持所有語言
PROJECT (HELLO) 指定了工程的名字,並且支持所有語言—建議
PROJECT (HELLO CXX) 指定了工程的名字,並且支持語言是C++
PROJECT (HELLO C CXX) 指定了工程的名字,並且支持語言是C和C++
該指定隱式定義了兩個CMAKE的變量
MESSAGE關鍵字就可以直接使用者兩個變量,當前都指向當前的工作目錄,后面會講外部編譯
問題:如果改了工程名,這兩個變量名也會改變
解決:又定義兩個預定義變量:PROJECT_BINARY_DIR和PROJECT_SOURCE_DIR,這兩個變量和HELLO_BINARY_DIR,HELLO_SOURCE_DIR是一致的。所以改了工程名也沒有關系
SET關鍵字
用來顯示的指定變量的
SET(SRC_LIST main.cpp) SRC_LIST變量就包含了main.cpp
也可以 SET(SRC_LIST main.cpp t1.cpp t2.cpp)
MESSAGE關鍵字
向終端輸出用戶自定義的信息
主要包含三種信息:
- SEND_ERROR,產生錯誤,生成過程被跳過。
- SATUS,輸出前綴為—的信息。
- FATAL_ERROR,立即終止所有 cmake 過程.
ADD_EXECUTABLE關鍵字
生成可執行文件
ADD_EXECUTABLE(hello ${SRC_LIST}) 生成的可執行文件名是hello,源文件讀取變量SRC_LIST中的內容
也可以直接寫 ADD_EXECUTABLE(hello main.cpp)
上述例子可以簡化的寫成
PROJECT(HELLO)
ADD_EXECUTABLE(hello main.cpp)
注意:工程名的 HELLO 和生成的可執行文件 hello 是沒有任何關系的
語法的基本原則
-
變量使用${}方式取值,但是在 IF 控制語句中是直接使用變量名
-
指令(參數 1 參數 2...) 參數使用括弧括起,參數之間使用空格或分號分開。 以上面的 ADD_EXECUTABLE 指令為例,如果存在另外一個 func.cpp 源文件
就要寫成:ADD_EXECUTABLE(hello main.cpp func.cpp)或者ADD_EXECUTABLE(hello main.cpp;func.cpp)
-
指令是大小寫無關的,參數和變量是大小寫相關的。但,推薦你全部使用大寫指令
語法注意事項
- SET(SRC_LIST main.cpp) 可以寫成 SET(SRC_LIST “main.cpp”),如果源文件名中含有空格,就必須要加雙引號
- ADD_EXECUTABLE(hello main) 后綴可以不行,他會自動去找.c和.cpp,最好不要這樣寫,可能會有這兩個文件main.cpp和main
內部構建和外部構建
- 上述例子就是內部構建,他生產的臨時文件特別多,不方便清理
- 外部構建,就會把生成的臨時文件放在build目錄下,不會對源文件有任何影響強烈使用外部構建方式
外部構建方式舉例
//例子目錄,CMakeLists.txt和上面例子一致
[root@localhost cmake]# pwd
/root/cmake
[root@localhost cmake]# ll
total 8
-rw-r--r--. 1 root root 198 Dec 28 20:59 CMakeLists.txt
-rw-r--r--. 1 root root 76 Dec 28 00:18 main.cpp
1、建立一個build目錄,可以在任何地方,建議在當前目錄下
2、進入build,運行cmake .. 當然..表示上一級目錄,你可以寫CMakeLists.txt所在的絕對路徑,生產的文件都在build目錄下了
3、在build目錄下,運行make來構建工程
注意外部構建的兩個變量
1、HELLO_SOURCE_DIR 還是工程路徑
2、HELLO_BINARY_DIR 編譯路徑 也就是 /root/cmake/bulid
讓Hello World看起來更像一個工程
- 為工程添加一個子目錄 src,用來放置工程源代碼
- 添加一個子目錄 doc,用來放置這個工程的文檔 hello.txt
- 在工程目錄添加文本文件 COPYRIGHT, README
- 在工程目錄添加一個 runhello.sh 腳本,用來調用 hello 二進制
- 將構建后的目標文件放入構建目錄的 bin 子目錄
- 將 doc 目錄 的內容以及 COPYRIGHT/README 安裝到/usr/share/doc/cmake/
將目標文件放入構建目錄的 bin 子目錄
每個目錄下都要有一個CMakeLists.txt說明
[root@localhost cmake]# tree
.
├── build
├── CMakeLists.txt
└── src
├── CMakeLists.txt
└── main.cpp
外層CMakeLists.txt
PROJECT(HELLO)
ADD_SUBDIRECTORY(src bin)
src下的CMakeLists.txt
ADD_EXECUTABLE(hello main.cpp)
ADD_SUBDIRECTORY 指令
ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
-
這個指令用於向當前工程添加存放源文件的子目錄,並可以指定中間二進制和目標二進制存放的位置
-
EXCLUDE_FROM_ALL函數是將寫的目錄從編譯中排除,如程序中的example
-
ADD_SUBDIRECTORY(src bin)
將 src 子目錄加入工程並指定編譯輸出(包含編譯中間結果)路徑為bin 目錄
如果不進行 bin 目錄的指定,那么編譯結果(包括中間結果)都將存放在build/src 目錄
更改二進制的保存路徑
SET 指令重新定義 EXECUTABLE_OUTPUT_PATH 和 LIBRARY_OUTPUT_PATH 變量 來指定最終的目標二進制的位置
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
思考:加載哪個CMakeLists.txt當中
哪里要改變目標存放路徑,就在哪里加入上述的定義,所以應該在src下的CMakeLists.txt下寫
安裝
- 一種是從代碼編譯后直接 make install 安裝
- 一種是打包時的指定 目錄安裝。
- 簡單的可以這樣指定目錄:make install DESTDIR=/tmp/test
- 稍微復雜一點可以這樣指定目錄:./configure –prefix=/usr
如何安裝HelloWord
使用CMAKE一個新的指令:INSTALL
INSTALL的安裝可以包括:二進制、動態庫、靜態庫以及文件、目錄、腳本等
使用CMAKE一個新的變量:CMAKE_INSTALL_PREFIX
// 目錄樹結構
[root@localhost cmake]# tree
.
├── build
├── CMakeLists.txt
├── COPYRIGHT
├── doc
│ └── hello.txt
├── README
├── runhello.sh
└── src
├── CMakeLists.txt
└── main.cpp
3 directories, 7 files
安裝文件COPYRIGHT和README
INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake/)
FILES:文件
DESTINATION:
1、寫絕對路徑
2、可以寫相對路徑,相對路徑實際路徑是:${CMAKE_INSTALL_PREFIX}/<DESTINATION 定義的路徑>
CMAKE_INSTALL_PREFIX 默認是在 /usr/local/
cmake -DCMAKE_INSTALL_PREFIX=/usr 在cmake的時候指定CMAKE_INSTALL_PREFIX變量的路徑
安裝腳本runhello.sh
PROGRAMS:非目標文件的可執行程序安裝(比如腳本之類)
INSTALL(PROGRAMS runhello.sh DESTINATION bin)
說明:實際安裝到的是 /usr/bin
安裝 doc 中的 hello.txt
-
一、是通過在 doc 目錄建立CMakeLists.txt ,通過install下的file
-
二、是直接在工程目錄通過
INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake)
DIRECTORY 后面連接的是所在 Source 目錄的相對路徑
注意:abc 和 abc/有很大的區別
目錄名不以/結尾:這個目錄將被安裝為目標路徑下的
目錄名以/結尾:將這個目錄中的內容安裝到目標路徑
安裝過程
cmake ..
make
make install
靜態庫和動態庫的構建
任務:
1,建立一個靜態庫和動態庫,提供 HelloFunc 函數供其他程序編程使用,HelloFunc 向終端輸出 Hello World 字符串。
2,安裝頭文件與共享庫。
靜態庫和動態庫的區別
- 靜態庫的擴展名一般為“.a”或“.lib”;動態庫的擴展名一般為“.so”或“.dll”。
- 靜態庫在編譯時會直接整合到目標程序中,編譯成功的可執行文件可獨立運行
- 動態庫在編譯時不會放到連接的目標程序中,即可執行文件無法單獨運行。
構建實例
[root@localhost cmake2]# tree
.
├── build
├── CMakeLists.txt
└── lib
├── CMakeLists.txt
├── hello.cpp
└── hello.h
hello.h中的內容
#ifndef HELLO_H
#define Hello_H
void HelloFunc();
#endif
hello.cpp中的內容
#include "hello.h"
#include <iostream>
void HelloFunc(){
std::cout << "Hello World" << std::endl;
}
項目中的cmake內容
PROJECT(HELLO)
ADD_SUBDIRECTORY(lib bin)
lib中CMakeLists.txt中的內容
SET(LIBHELLO_SRC hello.cpp)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
ADD_LIBRARY
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
- hello:就是正常的庫名,生成的名字前面會加上lib,最終產生的文件是libhello.so
- SHARED,動態庫 STATIC,靜態庫
- ${LIBHELLO_SRC} :源文件
同時構建靜態和動態庫
// 如果用這種方式,只會構建一個動態庫,不會構建出靜態庫,雖然靜態庫的后綴是.a
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
ADD_LIBRARY(hello STATIC ${LIBHELLO_SRC})
// 修改靜態庫的名字,這樣是可以的,但是我們往往希望他們的名字是相同的,只是后綴不同而已
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})
SET_TARGET_PROPERTIES
這條指令可以用來設置輸出的名稱,對於動態庫,還可以用來指定動態庫版本和 API 版本
同時構建靜態和動態庫
SET(LIBHELLO_SRC hello.cpp)
ADD_LIBRARY(hello_static STATIC ${LIBHELLO_SRC})
//對hello_static的重名為hello
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")
//cmake 在構建一個新的target 時,會嘗試清理掉其他使用這個名字的庫,因為,在構建 libhello.so 時, 就會清理掉 libhello.a
SET_TARGET_PROPERTIES(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
SET_TARGET_PROPERTIES(hello PROPERTIES OUTPUT_NAME "hello")
SET_TARGET_PROPERTIES(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)
動態庫的版本號
一般動態庫都有一個版本號的關聯
libhello.so.1.2
libhello.so ->libhello.so.1
libhello.so.1->libhello.so.1.2
CMakeLists.txt 插入如下
SET_TARGET_PROPERTIES(hello PROPERTIES VERSION 1.2 SOVERSION 1)
VERSION 指代動態庫版本,SOVERSION 指代 API 版本。
安裝共享庫和頭文件
本例中我們將 hello 的共享庫安裝到
將 hello.h 安裝到
//文件放到該目錄下
INSTALL(FILES hello.h DESTINATION include/hello)
//二進制,靜態庫,動態庫安裝都用TARGETS
//ARCHIVE 特指靜態庫,LIBRARY 特指動態庫,RUNTIME 特指可執行目標二進制。
INSTALL(TARGETS hello hello_static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
注意:
安裝的時候,指定一下路徑,放到系統下
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
使用外部共享庫和頭文件
准備工作,新建一個目錄來使用外部共享庫和頭文件
[root@MiWiFi-R4CM-srv cmake3]# tree
.
├── build
├── CMakeLists.txt
└── src
├── CMakeLists.txt
└── main.cpp
main.cpp
#include <hello.h>
int main(){
HelloFunc();
}
解決:make后頭文件找不到的問題
PS:include <hello/hello.h> 這樣include是可以,這么做的話,就沒啥好講的了
關鍵字:INCLUDE_DIRECTORIES 這條指令可以用來向工程添加多個特定的頭文件搜索路徑,路徑之間用空格分割
在CMakeLists.txt中加入頭文件搜索路徑
INCLUDE_DIRECTORIES(/usr/include/hello)
感謝:
網友:zcc720的提醒
解決:找到引用的函數問題
報錯信息:undefined reference to `HelloFunc()'
關鍵字:LINK_DIRECTORIES 添加非標准的共享庫搜索路徑
指定第三方庫所在路徑,LINK_DIRECTORIES(/home/myproject/libs)
關鍵字:TARGET_LINK_LIBRARIES 添加需要鏈接的共享庫
TARGET_LINK_LIBRARIES的時候,只需要給出動態鏈接庫的名字就行了。
在CMakeLists.txt中插入鏈接共享庫,主要要插在executable的后面
查看main的鏈接情況
[root@MiWiFi-R4CM-srv bin]# ldd main
linux-vdso.so.1 => (0x00007ffedfda4000)
libhello.so => /lib64/libhello.so (0x00007f41c0d8f000)
libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f41c0874000)
libm.so.6 => /lib64/libm.so.6 (0x00007f41c0572000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f41c035c000)
libc.so.6 => /lib64/libc.so.6 (0x00007f41bff8e000)
/lib64/ld-linux-x86-64.so.2 (0x00007f41c0b7c000)
鏈接靜態庫
TARGET_LINK_LIBRARIES(main libhello.a)
特殊的環境變量 CMAKE_INCLUDE_PATH 和 CMAKE_LIBRARY_PATH
注意:這兩個是環境變量而不是 cmake 變量,可以在linux的bash中進行設置
我們上面例子中使用了絕對路徑INCLUDE_DIRECTORIES(/usr/include/hello)來指明include路徑的位置
我們還可以使用另外一種方式,使用環境變量export CMAKE_INCLUDE_PATH=/usr/include/hello
補充:生產debug版本的方法:
cmake .. -DCMAKE_BUILD_TYPE=debug