写在前面

本文主要是基于用同一套代码生成不用包名、不同开发者证书、不同功能、不同组件依赖、不同Extension、多target等各种需求定制。

环境安装

  • 安装cocoapods,如果这个都没有安装,基本上不用往后面看了。
  • 安装ruby库xcodeproj,详见Xcodeproj Github

定制打包

一、加载xxx.project的配置
project_path = 'xxx.xcodeproj'
project = Xcodeproj::Project.open(project_path)
复制代码
二、修改工程某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
复制代码
三、依赖和取消依赖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
复制代码
四、向target添加或移除framework

Link binary with libraries system libraries, link them.

Embed Frameworks 3rd party libraries, embed them.

  • 添加或移除系统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
复制代码
五、是否开启SystemCapabilities

例如:AccessWiFi

#从外面传入的参数
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
复制代码
六、动态修改组件依赖
  • 原本思路 通过脚本通过sed命令直接替换字符串实现,如下:
target 'Scene' do
    pod 'iOSExtension/Widgets',:path => '../'
    pod 'XXX'
    pod 'YYY'
    pod 'AAA'
    pod 'BBB'
end
复制代码

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

$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库的,可以定义一个函数

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
复制代码

脚本如下:

#依赖的脚本
$sed -i "" 's/myPodModule(0)/myPodModule(1)/g' xxx/Podfile
#不依赖的脚本
$sed -i "" 's/myPodModule(1)/myPodModule(0)/g' xxx/Podfile
复制代码
七、多target的组件依赖

原始的Podfile 的结构如下:

target 'Scene' do
    pod 'iOSExtension/Widgets',:path => '../'
end
target 'Widgets' do
    pod 'my/Wiget',:path => '../'
end
复制代码

这时候的脚本让我很难受。其实同样可以采用上面的思路去封装

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
复制代码
八、更多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

转载于:https://juejin.im/post/5c943d35e51d45046d42e7b6

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐