前言
公司有個(gè)C/C++/ObjC的底層算法庫(kù)需要在IOS上使用,做了一番調(diào)查之后決定將其編成framework的形式(至于IOS上靜態(tài)庫(kù)后频、動(dòng)態(tài)庫(kù)和framework的區(qū)別沼撕,網(wǎng)上已經(jīng)有很多文章了遭顶,這里就不再累述了)。
提醒:底下的大部分鏈接都需要翻墻訪問(wèn)牛柒。
正文
ios-cmake生成framework
由于項(xiàng)目一直是用CMake來(lái)管理堪簿,所以找到了ios-cmake生成framework的方法,在Github上就有:用cmake生成ios framework庫(kù)皮壁。我使用的是其中的第二種方法椭更,也就是使用這里的ios.toolchain.cmake進(jìn)行編譯。
其中困擾了很久的問(wèn)題就是如何在framework/Headers中保留原有項(xiàng)目的目錄結(jié)構(gòu)蛾魄,找了很久虑瀑,提出這個(gè)問(wèn)題的人不少湿滓,但解決方案不合適,比如這里的:iOS框架標(biāo)題不保留文件夾層次結(jié)構(gòu)舌狗。
一開(kāi)始我采用了shell腳本手動(dòng)移動(dòng)文件的方法來(lái)達(dá)成需求叽奥,但這樣對(duì)于一個(gè)大型項(xiàng)目來(lái)說(shuō)顯然是很low的,一旦文件目錄更改痛侍,就得同時(shí)修改CMakeLists.txt和shell腳本朝氓,改的越多,出錯(cuò)就越容易主届。所以還是得找到一個(gè)可以在CMake里面實(shí)現(xiàn)的方法赵哲,最終被我找到了,如果急著解決君丁,可以看這里:一個(gè)2010年的帖子枫夺。不著急的話,就看一下我在底下寫(xiě)的一個(gè)小例子吧谈截。
項(xiàng)目結(jié)構(gòu)和初步的CMakeLists.txt
如圖所示筷屡,源文件和頭文件按功能模塊分目錄涧偷,都在src底下簸喂,src/CMakeLists.txt負(fù)責(zé)編譯這些文件,頂層目錄有三個(gè)文件:
- build.sh: 編譯用的腳本
- CMakeLists.txt:頂層CMakeLists.txt燎潮,可以用來(lái)編譯外部的文件喻鳄,這里只用來(lái)add_subdirectory(src)
- ios.toolchain.cmake:直接從上面所說(shuō)的網(wǎng)址上clone下來(lái)的
src/CMakeLists.txt
主要部分是“用cmake生成ios framework庫(kù)”這里提到的,而在framework/Headers保留目錄結(jié)構(gòu)的關(guān)鍵則是:MACOSX_PACKAGE_LOCATION
src/CMakeLists.txt內(nèi)容如下所示确封,備注應(yīng)該挺清楚的了的吧除呵。
cmake_minimum_required(VERSION 3.0)
set(CMAKE_CXX_STANDARD 11)
# 在這里所指示的目錄將會(huì)成為最終framework/Headers底下的目錄
set(dir1 dir1/test.cc dir1/test.h)
set(dir1_ios dir1/ios/test_ios.cc dir1/ios/test_ios.h)
set(dir2_1 dir2/subDir1/test2.cc dir2/subDir1/test2.h)
set(dir2_2 dir2/subDir2/test3.cc dir2/subDir2/test3.h)
set(dir3 dir3/test.cc dir3/test.h)
add_library(testFramework
${dir1}
${dir1_ios}
${dir2_1}
${dir2_2}
${dir3}
)
# 這里鏈接了第三方庫(kù):opencv.framework和幾個(gè)系統(tǒng)庫(kù)
set_target_properties(testFramework PROPERTIES
#-F后面接著的是opencv2.framework所在目錄
LINK_FLAGS "-W1,-F${CMAKE_CURRENT_SOURCE_DIR}/3party/opencv/ios"
)
target_link_libraries(testFramework
"-framework opencv2"
"-framework Foundation"
"-framework CoreVideo"
"-framework coreml"
)
# 列出要編譯的所有源文件和頭文件
set(SRC_FILES
${dir1}
${dir1_ios}
${dir2_1}
${dir2_2}
${dir3}
)
# 從SRC_FILES列表中找出所有頭文件,并放入INCLUDE_FILES變量中
set(INCLUDE_FILES "")
foreach(file ${SRC_FILES})
# 通過(guò)識(shí)別".h"子字符串的方式得出一個(gè)文件是否是頭文件爪喘,所以.h和.hpp文件都會(huì)被找到
string(FIND ${file} ".h" pos REVERSE)
if(NOT ${pos} MATCHES "-1")
message(STATUS "header file: ${file}")
list(APPEND INCLUDE_FILES ${file})
endif()
endforeach(file)
set(INCLUDE_FILES ${INCLUDE_FILES} CACHE INTERNAL "List of include files" FORCE)
set_xcode_property(testFramework GCC_GENERATE_DEBUGGING_SYMBOLS YES "ALL")
set_target_properties(testFramework PROPERTIES
FRAMEWORK TRUE
FRAMEWORK_VERSION A
MACOSX_FRAMEWORK_IDENTIFIER cn.yrh.test
VERSION 1.0.0
SOVERSION 1.0.0
#PUBLIC_HEADER ${INCLUDE_FILES} # 使用另一種方法生成Headers/颜曾,所以PUBLIC_HEADER就不用了
)
# 將INCLUDE_FILES變量中的所有文件按其路徑名放入testFramework.framework/Headers中
foreach(hfile ${INCLUDE_FILES})
# 截取出hfile變量中的路徑
string(FIND ${hfile} "/" pos REVERSE) # 得出最后一個(gè)"/"的位置pos
string(SUBSTRING ${hfile} 0 ${pos} dir)
message(STATUS "subDir: ${dir}")
# MACOSX_PACKAGE_LOCATION關(guān)鍵字可以將文件復(fù)制到特定的路徑中,在IOS framework中秉剑,
# 就是<name>.framework/
set_property(SOURCE ${hfile} PROPERTY
MACOSX_PACKAGE_LOCATION Headers/${dir})
endforeach(hfile)
include_directories(
.
${CMAKE_CURRENT_SOURCE_DIR}/3party/opencv/ios/opencv2.framework
)
build.sh
#!/bin/bash
rm -r build-ios
mkdir build-ios
cd build-ios
cmake .. -G Xcode -DCMAKE_TOOLCHAIN_FILE=../ios.toolchain.cmake -DPLATFORM=OS64 -DARCHS=arm64 -DDEPLOYMENT_TARGET=12.2 -DENABLE_STRICT_TRY_COMPILE=TRUE -DENABLE_VISIBILITY=TRUE -DCMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM=UBX9CH9GDX
cmake --build . --config Debug
#cmake --build . --config Release
解釋:最終編譯出來(lái)的文件都在build-ios目錄下泛豪,cmake的編譯選項(xiàng)在leetal/ios-cmake中有詳細(xì)介紹。
最終生成的framework應(yīng)該是在Debug-iphoneos或Release-iphoneos目錄下侦鹏。
對(duì)了诡曙,這個(gè)也是我: