1. 程式人生 > >CMake常用語法+包含常用庫+工程目錄組織

CMake常用語法+包含常用庫+工程目錄組織

命令不區分大小寫,變數名區分大小寫,引數之間使用空格進行分隔

 

內建變數的使用:

•在CMakeLists.txt中指定,使用set
•cmake命令中使用,如cmake -DBUILD_SHARED_LIBS=OFF

 

ADD_DEFINITIONS
向 C/C++編譯器新增-D 定義,比如:
ADD_DEFINITIONS(-DENABLE_DEBUG -DABC),引數之間用空格分割。

如果你的程式碼中定義了#ifdef ENABLE_DEBUG #endif,這個程式碼塊就會生效。
如果要新增其他的編譯器開關,可以通過 CMAKE_C_FLAGS 變數和 CMAKE_CXX_FLAGS 變數設定。

 

ADD_DEPENDENCIES
定義 target 依賴的其他 target,確保在編譯本 target 之前,其他的 target 已經被構建。
ADD_DEPENDENCIES(target-name depend-target1 depend-target2 ...)

 

CMAKE_C(XX)_FLAGS

變數 CMAKE_C_FLAGS 存放的內容會被傳給 C 編譯器,作用在所有的編譯組態上。如果希望只針對特定一種組態有效,可以設定 CMAKE_C_FLAGS_<編譯組態>,例如 CMAKE_C_FLAGS_RELEASE、CMAKE_C_FLAGS_DEBUG。
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}     -Wall -O3 -march=native -Wno-reorder")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -O3 -march=native -Wno-reorder")

編譯選項為-Wall O3的優化

 

debug 版的專案生成的可執行檔案需要有除錯資訊並且不需要進行優化,而 release 版的不需要除錯資訊但需要優化。這些特性在 gcc/g++ 中是通過編譯時的引數來決定的,如果將優化程度調到最高需要設定引數-O3,最低是 -O0 即不做優化;新增除錯資訊的引數是 -g -ggdb ,如果不新增這個引數,除錯資訊就不會被包含在生成的二進位制檔案中。

CMake 中有一個變數CMAKE_BUILD_TYPE ,可以的取值是 Debug、Release、RelWithDebInfo 和 MinSizeRel。當這個變數值為 Debug 的時候,CMake 會使用變數 CMAKE_CXX_FLAGS_DEBUG 和 CMAKE_C_FLAGS_DEBUG中的字串作為編譯選項生成Makefile ,當這個變數值為 Release 的時候,工程會使用變數 CMAKE_CXX_FLAGS_RELEASE 和CMAKE_C_FLAGS_RELEASE 選項生成 Makefile。

SET(CMAKE_CXX_FLAGS_DEBUG"$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")

SET(CMAKE_CXX_FLAGS_RELEASE"$ENV{CXXFLAGS} -O3 -Wall")

設定了兩個變數 CMAKE_CXX_FLAGS_DEBUG 和CMAKE_CXX_FLAGS_RELEASE, 這兩個變數是分別用於 debug 和 release 的編譯選項。

 

多原始檔目錄的處理方式

我們在每一個原始碼目錄中都會放置一個 CMakeLists.txt 檔案。我們現在假定有這麼一個工程:

HelloWorld

|

+------- Main.cpp

|

+------- CMakeLists.txt

|

+------- Lib

        |

        +------- Lib.cpp

        |

        +------- Lib.h

        |

        +------- CMakeLists.txt

這裡 Lib 目錄下的檔案將被編譯為一個庫。首先,我們看一下 Lib 目錄下的 CMakeLists.txt 檔案:

aux_source_directory(. DIR_SRCS)

add_library(Lib ${DIR_SRCS})

然後,看一下 HelloWorld 目錄下的 CMakeLists.txt 檔案:

project(Main)

cmake_minimum_required(VERSION 2.8)

add_subdirectory(Lib)

aux_source_directory(. DIR_SRCS)

add_executable(Main ${DIR_SRCS})

target_link_libraries(Main Lib)

這裡使用了 add_subdirectory 指定了需要進行構建的子目錄,並且使用了 target_link_libraries 命令,表示 Main 可執行檔案需要連結 Lib庫。我們執行 CMake . 命令,首先會執行 HelloWorld 目錄下的 CMakeLists.txt 中的命令,當執行到 add_subdirectory(Lib) 命令的時候會進入 Lib 子目錄並執行其中的CMakeLists.txt 檔案。

 

例子:

 CMakeLists.txt(放在工程頂層目錄下)

# 宣告要求的 cmake 最低版本
cmake_minimum_required( VERSION 2.8 )

# 宣告一個 cmake 工程
# PROJECT指令會隱式定義<projectname>_BINARY_DIR和<projectname>_SOURCE_DIR兩個變數
project( HelloSLAM )
# message([STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR] “message todisplay”…)
message(${PROJECT_NAME})
MESSAGE(STATUS "HOME dir: $ENV{HOME}")#呼叫系統的環境變數
message($ENV{PATH})

set(FLAG ON)
if(NOT FLAG)#在控制語句條件中使用變數,不能用${}引用,而是直接應用變數名
message("FLAG false!!!")
elseif(UNIX)#WIN32
message("UNIX!!!")
else()
message(FATAL_ERROR "error!!!")
endif()

# 設定編譯模式
# set( CMAKE_BUILD_TYPE "Debug" )
set( CMAKE_BUILD_TYPE "Release" )

# 編譯標誌相關變數,編譯選項為-Wall O3的優化
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -O3 -march=native -Wno-reorder")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -O3 -march=native -Wno-reorder")

# CHECK_CXX_COMPILER_FLAG(<flag> <var>)檢查CXX編譯器是否支援給定的flag,賦值給的var是個bool型
# 檢查當前編譯器是否支援c++11
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
if(COMPILER_SUPPORTS_CXX11)
   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
   add_definitions(-DCOMPILEDWITHC11)
   message(STATUS "Using flag -std=c++11.")
elseif(COMPILER_SUPPORTS_CXX0X)
   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
   add_definitions(-DCOMPILEDWITHC0X)
   message(STATUS "Using flag -std=c++0x.")
else()
   message(FATAL_ERROR "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
endif()

#最終生成的庫檔案存放在工程頂層目錄下的lib資料夾
#PROJECT_SOURCE_DIR、CMAKE_SOURCE_DIR、${PROJECT_NAME}_SOURCE_DIR都是指向工程頂層目錄
set(LIBRARY_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/lib)

#--------------------生成自己的庫--------------------#
#aux_source_directory(<dir> <variable>)用於將dir目錄下的所有原始檔的名字儲存在變數variable中
aux_source_directory(${PROJECT_SOURCE_DIR}/src/lib_src LIB_SRC)
FOREACH(F ${LIB_SRC})
    MESSAGE(${F})
ENDFOREACH(F)
#預設靜態庫(.a),若SET(BUILD_SHARED_LIBS ON)後,預設共享庫
add_library( hello ${LIB_SRC})
# 共享庫(.so)
add_library( hello_shared SHARED ${LIB_SRC})

#--------------------包含第三方庫--------------------#
# 包含Eigen庫
# 新增標頭檔案
include_directories( "/usr/include/eigen3" )

include_directories( "${PROJECT_SOURCE_DIR}/include" )

# FIND_PACKAGE(<name> [major.minor] [QUIET] [NO_MODULE][[REQUIRED|COMPONENTS] [componets...]])
# REQUIRED 引數,其含義是指這個共享庫是否是工程必須的,如果使用了這個引數,說明這個連結庫是必備庫,如果找不到這個連結庫,則工程不能編譯
# QUIET 引數,如果不指定這個引數,就會執行:

# 包含Sophus庫
# 尋找Sophus庫
FIND_PACKAGE( Sophus REQUIRED )
# 新增標頭檔案
include_directories( ${Sophus_INCLUDE_DIRS} )

# 包含OpenCV庫
# 尋找OpenCV庫
find_package(OpenCV 3.0 QUIET)
if(NOT OpenCV_FOUND)
   find_package(OpenCV 2.4.3 QUIET)
   if(NOT OpenCV_FOUND)
      message(FATAL_ERROR "OpenCV > 2.4.3 not found.")
   endif()
endif()
# 新增標頭檔案
include_directories( ${OpenCV_INCLUDE_DIRS} )

#最終生成的可執行檔案存放在工程頂層目錄下的bin資料夾
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

# 新增一個可執行程式
# 語法:add_executable( 程式名 原始碼檔案 )
add_executable( useHello  ${PROJECT_SOURCE_DIR}/src/useHello.cpp)
# 將庫檔案連結到可執行程式上
target_link_libraries( useHello hello_shared
${Sophus_LIBRARIES}
${OpenCV_LIBS})

#最終生成的可執行檔案分別放在不同資料夾下
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin/bin1)

add_executable( useHello1  ${PROJECT_SOURCE_DIR}/src/useHello.cpp)
target_link_libraries( useHello1 hello_shared
${Sophus_LIBRARIES}
${OpenCV_LIBS})

libHelloSLAM.h(放在include目錄下)

#ifndef LIBHELLOSLAM_H_
#define LIBHELLOSLAM_H_
// 上面的巨集定義是為了防止重複引用這個標頭檔案而引起的重定義錯誤

void printHello();

#endif

libHelloSLAM.cpp(放在src/lib_src目錄下)

//這是一個庫檔案
#include <iostream>
using namespace std;

void printHello()
{
    cout<<"Hello SLAM"<<endl;
}

useHello.cpp(放在src目錄下)

#include <iostream>
#include "libHelloSLAM.h"
#include <opencv2/opencv.hpp>
#include <Eigen/Core>
#include <Eigen/Geometry>
#include <sophus/so3.h>
#include <sophus/se3.h>

using namespace std;
using namespace cv;
 
int main( int argc, char** argv )
{
	if(argc<2)
	{
		cout<<"usage: useHello path_to_image"<<endl;
		return -1;
	}

	//使用C++11新特性
	vector<int> nums(10,1);
	for(auto &i : nums)
	{
		cout<<i<<" ";
	}
	cout<<endl;
	
	Mat img=imread(argv[1]);
	imshow("my_img",img);
	waitKey(0);

    printHello();//使用 libHelloSLAM.h 中的 printHello() 函式
    Eigen::Matrix3d Eigen_R = Eigen::AngleAxisd(M_PI/2, Eigen::Vector3d(0,0,1)).toRotationMatrix();
    cout<<"rotation from Eigen: "<<endl<<Eigen_R<<endl;
    Sophus::SO3 SO3_R(Eigen_R);
    cout<<"rotation from so3: "<<endl<<SO3_R<<endl;
    return 0;
}

參考:

linux CMakeLists.txt 語法

cmake 常用變數和常用環境變數查表手冊