1. 程式人生 > >CMakeLists.txt 編寫要點 && 一個關於install()的深坑

CMakeLists.txt 編寫要點 && 一個關於install()的深坑

#PS:要轉載請註明出處,本人版權所有

#PS:這個只是 《 我自己 》理解,如果和你的

#原則相沖突,請諒解,勿噴

Linux 4.15.0-29-generic #31-Ubuntu SMP Tue Jul 17 15:39:52 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

背景

1 我在2016-05-08的時候,寫了一篇關於Makefile的文章,在我來此公司實習的時候(2016.11左右),為一個稍微大一點的專案完全手動的構建了一個基於Makefile的編譯環境。現在已經過去了一年半多左右,根據此專案組的成員給我的反饋是:依然可用。但是如果要新增一些新內容到專案中,如果不瞭解這個Makefile的話,很有可能就是新內容沒有生效。我想了想,也確實是這樣的,如果不對Makefile進行一部分了解的話,想新增新的內容是很麻煩的一件事情。但是這個問題其實是Makefile只是在shell command上的一層封裝,抽象的層級不是太高,也就導致了本身其構建起來很繁瑣,不太適合大型工程管理和構建。
2 根據這兩年來的接觸,我經常瞭解到封裝層級在Makefile上的框架有兩個,一個是Autoconfig,一個就是CMake。其中我Autoconfig我完全停留在用的層面,完全沒有去了解相關的內容,但是對於CMake,近一年多來,多次接觸,苦於沒有一個合適的機會進行總結。最近,由於需要為一個專案構建一套合適的編譯環境,我選擇了CMake,也許,這是一個合適的機會進行總結。
注意:也許我這裡介紹的CMake用起來比Make或者更低一級的gcc簡單了許多倍,但是,我希望各位學習此文的同時,一定要去了解了解Make和GCC相關的內容,別的不說,自己寫個小工程,分別用gcc原始命令生成目標,同時用Make來生成目標。這樣或許你會對此篇文章更加深刻。

CMake 簡要內容

CMake 是cross platform make的簡寫,從這裡你完全可以看出,CMake是基於Make來實現相關的內容的,換句話說,CMake就是在Make的基礎上抽象出來的更高階的框架。

CMakeLists.txt的編譯test.cpp生成test可執行檔案的基本例子:

cmake_minimum_required(VERSION 2.8.10)
SET(PROJECT_NAME test)
project(${PROJECT_NAME})
add_executable(test test.cpp)  

通過以下Shell Command:

mkdir -p build && cd
build && cmake .. && make

通過上文的shell命令,其實你也已經發現了,cmake會生成Makefile,然後我們需要呼叫make來生成可執行檔案。

CMakeLists.txt 編寫要點

常用的cmake指令解釋
cmake_minimum_required(VERSION xxx) #cmake最小版本需求,新版本的cmake改了很多東西,提升了便利性,也可能讓你自己挖坑了
project(xxx) #設定此專案的名稱
add_executable(target target_source_codes) #生成可執行檔案target ,後面填寫的是生成此可執行檔案所依賴的原始檔列表。
SET(var_name var_value)# 設定一個名字var_name 的變數,同時給此變數賦值為var_value MESSAGE("MSG") #類比echo 列印訊息 option(var_name "comment" var_value) #給變數var_name賦值為var_value,comment是此變數的註釋,和SET 有類似的功效,用於給某變數設定預設值 include_directories(xxx) #新增include路徑,也就是 gcc -I xxx 的意思,或者vs ide中新增標頭檔案包含目錄 add_subdirectory(xxx) #呼叫xxx子目錄的CMakeLists.txt執行 add_compile_options(xxx) #給編譯器新增xxx引數,但是貌似沒有什麼用,我一般不這樣新增引數,不直接 link_directories(xxx) #給編譯器新增庫目錄,也就是 gcc -L xxx 的意思,或者vs ide中新增庫的包含目錄 add_library(lib_name SHARED or STATIC lib_source_code) #和add_executable類似,生成庫檔案,SHARED代表動態庫,STATIC代表靜態庫, 最後一個引數代表此庫的原始檔列表,此指令只有三個引數 target_link_libraries(target_name lib_name ...) #給目標新增依賴庫,類似與gcc -l lib_name,此指令有兩個用處,一個是給可執行target_name 新增庫依賴,二是給庫target_name 新增庫依賴。

我常見的cmake指令也就是上述的這些,還有部分比較常見的指令這裡沒有列出,我放到了下面單獨講解如:install()

cmake 流控制指令相關

條件語句

if(xxx)
...
elseif(xx)
...
else()
...
endif()

#常見條件語句用法為:
# if (va)  va為bool型
# if (va MATCHES xxx) va 是string型別,如果va包含了xxx,則此句為真

迴圈語句

foreach(va va_lists)
...
endforeach()

在foreach中,va的值會依次被va_lists的值替換

macro 和 function
macro(name arg ...)
...
endmacro()
function(name arg ...)
...
endfunction()

巨集和函式效果都類似,唯一區別為function中的變數為區域性的。

install 指令(主要是生成Makefile中的install target)
install(FILES flie DESTINATION dir_path) #執行make install時,把file拷貝到dir_path
install(PROGRAMS file DESTINATION dir_path) #執行make install時,把file拷貝到dir_path,並給予file可執行許可權
INSTALL(TARGETS  ylib ylib_s
    #RUNTIME DESTINATION xxx
    LIBRARY DESTINATION lib
    ARCHIVE DESTINATION lib
)# 安裝libylib.so到lib目錄,安裝libylib_s.a到lib目錄,RUNTIME 是安裝可執行檔案到xxx目錄,注意這個指令有個坑,我後面會說明這個問題。
configure_file指令
configure_file(fileA fileB @ONLY)
#把fileA 複製並重命名為fileB,此時,fileA中的@var@的值會被替換為cmakelists.txt 中var的值。@ONLY是隻轉換@va@這種變數
CMakeLists.txt常用的內建變數
CMAKE_INSTALL_PREFIX  #make install 的安裝路徑
CMAKE_BUILD_TYPE #生成的目標為debug或者release
CMAKE_C_FLAGS #gcc 的編譯引數指定,這個非常好用,一般通過set 修改其值
CMAKE_CXX_FLAGS #g++ 和上面CMAKE_C_FLAGS 類似
CMAKE_CURRENT_SOURCE_DIR # 當前CMakeLists.txt所在的目錄,主要用來定位某檔案
CMAKE_CURRENT_BINARY_DIR # 當前CMakeLists.txt對應的編譯時的目錄
XXXConfig.cmake檔案(cmake模組檔案)編寫以及引用

yLibConfig.cmake


find_path(yLib_INCLUDE_DIR NAMES ylib.h PATHS @[email protected]/include) 

find_library(yLib_LIBRARY NAMES ylib PATHS @[email protected]/lib) 
#find_library 會到@[email protected]/lib目錄查詢libylib.so


set(yLib_FOUND TRUE) 
set(yLib_INCLUDE_DIRS ${yLib_INCLUDE_DIR}) 
set(yLib_LIBS ${yLib_LIBRARY}) 


mark_as_advanced(yLib_INCLUDE_DIRS yLib_LIBS )

XXX_INCLUDE_DIR
XXX_LIBRARY
XXX_FOUND
XXX_INCLUDE_DIRS
XXX_LIBS
以上變數最好都定義了,不然find_package可能會報錯
.cmake 檔案就是定義了相關include變數和lib變數,沒有什麼其他的東西

呼叫:

set(yLib_DIR "@[email protected]/cmake")
#設定.cmake 的目錄所在
find_package(yLib REQUIRED)
#find_package會匯入.cmake 中的相關變數,完成相關模組的匯入
一個關於install()指令的深坑
INSTALL(TARGETS  ylib ylib_s
    #RUNTIME DESTINATION xxx
    LIBRARY DESTINATION lib
    ARCHIVE DESTINATION lib
)
#對於RUNTIME  和 LIBRARY 兩種目標,在安裝時候,cmake會預設給你移除掉目標檔案中的gcc的Wl,rpath的值,導致某些庫找不到的錯誤。
以下變數會影響此坑,更詳細的資訊去查查別的資料,我這裡就不詳細說明了。
#set(CMAKE_SKIP_BUILD_RPATH FALSE)                
#set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)        
#set(CMAKE_INSTALL_RPATH "")                      
#set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)    
#set(CMAKE_SKIP_INSTALL_RPATH TRUE)
#set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")

#set(CMAKE_SKIP_RPATH TRUE)
#set(CMAKE_SKIP_INSTALL_RPATH TRUE)

注意:cmake會直接修改你的二進位制檔案替換掉rpath的相關資訊。預設替換的值是一個空值,也就是說移除掉了你設定的rpath的值

說明

以上只是介紹了cmake中常見的內容,而且很多內容只涉及到一般的使用方法,某些指令還有很多其他的操作,我這裡沒有介紹。如果需要了解更加詳細的資訊,我推薦各位去看cmake 的doc。

#PS:請尊重原創,不喜勿噴

#PS:要轉載請註明出處,本人版權所有.

有問題請留言,看到後我會第一時間回覆