天天看點

CMAKE---- 控制編譯IntroductionBackground探究CMAKE進階功能References

Introduction

根據CMKAE官網的介紹,主要是用來管理編譯和單元測試。我們第一部分總結CMAKE如何進行管理編譯的。

C M a k e   i s   a n   o p e n − s o u r c e , c r o s s − p l a t f o r m   f a m i l y   o f   t o o l s   d e s i g n e d   t o   b u i l d ,   t e s t   a n d   p a c k a g e   s o f t w a r e . \color {red}CMake \, is \, an \, open-source, cross-platform \, family \, of \, tools \, designed \, to \, build, \, test \, and \, package \, software. CMakeisanopen−source,cross−platformfamilyoftoolsdesignedtobuild,testandpackagesoftware. CMake is used to control the software compilation process using simple platform and compiler independent configuration files, and generate native makefiles and workspaces that can be used in the compiler environment of your choice.

備注:如果沒有實戰過CMAKE的,建議先從實戰這一節看,然後再從頭開始看。

Background

既然是編譯需要知道編譯的過程,分為預編譯->編譯->連結。

根據這篇文章[1]

  1. 在預編譯過程時,将source files加載到translation 工具中,head files 根據sources files的需求加載,可能會被加載很多次。
  2. 在編譯過程時,每個source files 生成機器語言.o檔案
  3. 最後将這些.o檔案進行連結,生成可執行檔案或者庫檔案。

分析一下CMAKE的主要資料結構

p r o j e c t : \color {red}project: project: 管理一堆可執行檔案和庫檔案,一個大的project下面可以有很多小的project。

t a r g e t : \color {red}target: target: 自然就是最後生成的可執行檔案或者庫檔案了。

s o u r c e s : \color {red}sources: sources:自然是生成這個target所需要的源檔案,也就是各種.c,.cxx。

d i r e c t o r i e s : \color {red}directories: directories: 連結的時候需要尋找對應的檔案:是以.so, .a, .h這些檔案的位址非常重要。

探究CMAKE

根據background這章,我們知道,生成一個target其實很簡單啊,隻要說清楚target需要哪些source file,需要哪些連結庫,最後再把連結庫和頭檔案的位址跟target說一下,不就ok了嗎? 那麼為什麼一個項目的CMAKE有時候搞的這麼複雜啊。

最基礎的CMAKE項目

如果是一個最簡單的項目,我們确實可以按照上面說的

cmake_minimum_required(VERSION 3.13)   #CMKAE版本設定

project(Tutorial VERSION 1.0)    # 建立了一個項目

add_executable(Tutorial main.cxx )  # 生成可執行檔案
target_link_libraries(Tutorial lcm)  #添加連結庫
target_link_directories(Tutorial "/usr/local/lib/")  #添加連結位址
target_include_directories(Tutorial PRIVATE        #添加include位址
    "/usr/local/include/lcm/")
           

注意事項:安裝在标準位置的庫,因為庫已經在gcc的預設搜尋位置,不需要再重複聲明了

預設的link_directories,包括了标準庫的安裝位置和目前項目下生成的庫位置

預設的include_directories,包括gcc的預設搜尋位置和CMAKE_CURRENT_SOURCE_DIR

/usr/lib/gcc/x86_64-redhat-linux/4.8.2/…/…/…/…/include/c++/4.8.2

/usr/lib/gcc/x86_64-redhat-linux/4.8.2/…/…/…/…/include/c++/4.8.2/x86_64-redhat-linux

/usr/lib/gcc/x86_64-redhat-linux/4.8.2/…/…/…/…/include/c++/4.8.2/backward

/usr/lib/gcc/x86_64-redhat-linux/4.8.2/include

/usr/local/include

/usr/include

分 析 這 麼 寫 c m a k e 繁 瑣 的 地 方 在 哪 裡 ? \color {red} 分析這麼寫cmake繁瑣的地方在哪裡? 分析這麼寫cmake繁瑣的地方在哪裡?

sources file 太多了。

include_directories也很麻煩,尤其是source files太多,依賴的header file也很多的情況下

改進1:source files用庫檔案表示

使用動态連結庫,将一些source file 子產品化,并且可以讓項目重用

因為庫也是一個target,是以和生成可執行檔案沒有任何差別。

項目就産生了層級關系,用一個CMakeLIsts.txt沒辦法完成了

需要在頂層cmakelists中對應add_directories,這樣在project_binary_dir裡面才會出現子檔案夾,對應source 部分的子檔案夾下的CMakeLists.txt才能編譯。如果某一些檔案夾和他的子目錄都不需要編譯生成target,那就不需要添加CMakeLists.txt

改進2:source files 化整為零

方法1:用 t a r g e t s o u r c e s \color {red}target_sources targets​ources:将sources file分成多個子檔案夾,在每個子檔案夾中用target_sources進行添加,這樣在頂層時,不需要一下子添加那麼多sources file;

方法2:使用 F I L E ( G L O B   s o u r c e s d i r ) \color {red}FILE(GLOB \, sources dir) FILE(GLOBsourcesdir) 或者是 f i l e ( G L O B   R E C U R S E   s o u r c e s   d i r ) \color {red} file(GLOB \, RECURSE \, sources \, dir) file(GLOBRECURSEsourcesdir) 如果真的是sources files多到頭皮發麻,實在是不想一個個寫,用這種方法。

改進3:include_directories用usage requirements

header files經常被重複使用,這裡cmake借鑒了面向對象的思想,通過target_include_directories()添加PUBLIC, PRIVATE 或者INTERFACE在target之間進行共享

改進4:用find_

有時候一些檔案不知道放在什麼地方了,尤其是在編譯中生成的檔案。

find_file, find_library_, find_path, find_program, 以及find_package

進階功能

1.因為cmake可以做一些測試,可以定義一些函數,如果其他的CMAKE需要調用這些函數,就需要INCLUDE(頭檔案)

2.條件編譯:option,if endif, 可以用list資料結構

3.export功能

這方面目前連接配接的較少,後續再逐漸添加.

4.設定g++11和gnu++11:

錯誤提示:

#error This file requires compiler and library support for the ISO C++ 2011 standard. This support must be enabled with the -std=c++11 or -std=gnu++11 compiler options.

解決辦法:在cmake中添加,參考[2]

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")
elseif(COMPILER_SUPPORTS_CXX0X)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
else()
        message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
endif()
           

References

[1] https://www.toptal.com/c-plus-plus/c-plus-plus-understanding-compilation

[2] https://blog.csdn.net/heroacool/article/details/50906151

繼續閱讀