[关闭]
@yanzhiwei147 2019-09-17T12:04:24.000000Z 字数 11039 阅读 4501

SDK开发者引入包依赖管理开发流程

CocoaPods tips SDK


ps:本文以IMSDK为例,基于SDK目录结构规范cocoapods 0.36.4开发
ps2:本文成果传送门
ps3:本文阅读储备知识:Cocoapods三种语法git操作语义化版本
ps4:本文严格限定SDK使用第三方库版本
ps5:本文涉及到git远端操作的均使用HTTP形式,使用SSH形式请移步此处完成配置

安装CocoaPods

配置CocoaPods

  1. $ pod repo add sdp_repo http://git.sdp.nd/cocoapods/spec.git

创建SDK工程

本步骤基于xcode6.1.1

配置Podfile

  1. $ pod init
  1. source 'https://github.com/CocoaPods/Specs.git'
  2. source 'http://git.sdp.nd/cocoapods/spec.git'
  1. # 只能安装在可运行target上(单元测试以及App等),其他比如静态库不需要链接因而决不能安装
  2. pod 'UCSDK', '~> 0.3.1'
  1. source 'https://github.com/CocoaPods/Specs.git'
  2. source 'http://git.sdp.nd/cocoapods/spec.git'
  3. # 注意只能安装可运行的target
  4. target 'IMSDKTests' do
  5. pod 'UCSDK', '~> 0.3.1'
  6. end

安装依赖

  1. $ pod install

工程配置

静态库的target(此处为IMSDK)没有收到安装依赖的益处,因此我们需要配置一些项目(IMSDK.xcodeproj上的IMSDK必须能不依赖workspace进行编译):

  1. "$(SRCROOT)/Pods/Headers/Public"

经过上面搜索路径的配置,现在Xcode就能正常找到所有依赖的第三方包的头文件

开发

SDK生成

使用SDK顶层目录下的build.sh(内容见附录)进行打包,命令如下:

  1. // 更多功能使用-h进行查看
  2. $ sh build.sh -r IMSDKBundle

打包完毕后会在顶层目录下生成一个output目录,生成的framework以及resources都在该文件夹中。
output目录结构如下:

  1. ├── framework
  2.    ├── Headers -> Versions/Current/Headers
  3.    ├── IMSDK -> Versions/Current/IMSDK
  4.    └── Versions
  5. └── resources
  6. └── IMSDKBundle.bundle
  7. 5 directories, 1 file

SDK发布

配置本地podspec

  1. $ pod spec create IMSDK
  1. s.name = "IMSDK"
  2. s.version = "0.0.1"
  3. s.platform = :ios, "6.0"
  4. s.summary = "IMSDK官方版"
  5. s.author = "IMSDK团队"
  6. s.homepage = "http://git.sdp.nd/cocoapods/imsdk"
  7. s.license = "MIT"
  8. s.source = { :git => "http://git.sdp.nd/cocoapods/imsdk.git", :tag => "#{s.version}" }
  9. s.public_header_files = "#{s.name}.framework/Versions/A/**/*.h"
  10. s.preserve_paths = "*.framework"
  11. s.vendored_frameworks = "#{s.name}.framework"
  12. s.resources = "IMSDKBundle.bundle"
  13. s.dependency "MUPFoundation", "~> 0.3.6"
  14. s.dependency "SmartCan", "~> 0.3.4"
  1. $ pod ipc spec IMSDK.podspec >> IMSDK.podspec.json

发布framework

创建framework项目
版本库配置
  1. $ mkdir IMSDK
  2. $ cd IMSDK
  3. $ git init
  1. $ git remote add origin http://git.sdp.nd/cocoapods/imsdk.git
  1. $ touch README.md
  2. $ git add README.md IMSDK.framework IMSDKBundle.bundle IMSDK.podspec
  1. $ git commit -m "提交0.0.1版本发布(二进制库)"
  1. $ git tag -a 0.0.1 -m "tag 0.0.1版本"
  1. // 同步本地branch至远端
  2. $ git push -u origin master
  3. // 把本地库所有tag同步至远端
  4. $ git push --tag

发布podspec

部署podspec
  1. $ pod spec lint --use-libraries --allow-warnings --sources="https://github.com/CocoaPods/Specs.git,http://git.sdp.nd/cocoapods/spec.git" IMSDK.podspec
  1. $ pod repo push sdp_repo IMSDK.podspec --use-libraries --allow-warnings

SDKDemo开发

版本库规范

FAQ

为什么静态库的target不能安装pod依赖?

附录

build.sh脚本

  1. #!/bin/sh
  2. SDK_ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
  3. case $SDK_ROOT_DIR in
  4. *\ * )
  5. echo "您的路径中包含空格,像'make install'是不被支持的."
  6. exit 1
  7. ;;
  8. esac
  9. ###########################################################################
  10. #
  11. # 模拟器支持架构
  12. SIMULATOR_ARCHS="i386 x86_64"
  13. #
  14. # 真机支持架构
  15. IPHONEOS_ARCHS="armv7 armv7s arm64"
  16. #
  17. # 编译使用的SDK
  18. SDK_NAME="iphoneos8.1"
  19. #
  20. # 编译产品路径,指定指定build目录(默认为脚本所在目录下的build)
  21. BUILT_PRODUCTS_DIR="${SDK_ROOT_DIR}/build"
  22. #
  23. # 当前framework版本,一般使用字母递增来表示
  24. FRAMEWORK_VERSION="A"
  25. #
  26. ###########################################################################
  27. function show_version() {
  28. echo "version: 1.1"
  29. echo "updated date: 2015-04-20"
  30. }
  31. function show_usage() {
  32. echo "Usage(暂时不支持长选项):\n"
  33. echo "`printf %-16s " $ $0"` argument\n"
  34. echo "Description:"
  35. echo "`printf %-16s ` [-h|--help] 显示帮助信息"
  36. echo "`printf %-16s ` [-v|-V|--version] 显示版本"
  37. echo "`printf %-16s ` [-c|--configuration ... ] 指定编译配置"
  38. echo "`printf %-16s ` [-p|--project ... ] 指定编译工程"
  39. echo "`printf %-16s ` [-P|--frameworkproduct ... ] 指定生成framework产品名"
  40. echo "`printf %-16s ` [-t|--frameworktarget ... ] 指定需要编译framework的target名"
  41. echo "`printf %-16s ` [-r|--resourcetarget ... ] 指定资源编译target名"
  42. }
  43. # Call this when there is an error. This does not return.
  44. function die() {
  45. echo ""
  46. echo "FATAL: $*" >&2
  47. exit 1
  48. }
  49. # 工程名,用以指定需要编译的project
  50. PROJECT_NAME=""
  51. # target名,用以指定需要编译的target,默认与工程名一致
  52. TARGET_NAME=""
  53. # 产品名,用以指定需要编译的target,默认与target名一致
  54. PRODUCT_NAME=""
  55. # 配置,用以指定编译代码的配置
  56. CONFIGURATION=""
  57. # 资源名
  58. RESOURCE_NAME=""
  59. # 编译所有target标识
  60. ALLTARGETS_FLAG=0
  61. # 参数列表
  62. while getopts ":hvVac:p:P:t:r:" OPTNAME
  63. do
  64. case "$OPTNAME" in
  65. "h")
  66. show_usage && exit
  67. ;;
  68. "v")
  69. show_version && exit
  70. ;;
  71. "V")
  72. show_version && exit
  73. ;;
  74. "c")
  75. CONFIGURATION=$OPTARG
  76. ;;
  77. "p")
  78. PROJECT_NAME=$OPTARG
  79. ;;
  80. "P")
  81. PRODUCT_NAME=$OPTARG
  82. ;;
  83. "t")
  84. TARGET_NAME=$OPTARG
  85. ;;
  86. "r")
  87. RESOURCE_NAME=$OPTARG
  88. ;;
  89. "?")
  90. show_usage && exit
  91. ;;
  92. ":")
  93. echo "选项$OPTARG缺少输入参数"
  94. die
  95. ;;
  96. *)
  97. # Should not occur
  98. echo "处理选项过程发生未知错误"
  99. die
  100. ;;
  101. esac
  102. done
  103. XCODEPROJ_SEARCH_RESULT=`find . -name "*.xcodeproj" -d 1`
  104. if [ -n "${XCODEPROJ_SEARCH_RESULT}" ]; then
  105. FILENAME="`basename ${XCODEPROJ_SEARCH_RESULT}`"
  106. XCODEPROJ_NAME="${FILENAME%.*}"
  107. fi
  108. test -n "${CONFIGURATION}" || CONFIGURATION="Release"
  109. test -n "${PROJECT_NAME}" || PROJECT_NAME="${XCODEPROJ_NAME}"
  110. test -n "${TARGET_NAME}" || TARGET_NAME="${PROJECT_NAME}"
  111. test -n "${PRODUCT_NAME}" || PRODUCT_NAME="${TARGET_NAME}"
  112. test -n "${RESOURCE_NAME}" || RESOURCE_NAME=""
  113. # echo "${CONFIGURATION}\n${PROJECT_NAME}\n${PRODUCT_NAME}\n${TARGET_NAME}\n${RESOURCE_NAME}\n"
  114. set -e
  115. set +u
  116. # 避免递归调用
  117. if [[ $SF_MASTER_SCRIPT_RUNNING ]]
  118. then
  119. exit 0
  120. fi
  121. set -u
  122. export SF_MASTER_SCRIPT_RUNNING=1
  123. DEVELOPER=`xcode-select -print-path`
  124. if [ ! -d "$DEVELOPER" ]; then
  125. echo "xcode路径没有被设置正确,$DEVELOPER不存在"
  126. echo "运行"
  127. echo "sudo xcode-select -switch <xcode path>"
  128. echo "来进行默认安装:"
  129. echo "sudo xcode-select -switch /Applications/Xcode.app/Contents/Developer"
  130. exit 1
  131. fi
  132. SDK_IPHONEOS="iphoneos"
  133. SDK_IPHONESIMULATOR="iphonesimulator"
  134. # The following conditionals come from
  135. # https://github.com/kstenerud/iOS-Universal-Framework
  136. if [[ "$SDK_NAME" =~ ([A-Za-z]+) ]]
  137. then
  138. SF_SDK_PLATFORM=${BASH_REMATCH[1]}
  139. else
  140. echo "Could not find platform name from SDK_NAME: $SDK_NAME"
  141. exit 1
  142. fi
  143. if [[ "$SDK_NAME" =~ ([0-9]+.*$) ]]
  144. then
  145. SF_SDK_VERSION=${BASH_REMATCH[1]}
  146. else
  147. echo "Could not find sdk version from SDK_NAME: $SDK_NAME"
  148. exit 1
  149. fi
  150. if [[ "$SF_SDK_PLATFORM" = "iphoneos" ]]
  151. then
  152. SF_OTHER_PLATFORM=iphonesimulator
  153. else
  154. SF_OTHER_PLATFORM=iphoneos
  155. fi
  156. function buildFramework() {
  157. xcodebuild -project ./$PROJECT_NAME.xcodeproj -target $TARGET_NAME -sdk $SDK_IPHONEOS -configuration $CONFIGURATION ARCHS="${IPHONEOS_ARCHS}" build
  158. xcodebuild -project ./$PROJECT_NAME.xcodeproj -target $TARGET_NAME -sdk $SDK_IPHONESIMULATOR -configuration $CONFIGURATION ARCHS="${SIMULATOR_ARCHS}" build
  159. }
  160. function buildBundle() {
  161. xcodebuild -project ./$PROJECT_NAME.xcodeproj -target $RESOURCE_NAME -sdk $SDK_IPHONEOS -configuration $CONFIGURATION ARCHS="${IPHONEOS_ARCHS}" build
  162. }
  163. function copyFramework() {
  164. # prepare_framework
  165. mkdir -p "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/Versions/${FRAMEWORK_VERSION}/Headers"
  166. # Link the "Current" version to "${FRAMEWORK_VERSION}"
  167. ln -sfh ${FRAMEWORK_VERSION} "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/Versions/Current"
  168. ln -sfh Versions/Current/Headers "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/Headers"
  169. ln -sfh "Versions/Current/${PRODUCT_NAME}" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/${PRODUCT_NAME}"
  170. # The -a ensures that the headers maintain the source modification date so that we don't constantly
  171. # cause propagating rebuilds of files that import these headers.
  172. TARGET_BUILD_DIR="${BUILT_PRODUCTS_DIR}/${CONFIGURATION}-${SDK_IPHONEOS}"
  173. cp -a "${TARGET_BUILD_DIR}/include/${PRODUCT_NAME}/" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/Versions/${FRAMEWORK_VERSION}/Headers"
  174. # compile_framework
  175. SF_TARGET_NAME=${PROJECT_NAME}
  176. SF_EXECUTABLE_Name="lib${SF_TARGET_NAME}.a"
  177. SF_WRAPPER_NAME="${SF_TARGET_NAME}.framework"
  178. PLATFORM_EXECUTABLE_PATH="${BUILT_PRODUCTS_DIR}/${CONFIGURATION}-${SF_SDK_PLATFORM}/${SF_EXECUTABLE_Name}"
  179. OTHER_PLATFORM_EXECUTABLE_PATH="${BUILT_PRODUCTS_DIR}/${CONFIGURATION}-${SF_OTHER_PLATFORM}/${SF_EXECUTABLE_Name}"
  180. OUTPUT_PATH="${BUILT_PRODUCTS_DIR}/${SF_WRAPPER_NAME}/Versions/${FRAMEWORK_VERSION}/${SF_TARGET_NAME}"
  181. # Smash the two static libraries into one fat binary and store it in the .framework
  182. lipo -create "${PLATFORM_EXECUTABLE_PATH}" "${OTHER_PLATFORM_EXECUTABLE_PATH}" -output "${OUTPUT_PATH}"
  183. # Delete temporary folder if exists
  184. FINAL_OUTPUT_PATH="output/framework/${SF_WRAPPER_NAME}"
  185. if [ -d "${FINAL_OUTPUT_PATH}" ]
  186. mkdir -p "${FINAL_OUTPUT_PATH}"
  187. then
  188. rm -dR "${FINAL_OUTPUT_PATH}"
  189. fi
  190. # Copy the binary to the other architecture folder to have a complete framework in both.
  191. cp -a "${BUILT_PRODUCTS_DIR}/${SF_WRAPPER_NAME}" "${FINAL_OUTPUT_PATH}"
  192. }
  193. function copyBundle() {
  194. # Resources path
  195. RESOURCE_BUILD_PATH="${BUILT_PRODUCTS_DIR}/${CONFIGURATION}-${SF_SDK_PLATFORM}"
  196. # Resources name
  197. RESOURCE_PRODUCT_NAME="${RESOURCE_NAME}.bundle"
  198. # Delete temporary folder if exists
  199. FINAL_RESOURCE_OUTPUT_PATH="output/resources/${RESOURCE_PRODUCT_NAME}"
  200. if [ -d "${FINAL_RESOURCE_OUTPUT_PATH}" ]
  201. mkdir -p "${FINAL_RESOURCE_OUTPUT_PATH}"
  202. then
  203. rm -dR "${FINAL_RESOURCE_OUTPUT_PATH}"
  204. fi
  205. cp -a "${RESOURCE_BUILD_PATH}/${RESOURCE_PRODUCT_NAME}" "${FINAL_RESOURCE_OUTPUT_PATH}"
  206. }
  207. test -z "${PROJECT_NAME}" || (buildFramework && copyFramework)
  208. test -z "${RESOURCE_NAME}" || (buildBundle && copyBundle)

定制化gitignore文件

按照我们对git的版本控制的需要,我们写出如下的的.gitignore

  1. # Xcode
  2. #
  3. build/
  4. output/
  5. .DS_Store
  6. .svn
  7. *.pbxuser
  8. !default.pbxuser
  9. *.mode1v3
  10. !default.mode1v3
  11. *.mode2v3
  12. !default.mode2v3
  13. *.perspectivev3
  14. !default.perspectivev3
  15. xcuserdata
  16. *.xccheckout
  17. *.moved-aside
  18. DerivedData
  19. *.hmap
  20. *.ipa
  21. *.xcuserstate
  22. # CocoaPods
  23. #
  24. # We recommend against adding the Pods directory to your .gitignore. However
  25. # you should judge for yourself, the pros and cons are mentioned at:
  26. # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
  27. #
  28. #Pods/

别名git命令

每次使用commit、checkout等冗长的命令想抓狂,可以尝试一下如下的别名设置( ~/.gitconfig),你会有svn的那种简写畅快感:

  1. [alias]
  2. st = status
  3. ci = commit
  4. di = diff
  5. l = log --oneline --decorate -12 --color
  6. ll = log --oneline --decorate --color
  7. lc = log --graph --color
  8. co = checkout
  9. br = branch
  10. rb = rebase
  11. dci = dcommit
  12. sbi = submodule init
  13. sbu = submodule update
  14. sbp = submodule foreach git pull
  15. sbc = submodule foreach git co master

现在一些命令就可以这么用:

  1. // 等同于 git commit -m "我是提交注释"
  2. $ git ci -m "我是提交注释"
  3. // 等同于 git status
  4. $ git st
  5. // 等同于 git diff
  6. $ git di

部分Cocoapods技巧

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注