一补箍、背景:Cmakelist的使用
項目創(chuàng)建好以后我們可以看到和普通Android項目有以下4個不同尘喝。
- main 下面增加了 cpp 目錄,即放置 c/c++ 代碼的地方
- module-level 的 build.gradle 有修改
- 增加了 CMakeLists.txt 文件
- 多了一個 .externalNativeBuild 目錄
二是晨、正文第一章:CMakeLists.txt 文件 講解
2.1 一個基本的Cmakelist文件
cmake_minimum_required(VERSION 3.4.1)
# 編譯出一個動態(tài)庫 native-lib癌椿,源文件只有 src/main/cpp/native-lib.cpp
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/native-lib.cpp )
# 找到預編譯庫 log_lib 并link到我們的動態(tài)庫 native-lib中
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
target_link_libraries( # Specifies the target library.
native-lib
# Links the target library to the log library
# included in the NDK.
${log-lib} )
2.1.1 add_library - 添加庫(三方庫.so 或者編譯C/C++文件)
2.1.1 .1 添加庫的第一種方法
add_library(
avcodec-lib
SHARED
IMPORTED)
set_target_properties( avcodec-lib
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/${ANDROID_ABI}/libavcodec-56.so)
添加庫健蕊,也就是:add_library函數(shù),里面要傳入三個參數(shù)如失;第一個是要引入的庫別名绊诲,第二個是庫的類型,是靜態(tài)庫還是動態(tài)庫褪贵。第三個是通過什么樣方式引入進來掂之,第三方的一般都是通過包含進來,所以第三個參數(shù)基本也是固定的都是寫“IMPORTED”脆丁。
add_library后世舰,就要設置.so的詳細路徑了,通過set_target_properties()函數(shù)來設置;該函數(shù)也是要傳入三參數(shù)來指定.so庫的路徑槽卫。第一個參數(shù)和add_library的第一個參數(shù)一樣跟压,不過這里的庫別名要和add_library的庫別名要一致,要不然在編譯時會報找不到庫的錯誤歼培。第二個參數(shù)是固定的震蒋,都是寫“ PROPERTIES IMPORTED_LOCATION”主要用來指定庫的引入方式。都是通過本地引入躲庄。第三個就是庫的具體路徑查剖,這個不能寫錯,如果寫錯了噪窘,編譯時也同樣會找不到庫的笋庄。只要是引入第三方的庫使用add_library就要使用set_target_propeties這個組合,所以它們是成對出現(xiàn)的。add_library還有一種寫法那就是CMakeList.txt中默認寫法
2.1.1.2 添加庫的第二種方法
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/native-lib.cpp )
這樣是就是編譯我們自己在項目中寫的c/c++文件直砂。這里就不用set_target_propeties()菌仁。add_library用來設置編譯生成的本地庫的名字為native-lib,SHARED表示編譯生成的是動態(tài)鏈接庫(這個概念前面已經(jīng)提到過了)静暂,src/main/cpp/native-lib.cpp表示參與編譯的文件的路徑济丘,這里面可以寫多個文件的路徑。
2.1.2 find_library - 查找?guī)?/h2>
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
find_library 看到這個名字相信都會知道它是干嘛用的洽蛀,查找?guī)煊玫纳量怯脕硖砑右恍┪覀冊诰幾g我們的本地庫的時候需要依賴的一些庫,這個主要是查找系統(tǒng)庫用的辱士,如果項目里面有用到系統(tǒng)的.so庫就是要把庫名寫到這個函數(shù)里面去找到相對應的為。由于cmake已經(jīng)知道系統(tǒng)庫的路徑听绳,所以我們這里只是指定使用log庫颂碘,然后給log庫起別名為log-lib便于我們后面引用,此處的log庫是我們后面調(diào)試時需要用來打log日志的庫椅挣,是NDK為我們提供的头岔。
2.1.3 target_link_libraries
target_link_libraries( # Specifies the target library.
native-lib
# Links the target library to the log library
# included in the NDK.
${log-lib} )
函數(shù)target_link_libraries()這個是干嘛用的呢,講到這個函數(shù)就要講到c/c++的編譯原理了鼠证,在linux中c/c++的編譯一般都是用gcc來編譯的峡竣,c/c++編譯時會產(chǎn)生.o文件要通過make工具來把這些.o文件鏈接起來,這樣才能得一個可執(zhí)行程序量九。所以.so在編譯時要把所有庫鏈接起來才能編譯适掰。target_link_libraries()就是干這個事,target_link_libraries 是為了關(guān)聯(lián)我們自己的庫和一些第三方庫或者系統(tǒng)庫荠列。把要鏈接的庫別名都寫到這里就可以了类浪,如果是系統(tǒng)的庫要用這個格式${庫的名字}。
2.2 更多參數(shù)的Cmakelist文件
#指定需要CMAKE的最小版本
cmake_minimum_required(VERSION 3.4.1)
#C的編譯選項是 CMAKE_C_FLAGS
#指定編譯參數(shù)肌似,可選
#SET(CMAKE_CXX_FLAGS "-Wno-error=format-security -Wno-error=pointer-sign")
#設置生成的so動態(tài)庫最后輸出的路徑
#set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../jniLibs/${ANDROID_ABI})
#調(diào)用指定目錄下的cmakelist
add_subdirectory(src/main/cpp/mbedtls)
#設置需要引用的庫的類型(動/靜態(tài)庫)费就,庫的地址,名稱
#add_library(lib SHARED/STATIC IMPORTED)
#set_target_properties(lib PROPERTIES IMPORTED_LOCATION ${LIB_DIR}/lib.a)
#設置頭文件搜索路徑(和此txt同個路徑的頭文件無需設置),可選
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/mbedtls/include)
#指定用到的系統(tǒng)庫或者NDK庫或者第三方庫的搜索路徑川队,可選力细。
#LINK_DIRECTORIES(/usr/local/lib)
add_library( nodepp
SHARED
src/main/cpp/utils.c
src/main/cpp/xxtea.c
src/main/cpp/mbed_client.c)
target_link_libraries( nodepp
mbedcrypto
mbedx509
mbedtls
log )
三、正文第二章:NDK自定義配置
3.1 添加多個參與編譯的C/C++文件
首先固额,我們發(fā)現(xiàn)我們上面的例子都是涉及到一個C++文件眠蚂,那么我們實際的項目不可能只有一個C++文件,所以我們首先要改變CMakeLists.txt文件对雪,如下 :
add_library( HelloNDK
SHARED
src/main/cpp/HelloNDK.c
src/main/cpp/HelloJNI.c)
簡單吧河狐,簡單明了,但是這里要注意的是,你在寫路徑的時候一定要注意當前的CMakeLists.txt在項目中的位置馋艺,上面的路徑是相對于CMakeLists.txt 寫的栅干。
3.2 我們想編譯出多個so庫
大家會發(fā)現(xiàn),我們上面這樣寫捐祠,由于只有一個CMakeLists.txt文件碱鳞,所以我們會把所有的C/C++文件編譯成一個so庫,這是很不合適的踱蛀,這里我們就試著學學怎么編譯出多個so庫窿给。
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
find_library 看到這個名字相信都會知道它是干嘛用的洽蛀,查找?guī)煊玫纳量怯脕硖砑右恍┪覀冊诰幾g我們的本地庫的時候需要依賴的一些庫,這個主要是查找系統(tǒng)庫用的辱士,如果項目里面有用到系統(tǒng)的.so庫就是要把庫名寫到這個函數(shù)里面去找到相對應的為。由于cmake已經(jīng)知道系統(tǒng)庫的路徑听绳,所以我們這里只是指定使用log庫颂碘,然后給log庫起別名為log-lib便于我們后面引用,此處的log庫是我們后面調(diào)試時需要用來打log日志的庫椅挣,是NDK為我們提供的头岔。
target_link_libraries( # Specifies the target library.
native-lib
# Links the target library to the log library
# included in the NDK.
${log-lib} )
函數(shù)target_link_libraries()這個是干嘛用的呢,講到這個函數(shù)就要講到c/c++的編譯原理了鼠证,在linux中c/c++的編譯一般都是用gcc來編譯的峡竣,c/c++編譯時會產(chǎn)生.o文件要通過make工具來把這些.o文件鏈接起來,這樣才能得一個可執(zhí)行程序量九。所以.so在編譯時要把所有庫鏈接起來才能編譯适掰。target_link_libraries()就是干這個事,target_link_libraries 是為了關(guān)聯(lián)我們自己的庫和一些第三方庫或者系統(tǒng)庫荠列。把要鏈接的庫別名都寫到這里就可以了类浪,如果是系統(tǒng)的庫要用這個格式${庫的名字}。
#指定需要CMAKE的最小版本
cmake_minimum_required(VERSION 3.4.1)
#C的編譯選項是 CMAKE_C_FLAGS
#指定編譯參數(shù)肌似,可選
#SET(CMAKE_CXX_FLAGS "-Wno-error=format-security -Wno-error=pointer-sign")
#設置生成的so動態(tài)庫最后輸出的路徑
#set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../jniLibs/${ANDROID_ABI})
#調(diào)用指定目錄下的cmakelist
add_subdirectory(src/main/cpp/mbedtls)
#設置需要引用的庫的類型(動/靜態(tài)庫)费就,庫的地址,名稱
#add_library(lib SHARED/STATIC IMPORTED)
#set_target_properties(lib PROPERTIES IMPORTED_LOCATION ${LIB_DIR}/lib.a)
#設置頭文件搜索路徑(和此txt同個路徑的頭文件無需設置),可選
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/mbedtls/include)
#指定用到的系統(tǒng)庫或者NDK庫或者第三方庫的搜索路徑川队,可選力细。
#LINK_DIRECTORIES(/usr/local/lib)
add_library( nodepp
SHARED
src/main/cpp/utils.c
src/main/cpp/xxtea.c
src/main/cpp/mbed_client.c)
target_link_libraries( nodepp
mbedcrypto
mbedx509
mbedtls
log )
首先固额,我們發(fā)現(xiàn)我們上面的例子都是涉及到一個C++文件眠蚂,那么我們實際的項目不可能只有一個C++文件,所以我們首先要改變CMakeLists.txt文件对雪,如下 :
add_library( HelloNDK
SHARED
src/main/cpp/HelloNDK.c
src/main/cpp/HelloJNI.c)
簡單吧河狐,簡單明了,但是這里要注意的是,你在寫路徑的時候一定要注意當前的CMakeLists.txt在項目中的位置馋艺,上面的路徑是相對于CMakeLists.txt 寫的栅干。
大家會發(fā)現(xiàn),我們上面這樣寫捐祠,由于只有一個CMakeLists.txt文件碱鳞,所以我們會把所有的C/C++文件編譯成一個so庫,這是很不合適的踱蛀,這里我們就試著學學怎么編譯出多個so庫窿给。
先放上我的項目文件夾結(jié)構(gòu)圖:
然后看看我們每個CMakeLists.txt文件是怎么寫的:
one文件夾內(nèi)的CMakeLists.txt文件的內(nèi)容:
ADD_LIBRARY(one-lib SHARED one-lib.c)
target_link_libraries(one-lib log)
two文件夾內(nèi)的CMakeLists.txt文件的內(nèi)容:
ADD_LIBRARY(two-lib SHARED two-lib.c)
target_link_libraries(two-lib log)
app目錄下的CMakeLists.txt文件的內(nèi)容
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
add_library( HelloNDK
SHARED
src/main/cpp/HelloNDK.c
src/main/cpp/HelloJNI.c)
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
target_link_libraries(HelloNDK log)
ADD_SUBDIRECTORY(src/main/cpp/one)
ADD_SUBDIRECTORY(src/main/cpp/two)
通過以上的配置我們可以看出CMakeLists.txt 文件的配置是支持繼承的,所以我們在子配置文件中只是寫了不同的特殊配置項的配置,最后在最上層的文件中配置子配置文件的路徑即可率拒,現(xiàn)在編譯項目崩泡,我們會在 <項目目錄>\app\build\intermediates\cmake\debug\obj\armeabi 下面就可以看到生成的動態(tài)鏈接庫。而且是三個動態(tài)鏈接庫.
3.3 更改動態(tài)鏈接庫生成的目錄
我們是不是發(fā)現(xiàn)上面的so庫的路徑太深了猬膨,不好找角撞,沒事,可以配置勃痴,我們只需要在頂層的CMakeLists.txt文件中加入下面這句就可以了
設置生成的so動態(tài)庫最后輸出的路徑
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI})
然后我們就可以在app/src/main下看到jniLibs目錄谒所,在其中看到我們的動態(tài)鏈接庫的文件夾和文件(這里直接配置到了系統(tǒng)默認的路徑,如果配置到其他路徑需要在gradle文件中使用jinLibs.srcDirs = ['newDir']進行指定)沛申。
四劣领、正文第三章:Cmakelist綜合運用
4.1 native-lib文件調(diào)用一個第三方庫
第三方庫是被編譯成so庫直接調(diào)用的,配置so庫的時候铁材,對應的頭文件也要添加上
(1)指定頭文件目錄:
(2)添加庫:
(3)鏈接到本地庫:
譬如加載一個FFMPEG模塊
include_directories(${pathToFFMPEG}/include)
#添加ffmpeg對應的頭文件目錄,${pathToFFMPEG}為前面配置過的路徑,可以替換為include_directories(E:/ffmpeg/include)這種路徑格式
add_library( ffmpeg
SHARED
IMPORTED)
#添加庫文件尖淘,實際上就是引入so文件,IMPORT代表從第三方引入的意思
set_target_properties( ffmpeg
PROPERTIES IMPORTED_LOCATION
${pathToProject}/app/src/main/jniLibs/
${ANDROID_ABI}/libffmpeg.so
#這句話是ffmpeg對應的so文件,so文件是放到JNILibs這個文件夾中
target_link_libraries( $\{log-lib}
native-lib
ffmpeg)
#為native-lib加載ffmpeg庫.
4.2使用兩個依賴庫,然后生成兩個工具庫
4. 主要有: 添加庫的數(shù)目著觉,C/C++文件數(shù)目德澈,生成so庫的數(shù)目
五、正文第四章:Cmakelist使用常見問題
5.1 missing and no known rule to make it 問題
很蛋疼固惯,就是一個很簡單的.so路徑不對梆造。但是路徑放哪兒對呢,要看項目的build.gradle中設置的lib文件夾在哪兒葬毫。當你沒有設置時镇辉,默認是jniLibs文件夾,所以要把so放在這里面贴捡,但是如果你設置了忽肛,那就要把文件夾放在那個指定的目錄下面,就是下面這個設置。
sourceSets {
main {
jniLibs.srcDirs = ["libs"]
}
}
參考文章:
CMAKE手冊 - 作業(yè)部落 Cmd Markdown 編輯閱讀器 https://www.zybuluo.com/khan-lau/note/254724