iOS定製化的自動打包腳本

寫在前面

本文主要是基於用同一套代碼生成不用包名、不一樣開發者證書、不一樣功能、不一樣組件依賴、不一樣Extension、多target等各類需求定製。git

環境安裝

  • 安裝cocoapods,若是這個都沒有安裝,基本上不用日後面看了。
  • 安裝ruby庫xcodeproj,詳見Xcodeproj Github

定製打包

1、加載xxx.project的配置

project_path = 'xxx.xcodeproj'
project = Xcodeproj::Project.open(project_path)
複製代碼

2、修改工程某Target的build setting

project.targets.each do |target|
    #your target name
    if target.name == 'Scene'
        target.build_configurations.each do |config|
         # Debug / Release
        if config.name == 'Release'
             #得到build settings
            build_settings = config.build_settings
            #build_settings是一個哈希,裏面是一個個配置
            build_settings.each do |key,value|
                print key, " == ", value, "\n"
                # 可在這裏進行設置證書等操做,經常使用的以下:
                # 好比修改bundle id ,測試
                # build_settings[key] = "com.test.demo"
                # 設置受權文件
                # build_settings["CODE_SIGN_ENTITLEMENTS"] = "xxx.entitlements"
                # 設置簽名 iPhone Distribution / iPhone Developer
                build_settings["PROVISIONING_PROFILE_SPECIFIER"] = "PROFILENAME"
                build_settings["PRODUCT_BUNDLE_IDENTIFIER"] = "your BUNDLEID"
                build_settings["DEVELOPMENT_TEAM"] = "your TEAMID"
                # ..... 其餘的視狀況(需求)去查找API
            end
        end
    end
 end
複製代碼

3、依賴和取消依賴Extension

def updateDependecies(mode, project, app_target_name, ext)
        require 'xcodeproj'
        
        # Find project and app target
        puts mode
        xc = project
        app_target = xc.targets.find { |t| t.name == app_target_name }
        
        # Find app extensions' target objects
        target = xc.targets.find { |t| t.name == ext }
        return "Couldn't find a '#{ext}' target in '#{app_target_name}'" if target.nil?
        return "'#{ext}' doesn't seem to be an application extension target" unless target.product_type.include? 'app-extension'
        target
        
        # Find .appex product files
        appex = target.product_reference
        
        # Add or remove dependency on extension targets
        dependency = app_target.dependency_for_target(target)
        puts app_target.dependencies
        if mode == 'add'
            if dependency
                puts "[WARN] App already has dependency on #{target.name}"
                else
                app_target.add_dependency(target)
            end
            else
            if dependency
                app_target.dependencies.delete(dependency)
                else
                puts "[WARN] Couldn't find dependency on #{target.name}"
            end
        end
        
        # Add or remove .appex copy jobs
        embed_extensions_phase = app_target.copy_files_build_phases.find do |copy_phase|
            copy_phase.symbol_dst_subfolder_spec == :plug_ins
        end
        
        return "Couldn't find 'Embed App Extensions' phase" if embed_extensions_phase.nil?
        appex_included = embed_extensions_phase.files_references.include? appex
        
        if mode == 'add'
            if appex_included
                puts "[WARN] App already embeds #{appex.display_name}"
                else
                build_file = embed_extensions_phase.add_file_reference(appex)
                build_file.settings = { "ATTRIBUTES" => ['RemoveHeadersOnCopy'] }
            end
            else
            if appex_included
                embed_extensions_phase.remove_file_reference(appex)
                else
                puts "[WARN] App doesn't seem to embed #{appex.display_name}"
            end
        end
         return "更新依賴完成,update dependency '#{ext}' target in '#{app_target_name}'"
    end
複製代碼

4、向target添加或移除framework

Link binary with libraries system libraries, link them.github

Embed Frameworks 3rd party libraries, embed them.xcode

  • 添加或移除系統framework
def exsit_framework?(build_phase,name)
        # next !if build_phase.nil?
        build_phase.files_references.each do |ref|
            if ref.name == "#{name}"
                puts "已經存在 #{name}"
                return true
            end
        end
        puts "不存在 #{name}"
        return false
end

def updateSystemFramework(project,target,name, command)
        require 'xcodeproj'
        puts "#{command} #{name} to #{target}"
        build_phase = target.frameworks_build_phase
        framework_group = project.frameworks_group
        if command == :add
            if self.exsit_framework?(build_phase,name)
                return
            end
            systempath = "System/Library/Frameworks/"
            path = "#{systempath}#{name}"
            file_ref = framework_group.new_reference(path)
            file_ref.name = "#{name}"
            file_ref.source_tree = 'SDKROOT'
            build_file = build_phase.add_file_reference(file_ref)
        else
            build_phase.files_references.each do |ref|
                if ref.name == "#{name}"
                    puts "刪除 #{name}"
                    build_phase.remove_file_reference(ref)
                    framework_group.children.delete(ref)                    
                    break
                end
            end 
        end
    end
複製代碼
  • 添加或移除自定義framework 和lib
def updateCustomFramework(project,target, path,name,command)

        # Get useful variables
        frameworks_group = project.groups.find { |group| group.display_name == 'Frameworks' }
        frameworks_build_phase = target.build_phases.find { |build_phase| build_phase.to_s == 'FrameworksBuildPhase' }
        embed_frameworks_build_phase = nil
        target.build_phases.each do |phase|
            if phase.display_name == 'Embed Frameworks'
                embed_frameworks_build_phase = phase
            end
            # puts phase
        end
        if command == :add
            # Add new "Embed Frameworks" build phase to target
            if embed_frameworks_build_phase.nil?
                embed_frameworks_build_phase = project.new(Xcodeproj::Project::Object::PBXCopyFilesBuildPhase)
                embed_frameworks_build_phase.name = 'Embed Frameworks'
                embed_frameworks_build_phase.symbol_dst_subfolder_spec = :frameworks
                target.build_phases << embed_frameworks_build_phase
            end
           
            # Add framework search path to target
            ['Debug', 'Release'].each do |config|
                paths = ['$(inherited)', path]
                orgpath = target.build_settings(config)['FRAMEWORK_SEARCH_PATHS']
                if orgpath.nil?
                    puts "路徑爲空----------"
                    target.build_settings(config)['FRAMEWORK_SEARCH_PATHS'] = paths
            else
                    puts "路徑非空----------"
                    paths.each do |p|
                        if !orgpath.include?(p)
                            orgpath << p
                        end 
                    end
                end
            end

            # Add framework to target as "Embedded Frameworks"
            framework_ref = frameworks_group.new_file("#{path}#{name}")
            exsit = false
            embed_frameworks_build_phase.files_references.each do |ref|
                if ref.name = name
                    exsit = true
                end
            end
            if !exsit
                build_file = embed_frameworks_build_phase.add_file_reference(framework_ref)
                frameworks_build_phase.add_file_reference(framework_ref)
                build_file.settings = { 'ATTRIBUTES' => ['CodeSignOnCopy'] }
            end
        else
            frameworks_build_phase.files_references.each do |ref|

                if ref.name == "#{name}"
                    puts "刪除 #{name}"
                    frameworks_build_phase.remove_file_reference(ref)
                    embed_frameworks_build_phase.remove_file_reference(ref)
                    break
                end
            end
        end

    end
複製代碼

5、是否開啓SystemCapabilities

例如:AccessWiFiruby

#從外面傳入的參數
wifiFlag = ARGV[0]
project.objects.each do |obj|
    if obj.isa == "PBXProject"
        systemCapabilities = obj.attributes["TargetAttributes"][ta.uuid]["SystemCapabilities"]
        puts systemCapabilities
        systemCapabilities["com.apple.AccessWiFi"] = {"enabled":"#{wifiFlag}"}
    end
end
複製代碼

6、動態修改組件依賴

  • 本來思路 經過腳本經過sed命令直接替換字符串實現,以下:
target 'Scene' do
    pod 'iOSExtension/Widgets',:path => '../'
    pod 'XXX'
    pod 'YYY'
    pod 'AAA'
    pod 'BBB'
end
複製代碼

使用sed 命令刪除包名XXX的指定行bash

$sed -i '' "/XXX.*/d" filePath
複製代碼
  • 優化思路 雖然上面的指定咱們達到效果,可是某些場景下面,就顯得有些醜了。如:在某些環境下,咱們某個模塊的因此pod庫,若是仍是按上面的腳本的話寫法應該以下:
$sed -i '' "/XXX.*/d" filePath
$sed -i '' "/YYY.*/d" filePath
$sed -i '' "/AAA.*/d" filePath
$sed -i '' "/BBB.*/d" filePath
複製代碼

其實,Podfile 自己也是一個ruby文件,因此在其中能夠定義函數。這時候,解決上面移除多個pod庫的,能夠定義一個函數app

def myPodModule(flag)
    if flag == 0
        puts "不--安裝myPodModule"
        return
    end
    pod 'XXX'
    pod 'YYY'
    pod 'AAA'
    pod 'BBB'
    puts "安裝myPodModule"
end
target 'Scene' do
    pod 'iOSExtension/Widgets',:path => '../'
    #調用
    myPodModule(1)
end
複製代碼

腳本以下:less

#依賴的腳本
$sed -i "" 's/myPodModule(0)/myPodModule(1)/g' xxx/Podfile
#不依賴的腳本
$sed -i "" 's/myPodModule(1)/myPodModule(0)/g' xxx/Podfile
複製代碼

7、多target的組件依賴

原始的Podfile 的結構以下:函數

target 'Scene' do
    pod 'iOSExtension/Widgets',:path => '../'
end
target 'Widgets' do
    pod 'my/Wiget',:path => '../'
end
複製代碼

這時候的腳本讓我很難受。其實一樣能夠採用上面的思路去封裝post

def widgets(flag)
    if flag == 0
        puts "不--給widgets target安裝依賴"
        return
    end
    target 'Widgets' do
        pod 'my/Wiget',:path => '../'
    end
end
target 'Scene' do
    pod 'iOSExtension/Widgets',:path => '../'
end
#調用
widgets(1)
複製代碼

腳本以下:測試

#依賴的腳本
$sed -i "" 's/widgets(0)/widgets(1)/g' xxx/Podfile
#不依賴的腳本
$sed -i "" 's/widgets(1)/widgets(0)/g' xxx/Podfile
複製代碼

8、更多Podfile騷操做

post_install do |installer|
    project = installer.aggregate_targets.first.user_project
    
    project.targets.each do |target|
        puts target.name,'名稱:'
        end
end

複製代碼

更多資料

Xcodeproj GitHub地址

Xcodeproj API文檔

最後

fuck996

相關文章
相關標籤/搜索