iOS 組件二進制化方案--(一)

背景

隨著業(yè)務(wù)的擴展,私有CocoaPod庫和第三方 CocoaPod 庫越來越多,App項目中的文件也越來越多。每次 pod install/update 或提交到 Jenkins 上構(gòu)建的時候,重新編譯的過程需要等待很長時間,這就間接地向我們提出了加快編譯速度的需求。

什么是組件二進制化?

開發(fā)過程中我們使用CocoaPods生成、管理和使用模塊庫、組件庫或業(yè)務(wù)庫,在工程中組件庫或業(yè)務(wù)庫均可以看成一個模塊。二進制化指的是通過編譯把模塊的源碼轉(zhuǎn)換成靜態(tài)庫或動態(tài)庫,以提高該組件在App項目中的編譯速度。

期望效果

1、提交至 Jenkins 的構(gòu)建,采用二進制;
2、二進制文件與源碼倉庫權(quán)限一致;
3、podfile導(dǎo)入時,可在源碼和二進制文件之間切換;

cocoapods-packager 生成二進制文件

cocoapods-packager是 cocoapods 的一個自動化打包插件,可以用來打包生成動態(tài)庫或靜態(tài)庫,如果沒有的話使用以下命令安裝:

sudo gem install cocoapods-packager

部分參數(shù)配置:

//強制覆蓋之前已經(jīng)生成過的二進制庫
--force

//生成靜態(tài).framework
--embedded

//生成靜態(tài).a
--library

//生成動態(tài).framework
--dynamic

//動態(tài).framework是需要簽名的,所以只有生成動態(tài)庫的時候需要這個BundleId
--bundle-identifier

//不包含依賴的符號表,生成動態(tài)庫的時候不能包含這個命令,動態(tài)庫一定需要包含依賴的符號表。
--exclude-deps

//表示生成的庫是debug還是release,默認是
release。--configuration=Debug
--configuration

//表示不使用name mangling技術(shù),pod package默認是使用這個技術(shù)的。如果你的pod庫沒有其他依賴的話,那么不使用這個命令也不會報錯。但是如果有其他依賴,不使用--no-mangle這個命令的話,那么你在工程里使用生成的二進制庫的時候就會報錯:Undefined symbols for architecture x86_64。
--no-mangle

創(chuàng)建靜態(tài)Framework

pod package SAKit.podspec --force --embedded --spec-sources=ssh://git@192.168.6.115:7999/xdwios/saspecs.git,https://github.com/CocoaPods/Specs.git

創(chuàng)建靜態(tài) .a 便是用 --library 替換 --embedded;

pod package SAKit.podspec --force --library --spec-sources=ssh://git@192.168.6.115:7999/xdwios/saspecs.git,https://github.com/CocoaPods/Specs.git

步驟

  1. 代碼庫打tag;
  2. 執(zhí)行 pod package 命令,并將生成的 framework 按指定目錄存放;
  3. 提交代碼并重新打tag(與第1步tag相同),先移除再添加;
  4. 提交版本至 repo 庫;

通過 pod-packager 生成的二進制文件支持的指令集:armv7 armv7s i386 x86_64 arm64

xcodebuild 生成二進制文件

xcodebuild 命令是 Xcode Command Line Tools 的一部分。通過調(diào)用這個命令,可以完成 iOS 工程的編譯,打包和簽名過程。
我們這里用到的是編譯;

命令行執(zhí)行 man xcodebuild 或 xcodebuild -h 可查看參數(shù)配置,常用的如下:

xcodebuild.png

以工程名字為 SATestProject 為例,步驟:
1、編譯

xcodebuild -workspace Example/SATestProject.xcworkspace -scheme SATestProject -configuration Release -verbose -derivedDataPath Library/DerivedDataPath

2、更換目錄

mv Library/DerivedDataPath/Build/Products/Release-iphoneos/SATestProject/libSATestProject.a Library/SATestProject.a

rm -rf Library/DerivedDataPath

3、打tag并提交至repo庫;

當前 xcode 版本 9.0,通過 xcodebuild 編譯默認生成的二進制文件支持的指令集:armv7 arm64
通過 build-setting中設(shè)置 Architectures ,添加 armv7s 后,再編譯生成的 .a 支持的指令集:armv7 arm64 armv7s:

set_archectures.png

經(jīng)測試發(fā)現(xiàn):cocoapods-packager打包生成的.a會將已用到的其他庫的文件打進去;而采用xcodebuild編譯打包則不會;

編輯podfile文件,配置.a文件支持的指令集

以下代碼為通過podfile配置所生成的.a支持的指令集,if判斷是因為Cocoapods升級到0.38或0.39版本后installer_representation.project.targets.each中的project改名了,變成了pods_project,為適配不同版本的cocoapods的設(shè)置

post_install do |installer_representation|

        if defined? installer_representation.project
                installer_representation.project.targets.each do |target|
                        target.build_configurations.each do |config|
                                config.build_settings['ARCHS'] = 'armv7s arm64'
                                config.build_settings['VALID_ARCHS'] = 'armv7s arm64'
                        end
                end
        end

    
        if defined? installer_representation.pods_project
                installer_representation.pods_project.targets.each do |target|
                        target.build_configurations.each do |config|
                                config.build_settings['ARCHS'] = 'armv7s arm64'
                                config.build_settings['VALID_ARCHS'] = 'armv7s arm64'
                        end
        end
        end
end

附:Xcode Build Settings Reference

源碼和二進制之間的切換

podspec 文件

為了編譯快我們可能會使用二進制,但是有時候我們需要切換回源碼進行調(diào)試代碼。我們在pod倉庫中同時存放源碼和生成的二進制庫,然后就是讓CocoaPods在安裝Pod的時候判斷使用源碼還是二進制庫。以 SAKit 為例,導(dǎo)入文件路徑及依賴部分的寫法如下:

#控制安裝 Pod 的時候判斷使用源碼還是二進制庫
$lib = ENV['use_lib']
$lib_name = ENV["#{s.name}_use_lib"]

if $lib || $lib_name
    s.ios.vendored_libraries = "Library/#{s.version}/#{s.name}.a"
    s.source_files = "#{s.name}/Classes/**/*.h"
    #這種是你已經(jīng)打包好了bundle,推薦這種,可以省去每次pod幫你生成bundle的時間
    s.resources = "#{s.name}/Resource/*.bundle", "#{s.name}/Resource/SAKit_Temp.xcassets/*.png"
else
    #源碼
    s.source_files = "#{s.name}/Classes/**/*.{h,m}"
    s.resources = "#{s.name}/Resource/*.bundle", "#{s.name}/Resource/SAKit_Temp.xcassets/*.png"
    s.dependency 'Masonry'
    s.dependency 'MJRefresh'
    s.dependency 'SALocalizable'
    s.dependency 'Aspects'
    s.dependency 'WebViewJavascriptBridge'
    s.dependency 'SAImageCropper'
    s.dependency 'SDWebImage'
    s.dependency 'SAFoundation'
end


#第一次安裝時緩存兩種格式的文件,解決 切換為另一種格式的時候發(fā)現(xiàn)Pod目錄下為空,沒有需要的文件 的問題
s.preserve_paths = "#{s.name}/Classes/**/*","#{s.name}/Assets/*.{png,xib,plist}","Library/#{s.version}/#{s.name}.a"

具體使用可參照以上寫法根據(jù)實際情況對路徑、resource等稍作調(diào)整;

源碼或二進制導(dǎo)入

使用源碼:

直接運行 pod install

所有庫拉取靜態(tài)庫Framework

use_lib=1 pod install

指定 SAKit 使用二進制導(dǎo)入

SAKit_use_lib=1 pod install

.a文件提交控制

因重新生成.a文件時,git會將整個.a重新保存,會導(dǎo)致git倉庫容量被大量占用,因此在生成某一版本.a文件時,因此在之前的所有提交記錄包含此版本.a文件的提交記錄中將其刪除;

通過git-filter-branch實現(xiàn);

git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch path-to-your-remove-file' --prune-empty --tag-name-filter cat -- --all

初步方案

  1. 修改podspec;
  2. 若之前生成過此版本,在包含的提交記錄中將其刪除;
  3. xcodebuild 編譯生成 .a,存放至指定目錄;
  4. 打tag,提交至repo;

2、3兩步可通過執(zhí)行 salib 腳本即可完成,生成的 .a 自動存放在工程根目錄的 Library 文件夾內(nèi);

  • 編譯 podspec 文件同名的 scheme,需在工程根目錄下執(zhí)行;

salib

  • 指定 scheme 編譯,需在工程根目錄下執(zhí)行

salib PINCache

  • 指定 xcworkspace 文件及需要編譯的 scheme,在任何目錄下執(zhí)行都可以

salib Desktop/SAFoundation/Example/SAFileUpload.xcworkspace SAUpload

下面貼的是腳本內(nèi)容:

#!/bin/bash
#
#------------iOS 組件二進制化實現(xiàn)方法--------------

#------------獲取 workspace 與 scheme 路徑---------------
if [ $# == 0 ]; then    #不存在參數(shù)
     
    if [ -e *.podspec ];then  
        
        #存在 podspec 文件
        #采用 podspec 的名字指定 workspace 與需要編譯的 scheme
        project_name=$(basename *.podspec .podspec)  
        workspace_name=Example/$project_name.xcworkspace
        
    else
        echo "ERROR:not find podspec file, please run shell in project root path"
    exit 1
    fi
    
else
    
    if [ -n "$2" ]; then
        #手動指定 workspace 和 scheme
        workspace_name=$1 # workspace name
        project_name=$2 # scheme name
        project_root_path=$(cd `dirname $workspace_name`; cd ../; pwd;)
    else
        #手動指定 scheme
        project_name=$1 # scheme name
        if [ -e Example/*.xcworkspace ];then  #存在 xcworkspace 文件
            workspace_name=Example/*.xcworkspace
        else
            echo "ERROR: not find xcworkspace file, please run shell in project root path"
            exit 1
        fi
    fi
fi

#------------設(shè)定生成二進制對應(yīng)的版本號,配置.a文件存儲路徑---------------
echo "--- Create library for $project_name ---"
echo
echo "please input the version which you want to build:"

read version

#------------配置存儲路徑---------------
if [ -n "$project_root_path" ]; then
    base_path=$project_root_path/Library
else
    base_path=Library
fi

if [ -n "$version" ]; then
    mkdir $base_path/$version
    version_base_path=$base_path/$version
else
    mkdir $base_path/0.1.0
    version_base_path=$base_path/0.1.0
fi


# ------------刪除之前生成.a的提交記錄---------------------------------
if [ -e $version_base_path/lib$project_name.a ];then

    cd $base_path && cd ../
    echo "刪除記錄"
    
    git filter-branch --force  --index-filter "git rm -r --cached --ignore-unmatch $version_base_path/lib$project_name.a" HEAD

    if [ $?  -eq 0 ]; then
        git push --all --force
    else
        echo
        echo "--- BUILD FAILED ---"
        exit 1
    fi

fi

#--------------清空Library———————————————
rm -rf $base_path/*
    
# ------------生成.a文件,并存儲在對應(yīng)路徑下---------------------------------
xcodebuild -workspace $workspace_name -scheme $project_name -configuration Release -verbose -derivedDataPath $version_base_path/DerivedDataPath

if [ $?  -eq 0 ]; then
    mv $version_base_path/DerivedDataPath/Build/Products/Release-iphoneos/$project_name/lib$project_name.a  $version_base_path/lib$project_name.a
    rm -rf $version_base_path/DerivedDataPath

    lipo -info $version_base_path/lib$project_name.a
else
    echo
    echo "--- BUILD FAILED ---"
    exit 1
fi

exit 0

不足

  • 二進制文件存放于源碼庫,污染源碼庫;
  • 每個業(yè)務(wù)都得去修改podspec文件,以配置區(qū)別拉取二進制文件和源碼;



?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容