前言
公司有个C/C++/ObjC的底层算法库需要在IOS上使用,做了一番调查之后决定将其编成framework的形式(至于IOS上静态库、动态库和framework的区别,网上已经有很多文章了,这里就不再累述了)。
提醒:底下的大部分链接都需要翻墙访问。
正文
ios-cmake生成framework
由于项目一直是用CMake来管理,所以找到了ios-cmake生成framework的方法,在Github上就有:用cmake生成ios framework库。我使用的是其中的第二种方法,也就是使用这里的ios.toolchain.cmake进行编译。
其中困扰了很久的问题就是如何在framework/Headers中保留原有项目的目录结构,找了很久,提出这个问题的人不少,但解决方案不合适,比如这里的:iOS框架标题不保留文件夹层次结构。
一开始我采用了shell脚本手动移动文件的方法来达成需求,但这样对于一个大型项目来说显然是很low的,一旦文件目录更改,就得同时修改CMakeLists.txt和shell脚本,改的越多,出错就越容易。所以还是得找到一个可以在CMake里面实现的方法,最终被我找到了,如果急着解决,可以看这里:一个2010年的帖子。不着急的话,就看一下我在底下写的一个小例子吧。
项目结构和初步的CMakeLists.txt
如图所示,源文件和头文件按功能模块分目录,都在src底下,src/CMakeLists.txt负责编译这些文件,顶层目录有三个文件:
- build.sh: 编译用的脚本
- CMakeLists.txt:顶层CMakeLists.txt,可以用来编译外部的文件,这里只用来add_subdirectory(src)
- ios.toolchain.cmake:直接从上面所说的网址上clone下来的
src/CMakeLists.txt
主要部分是“用cmake生成ios framework库”这里提到的,而在framework/Headers保留目录结构的关键则是:MACOSX_PACKAGE_LOCATION
src/CMakeLists.txt内容如下所示,备注应该挺清楚的了的吧。
cmake_minimum_required(VERSION 3.0)
set(CMAKE_CXX_STANDARD 11)
# 在这里所指示的目录将会成为最终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}
)
# 这里链接了第三方库:opencv.framework和几个系统库
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})
# 通过识别".h"子字符串的方式得出一个文件是否是头文件,所以.h和.hpp文件都会被找到
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) # 得出最后一个"/"的位置pos
string(SUBSTRING ${hfile} 0 ${pos} dir)
message(STATUS "subDir: ${dir}")
# MACOSX_PACKAGE_LOCATION关键字可以将文件复制到特定的路径中,在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
解释:最终编译出来的文件都在build-ios目录下,cmake的编译选项在leetal/ios-cmake中有详细介绍。
最终生成的framework应该是在Debug-iphoneos或Release-iphoneos目录下。
对了,这个也是我:
yeRuiHuan
网友评论