iOS元件二進位制方案之prepare_command實現

語言: CN / TW / HK

一、背景

  1. 目前不方便在主工程裡使用原始碼聯調
  2. 目前每一個基礎庫依賴外部的工具靜態庫非常困難或者是引進進來工程角色越來越不清晰,架構規劃未來會越來越不好劃分

二、入門要求

  1. cocoapods組成以及核心模組工作原理
  2. pod update 和 pod install 原理
  3. 簡單瞭解ruby語法

三、期望目標

  1. Pod update時對已經push到spec倉庫的pod庫可以通過tag或者全域性ruby環境變數對區分是載入framework或者是原始碼;

四、行業方案彙總

方式1:

pod repo push 時,我們可以釋出兩套podspec,eg: ABC.podspec 和 ABC_Binary.podspec 成為雙podspec方案;

方式2:

CocoaPods提供了針對podspec的預執行指令碼,prepare_command(戳我進官網)命令,該命令可以指定相應的指令碼在pod install時去執行,那麼我們就可以將編譯打包的指令碼放入spec檔案中,從而完成延遲打包

A bash script that will be executed after the Pod is downloaded. This command can be used to create, delete and modify any file downloaded and will be ran before any paths for other file attributes of the specification are collected.

此命令可用於建立,刪除和修改下載的任何檔案,並且在收集規範的其他檔案屬性的任何路徑之前將ran,😄😄😄 似乎發現了新大陸。

注意:如果是:path方式引入這個指令碼不起作用 (官方有具體的描述,我的理解是本地這樣操作是冗餘的)

擴充套件prepare_command 使用方式

格式:

``` s.prepare_command = '指令碼型別 指令碼路徑'

s.prepare_command = <<-CMD ... 指令 CMD ```

例子如下:

``` s.prepare_command = 'ruby build_files.rb'

s.prepare_command = '/bin/bash build_files.sh'

s.prepare_command = '/bin/bash build_files.sh'

s.prepare_command = <<-CMD

                    linux unix各種執行指令,eg:操作檔案

CMD ```

這種方式執行之前需要執行以下指令刪除pod庫在本地快取以及工程快取,我這裡比較強暴點😄,具體如下:

``` pod cache clean --all

rm -rf ~/Library/Caches/CocoaPods ```

方式3:

通過cocoapods外掛 cocoapods-binary 實現

第一步:sudo gem install cocoapods-binary

第二步:例如如下podfile的寫法:

``` plugin 'cocoapods-binary'

use_frameworks!

all_binary!

target "HP" do

pod "ExpectoPatronum", :binary => true

end ```

方案4:

目前還有一種方式,僅限於利用xcode打包時保留的DWARF除錯資訊臨時還原出原始碼,具體可以參考iOS元件二進位制原始碼除錯熱切換方案,但是跟我們這次目標不符合,感興趣也可以瞭解😄;

調研方案對比

| 方案 | 優點 | 缺點 | | -------------------- | -------------------------------------------------------- | ------------------------------------------ | | 雙podspec方案 | 加速pod 匯入的速度 | 需要佔用部分服務資源 | | prepare_command 延遲打包 | 釋出簡單,只需根據ruby環境變數確定是否需要呼叫prepare_command的frmaework打包指令碼即可 | 因為這種方式是拉取程式碼,延遲決定是否打framework形式匯入,相比第一種時間會長 | | cocoapods-binary | 外部使用方做的時候過多 | |

五、demo實踐

實現步驟:

方案:demo使用的prepare_command 延遲打包方式打包,這裡採用的.a靜態庫的形式打包

原始碼倉庫 dev分支

Spec倉庫

第一步

熟悉ABC.podspec檔案,如何分別區分原始碼和二進位制庫,以及二進化的話在什麼時機和如何打包靜態庫 ```

Be sure to run `pod lib lint ABC.podspec' to ensure this is a

valid spec before submitting.

Any lines starting with a # are optional, but their use is encouraged

To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html

Pod::Spec.new do |s|   s.name             = 'ABC'   s.version          = '0.1.0'   s.summary          = 'A short description of ABC.'

This description is used to generate tags and improve search results.

* Think: What does it do? Why did you write it? What is the focus?

* Try to keep it short, snappy and to the point.

* Write the description between the DESC delimiters below.

* Finally, don't worry about the indent, CocoaPods strips it!

s.description      = <<-DESC TODO: Add long description of the pod here.   DESC

s.homepage         = 'https://github.com/葛高召/ABC'   # s.screenshots     = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'   s.license          = { :type => 'MIT', :file => 'LICENSE' }   s.author           = { 'xxx' => '[email protected]' }   s.source           = { :git => '[email protected]:GE-GAO-ZHAO/ComommentSpecBinarySDK.git', :tag => s.version.to_s }

# s.social_media_url = 'https://twitter.com/'

s.ios.deployment_target = '9.0'

if s.version.to_s.include?'Binary' or ENV['IS_BINARY']      puts '-------------------------------------------------------------------'      puts 'Notice:ABC is binary now'      puts '-------------------------------------------------------------------'      s.prepare_command = '/bin/bash build_lib.sh'      s.ios.vendored_frameworks= 'PodProducts/.framework'    else      puts '-------------------------------------------------------------------'      puts 'Notice:ABC is source code now'      puts '-------------------------------------------------------------------'      s.static_framework = false      s.source_files = 'ABC/Classes//'    end end ```

第二步

具體靜態庫打包指令碼如下(僅做參考) ```

!/bin/sh

Script.sh

ABC

Created by 葛高召 on 2022/1/6.

Copyright © 2022 葛高召. All rights reserved.

echo ///                        /// echo /// 🚀開始延遲編譯二進位制庫🚀  /// echo ///                       ///

read_dir() {     for file in ls $1 do if [ -d $1"/"$file ]     then read_dir $1"/"$file     else echo $1"/"$file     fi     done }

echo ======😂😂😂目錄資訊😂😂😂========= CURRENT_DIR1=$(cd dirname $0; pwd) read_dir $CURRENT_DIR1 echo ======😂😂😂目錄資訊😂😂😂=========

workspace名、scheme名字

PROJECT_NAME='ABC' BINARY_NAME="${PROJECT_NAME}"

進入工程根目錄

cd Example

framework路徑

INSTALL_DIR=../PodProducts if [ -d ${INSTALL_DIR} ];then     echo "移除framework快取  $INSTALL_DIR"     rm -rf ${INSTALL_DIR} else     echo "建立framework路徑  $INSTALL_DIR"     mkdir -p $INSTALL_DIR fi

編譯場地

BUILD_PATH="${CURRENT_DIR1}/build" RE_OS="Release-iphoneos" RE_SIMULATOR="Release-iphonesimulator" DEVICE_DIR_FOLDER="${BUILD_PATH}/${RE_OS}" SIMULATOR_DIR_FOLDER="${BUILD_PATH}/${RE_SIMULATOR}" DEVICE_DIR="${DEVICE_DIR_FOLDER}/${BINARY_NAME}.framework" SIMULATOR_DIR="${SIMULATOR_DIR_FOLDER}/${BINARY_NAME}.framework"

echo ======😂😂😂編譯場地資訊😂😂😂========= CURRENT_DIR2=$(cd dirname $0; pwd) echo "CURRENT_DIR: ${CURRENT_DIR2}" echo "BUILD_PATH: ${BUILD_PATH}" echo "DEVICE_DIR_FOLDER: ${DEVICE_DIR_FOLDER}" echo "SIMULATOR_DIR_FOLDER: ${SIMULATOR_DIR_FOLDER}" echo "DEVICE_DIR: ${DEVICE_DIR}" echo "SIMULATOR_DIR: ${SIMULATOR_DIR}" echo ======😂😂😂編譯場地資訊😂😂😂========

分別編譯模擬器和真機的Framework

xcodebuild -configuration "Release" -workspace "${PROJECT_NAME}.xcworkspace" -scheme "${BINARY_NAME}" ONLY_ACTIVE_ARCH=NO MACH_O_TYPE="staticlib" -sdk iphoneos CONFIGURATION_BUILD_DIR="${DEVICE_DIR_FOLDER}" clean build xcodebuild -configuration "Release" -workspace "${PROJECT_NAME}.xcworkspace" -scheme "${BINARY_NAME}" ONLY_ACTIVE_ARCH=NO MACH_O_TYPE="staticlib" ARCHS='i386 x86_64' VALID_ARCHS='i386 x86_64' -sdk iphonesimulator CONFIGURATION_BUILD_DIR="${SIMULATOR_DIR_FOLDER}" clean build

echo ======😂😂😂目錄資訊😂😂😂========= read_dir ../ echo ======😂😂😂目錄資訊😂😂😂=========

if [ -d "${DEVICE_DIR}/" ];then     echo "exist ${DEVICE_DIR}" else     echo "termination | reason: not exist ${DEVICE_DIR}"     exit fi

if [ -d "${SIMULATOR_DIR}/" ];then     echo "exist ${SIMULATOR_DIR}" else     echo "termination | reason: not exist ${SIMULATOR_DIR}"     exit fi

合成fat庫

INSTALL_LIB_DIR=${INSTALL_DIR}/${BINARY_NAME}.framework if [ -d "${INSTALL_LIB_DIR}" ] then rm -rf "${INSTALL_LIB_DIR}" fi

mkdir -p "${INSTALL_LIB_DIR}" cp -a "${DEVICE_DIR}/" "${INSTALL_LIB_DIR}/" lipo -create "${DEVICE_DIR}/${BINARY_NAME}" "${SIMULATOR_DIR}/${BINARY_NAME}" -output "${INSTALL_LIB_DIR}/${BINARY_NAME}"

刪除編譯產物

rm -rf $BUILD_PATH

echo ///                        /// echo /// 🚀完成延遲編譯二進位制庫🚀  /// echo ///                       ///

```

第三步

整體入口指令碼,在podfile同級目錄放入下面的指令碼直接執行即可

```

!/bin/sh

Script.sh

ABC

Created by 葛高召 on 2022/1/6.

Copyright © 2022 葛高召. All rights reserved.

提示匯入的pod庫形式

read -p "請輸入安裝的形式? 是:1(二進位制) , 否:0(原始碼) " binary

清除pod快取

pod cache clean --all rm -rf ~/Library/Caches/CocoaPods

移除Podfile.lock 和 Pods/ABC資料夾

work_path=$(pwd) echo 當前目錄:$work_path PodfileLockDir=${work_path}/Podfile.lock PodsDir=${work_path}/Pods

if [[ ! -f "$PodfileLockDir" ]]; then echo "PodfileLockDir資料夾不存在" else rm -f $PodfileLockDir fi

if [[ ! -d "$PodsDir" ]]; then echo "PodsDir資料夾不存在" else rm -rf $PodsDir fi

安裝

case $binary in 0) echo "安裝原始碼庫" pod update --verbose ;; 1) echo "安裝二進位制庫" IS_BINARY=1 pod update --verbose ;; *) echo '輸入錯誤,異常退出' ;; esac

``` 執行根據提示輸入不同的配置,實現匯入二進位制庫或者原始碼庫

六、參考文獻

Podspec****

pod install vs. pod update

script_phases****

How to use CocoaPods plugins****

cocoapods-imy-bin無侵入式

cocopods原始碼分析

七、後語

這裡介紹iOS元件二進化常見的方式,以及使用ruby環境變數和prepare_command在pod install之前通過指令碼提交打好靜態庫方式實現。缺陷是釋出後開發者可以看到podspec檔案實現,其實還是不太好,可以參考我的另一篇文章 https://juejin.cn/post/7159494385204199437 來避免這個問題