美文网首页ios学习交流xcode工具
【iOS】分享一个ipa打包脚本

【iOS】分享一个ipa打包脚本

作者: 谦言忘语 | 来源:发表于2018-01-14 15:52 被阅读396次

    写在前面

    之前写了一个很简单的ipa打包脚本,主要是用Xcode命令中的xcodebuild和xcrun命令来完成的,其中打包ipa的命令用了PackageApplication,本来用的好好的,升级了Xcode9之后,发现苹果把PackageApplication这个东西给删了,于是脚本就跪了。。。
    所以,这两个月我用了一个很原始的方法来打ipa包。大家也可以试试(大家常用的直接用Xcode中的Product->Archive的方法我就懒得说了,大家都懂):
    1.自己在Xcode配置好项目的签名
    2.用Generic iOS Device来build工程
    3.找到生成的.app包,然后新建一个名为Payload的文件夹,将.app包放到文件夹里面
    4.压缩Payload文件夹,然后将压缩包的后缀从.zip改成.ipa。
    然后就可以将ipa包安装到手机上测试了。


    用Generic iOS Device来build工程
    压缩Payload文件夹,然后将压缩包的后缀从.zip改成.ipa

    为什么需要一个ipa打包脚本

    嗯,因为想偷懒。
    直接在Xcode上点击Product->Archive->此处省略n步选择.......这种方式确实是挺方便的,估计很多人也是这么干的。但是,当你多次打包了之后你就发现,打包这事好无聊。每次都是同样的配置,同样的操作步骤,同样的选择,这么简单的操作我居然要重复N遍。。。而且有时候要等很久才能进行下一步。总之我觉得这种方式操作多了很是蛋疼。
    我一直秉承一个理念:能用机器自动解决的问题尽量不用人工操作
    要是有一个东西,只要我配置好了一次之后,以后直接双击就能直接打包,是不是比之前的方式更好?
    嗯,有挺多这种工具的,比如shenzhen,fastlane等等。
    不过我是自己写了一个简单的shell脚本来实现功能的,比较简单,而且不会对项目有侵入性。借此还可以顺便学习下shell脚本使用。

    自动打包脚本如何使用?

    脚本的github地址为:https://github.com/shixueqian/AutoPackageScript
    使用方法:

    • 1.将脚本复制到工程的根目录
    • 2.用代码编辑软件(比如Xcode)打开脚本,然后根据情况修改脚本内的一些参数
    • 3.打开终端,输入 sh ${打包脚本的全路径}就可执行打包脚本。
      比如我的项目工程在
      /Users/mac/Desktop/AutoPackageScriptDemo,
      那么我的脚本路径应该是
      /Users/mac/Desktop/AutoPackageScriptDemo/AutoPackageScript.sh,
      所以我要执行的命令是
      sh /Users/mac/Desktop/AutoPackageScriptDemo/AutoPackageScript.sh

    sh ${打包脚本的全路径}这行命令的作用是执行shell打包脚本。
    除了这样执行,我们还可以直接双击脚本文件就执行脚本,不过在这之前我们需要进行一些设置。
    首先,将AutoPackageScript.sh文件的扩展名去掉,变成AutoPackageScript
    然后,打开终端,执行命令 chmod +x ${打包脚本的全路径},这样可以给脚本加上可执行权限,并且默认的打开方式是终端。
    例如:chmod +x /Users/mac/Desktop/AutoPackageScriptDemo/AutoPackageScript
    以后,直接鼠标双击就可以执行脚本了

    脚本参数配置

    看了脚本的使用,其中有一个很关键的东西,脚本参数配置。我对多种情况都有适配,所以脚本参数会稍微有点复杂,以下我们慢慢道来。

    项目的工程结构

    我也不知道怎么称呼这个东西,简单来讲就是你的工程里面是不是使用xcworkspace(工作空间)来管理你的工程。
    我们在Xcode直接新建一个iOS工程,这个时候仅仅只有一个.xcodeproj文件,是没有.xcworkspace文件的。
    而很多的项目都采用了cocoapods来管理项目,这个时候是有.xcworkspace文件的,cocoapods通过xcworkspace来管理了第三方库。
    这两种结构的参数配置是不一样的。(因为写脚本的时候有区别)

    # 是否编译工作空间 (例:若是用Cocopods管理的.xcworkspace项目,赋值true;用Xcode默认创建的.xcodeproj,赋值false)
    is_workspace="false"
    
    # .xcworkspace的名字,如果is_workspace为true,则必须填。否则可不填
    workspace_name=""
    
    # .xcodeproj的名字,如果is_workspace为false,则必须填。否则可不填
    project_name="AutoPackageScriptDemo"
    
    # 指定项目的scheme名称(也就是工程的target名称),必填
    scheme_name="AutoPackageScriptDemo"
    

    注释其实已经写得很清楚了。举个例子,github上的workspace_demo是通过.xcworkspace来管理的:


    通过.xcworkspace来管理
    项目的scheme_name为AutoPackageScriptDemo

    所以这几个参数就是

    is_workspace="true"
    workspace_name="AutoPackageScriptDemo"
    project_name=""
    scheme_name="AutoPackageScriptDemo"
    

    否则,像github上面的project_demo里面的单工程结构就是

    is_workspace="false"
    workspace_name=""
    project_name="AutoPackageScriptDemo"
    scheme_name="AutoPackageScriptDemo"
    

    method,打包的方式。

    证书签名的方式,是通过脚本中的method变量控制的。
    分别为 development, ad-hoc, app-store, enterprise 。看到这几个参数估计都明白了吧?还不是不太了解的话建议看下蒲公英的这篇文档

    # method,打包的方式。方式分别为 development, ad-hoc, app-store, enterprise 。必填
    method="development"
    

    profile文件的管理方式

    一般来说,证书管理方式,如今应该挺多人使用Xcode自动管理的。省心而且方便(老实说,由于工作原因,我平常比较少用自动管理,都是手动管理的,所以理解有误的话请提出)。
    在Xcode->Preferrence->Account里面添加开发者账号,然后在工程的General->勾选Automatically manage signing->选择开发者账号。就可以自动管理了。
    另外一种就是古老的手动管理方式了。
    在开发者后台上面创建BundleID,然后创建mobileprovision文件,安装到Xcode上面选择使用。
    针对这两种方式有不同的配置。

    #  下面两个参数只是在手动指定Pofile文件的时候用到,如果使用Xcode自动管理Profile,直接留空就好
    # (跟method对应的)mobileprovision文件名,需要先双击安装.mobileprovision文件.手动管理Profile时必填
    mobileprovision_name=""
    
    # 项目的bundleID,手动管理Profile时必填
    bundle_identifier=""
    

    注释讲得很清楚,使用Xcode自动管理profile的话直接留空就好了。
    如果使用手动管理的话就需要填写对应的参数了。

    源码解析

    脚本代码其实很简单的。主要讲解一下关键代码。
    先把源码放出来:

    #!/bin/sh
    
    # 使用方法:
    # step1: 将该脚本放在工程的根目录下(跟.xcworkspace文件or .xcodeproj文件同目录)
    # step2: 根据情况修改下面的参数
    # step3: 打开终端,执行脚本。(输入sh ,然后将脚本文件拉到终端,会生成文件路径,然后enter就可)
    
    # =============项目自定义部分(自定义好下列参数后再执行该脚本)=================== #
    
    # 是否编译工作空间 (例:若是用Cocopods管理的.xcworkspace项目,赋值true;用Xcode默认创建的.xcodeproj,赋值false)
    is_workspace="false"
    
    # .xcworkspace的名字,如果is_workspace为true,则必须填。否则可不填
    workspace_name=""
    
    # .xcodeproj的名字,如果is_workspace为false,则必须填。否则可不填
    project_name="AutoPackageScriptDemo"
    
    # 指定项目的scheme名称(也就是工程的target名称),必填
    scheme_name="AutoPackageScriptDemo"
    
    # 指定要打包编译的方式 : Release,Debug。一般用Release。必填
    build_configuration="Release"
    
    # method,打包的方式。方式分别为 development, ad-hoc, app-store, enterprise 。必填
    method="development"
    
    
    #  下面两个参数只是在手动指定Pofile文件的时候用到,如果使用Xcode自动管理Profile,直接留空就好
    # (跟method对应的)mobileprovision文件名,需要先双击安装.mobileprovision文件.手动管理Profile时必填
    mobileprovision_name=""
    
    # 项目的bundleID,手动管理Profile时必填
    bundle_identifier=""
    
    
    echo "--------------------脚本配置参数检查--------------------"
    echo "\033[33;1mis_workspace=${is_workspace} "
    echo "workspace_name=${workspace_name}"
    echo "project_name=${project_name}"
    echo "scheme_name=${scheme_name}"
    echo "build_configuration=${build_configuration}"
    echo "bundle_identifier=${bundle_identifier}"
    echo "method=${method}"
    echo "mobileprovision_name=${mobileprovision_name} \033[0m"
    
    
    # =======================脚本的一些固定参数定义(无特殊情况不用修改)====================== #
    
    # 获取当前脚本所在目录
    script_dir="$( cd "$( dirname "$0"  )" && pwd  )"
    # 工程根目录
    project_dir=$script_dir
    
    # 时间
    DATE=`date '+%Y%m%d_%H%M%S'`
    # 指定输出导出文件夹路径
    export_path="$project_dir/Package/$scheme_name-$DATE"
    # 指定输出归档文件路径
    export_archive_path="$export_path/$scheme_name.xcarchive"
    # 指定输出ipa文件夹路径
    export_ipa_path="$export_path"
    # 指定输出ipa名称
    ipa_name="${scheme_name}_${DATE}"
    # 指定导出ipa包需要用到的plist配置文件的路径
    export_options_plist_path="$project_dir/ExportOptions.plist"
    
    
    echo "--------------------脚本固定参数检查--------------------"
    echo "\033[33;1mproject_dir=${project_dir}"
    echo "DATE=${DATE}"
    echo "export_path=${export_path}"
    echo "export_archive_path=${export_archive_path}"
    echo "export_ipa_path=${export_ipa_path}"
    echo "export_options_plist_path=${export_options_plist_path}"
    echo "ipa_name=${ipa_name} \033[0m"
    
    # =======================自动打包部分(无特殊情况不用修改)====================== #
    
    echo "------------------------------------------------------"
    echo "\033[32m开始构建项目  \033[0m"
    # 进入项目工程目录
    cd ${project_dir}
    
    # 指定输出文件目录不存在则创建
    if [ -d "$export_path" ] ; then
        echo $export_path
    else
        mkdir -pv $export_path
    fi
    
    # 判断编译的项目类型是workspace还是project
    if $is_workspace ; then
    # 编译前清理工程
    xcodebuild clean -workspace ${workspace_name}.xcworkspace \
                     -scheme ${scheme_name} \
                     -configuration ${build_configuration}
    
    xcodebuild archive -workspace ${project_name}.xcworkspace \
                       -scheme ${scheme_name} \
                       -configuration ${build_configuration} \
                       -archivePath ${export_archive_path}
    else
    # 编译前清理工程
    xcodebuild clean -project ${project_name}.xcodeproj \
                     -scheme ${scheme_name} \
                     -configuration ${build_configuration}
    
    xcodebuild archive -project ${project_name}.xcodeproj \
                       -scheme ${scheme_name} \
                       -configuration ${build_configuration} \
                       -archivePath ${export_archive_path}
    fi
    
    #  检查是否构建成功
    #  xcarchive 实际是一个文件夹不是一个文件所以使用 -d 判断
    if [ -d "$export_archive_path" ] ; then
        echo "\033[32;1m项目构建成功 🚀 🚀 🚀  \033[0m"
    else
        echo "\033[31;1m项目构建失败 😢 😢 😢  \033[0m"
        exit 1
    fi
    echo "------------------------------------------------------"
    
    echo "\033[32m开始导出ipa文件 \033[0m"
    
    
    # 先删除export_options_plist文件
    if [ -f "$export_options_plist_path" ] ; then
        #echo "${export_options_plist_path}文件存在,进行删除"
        rm -f $export_options_plist_path
    fi
    # 根据参数生成export_options_plist文件
    /usr/libexec/PlistBuddy -c  "Add :method String ${method}"  $export_options_plist_path
    /usr/libexec/PlistBuddy -c  "Add :provisioningProfiles:"  $export_options_plist_path
    /usr/libexec/PlistBuddy -c  "Add :provisioningProfiles:${bundle_identifier} String ${mobileprovision_name}"  $export_options_plist_path
    
    
    xcodebuild  -exportArchive \
                -archivePath ${export_archive_path} \
                -exportPath ${export_ipa_path} \
                -exportOptionsPlist ${export_options_plist_path} \
                -allowProvisioningUpdates
    
    # 检查ipa文件是否存在
    if [ -f "$export_ipa_path/$scheme_name.ipa" ] ; then
        echo "\033[32;1mexportArchive ipa包成功,准备进行重命名\033[0m"
    else
        echo "\033[31;1mexportArchive ipa包失败 😢 😢 😢     \033[0m"
        exit 1
    fi
    
    # 修改ipa文件名称
    mv $export_ipa_path/$scheme_name.ipa $export_ipa_path/$ipa_name.ipa
    
    # 检查文件是否存在
    if [ -f "$export_ipa_path/$ipa_name.ipa" ] ; then
        echo "\033[32;1m导出 ${ipa_name}.ipa 包成功 🎉  🎉  🎉   \033[0m"
        open $export_path
    else
        echo "\033[31;1m导出 ${ipa_name}.ipa 包失败 😢 😢 😢     \033[0m"
        exit 1
    fi
    
    # 删除export_options_plist文件(中间文件)
    if [ -f "$export_options_plist_path" ] ; then
        #echo "${export_options_plist_path}文件存在,准备删除"
        rm -f $export_options_plist_path
    fi
    
    # 输出打包总用时
    echo "\033[36;1m使用AutoPackageScript打包总用时: ${SECONDS}s \033[0m"
    
    exit 0
    
    

    clean工程

    以单工程项目为例(wordspace结构是相似的),参数配置完成之后,clean下工程,用以清除缓存,保证我们的项目是纯净的。
    跟我们在Xcode中Product->Clean这个命令是一样的功能。

    xcodebuild clean -project ${project_name}.xcodeproj \
                     -scheme ${scheme_name} \
                     -configuration ${build_configuration}
    

    project_name就是我们之前配置的工程的名称,
    scheme_name就是之前配置的工程的target名称
    build_configuration为之前配置的设置,Debug或者Release
    以我们demo中的单工程为例,就是这样的

    xcodebuild clean -project  AutoPackageScriptDemo.xcodeproj -scheme AutoPackageScriptDemo -configuration Release
    

    archive工程

    build工程,然后archive到一个文件夹里面。

    xcodebuild archive -project ${project_name}.xcodeproj \
                       -scheme ${scheme_name} \
                       -configuration ${build_configuration} \
                       -archivePath ${export_archive_path}
    

    其中, export_archive_path就是我们存放archive结果的文件夹。
    这条命令执行以后,会在指定的位置生成.xcarchive文件(其实这个是文件夹来的。里面会有.app包和dSYM符号文件等内容)
    以我们的单工程demo为例:

    export_archive_path=/Users/mac/Desktop/简书/AutoPackageScript/project_demo/AutoPackageScriptDemo/Package/AutoPackageScriptDemo_20180108/AutoPackageScriptDemo.xcarchive
    xcodebuild archive -project AutoPackageScriptDemo.xcodeproj -scheme AutoPackageScriptDemo -configuration Release -archivePath ${export_archive_path}
    
    命令执行成功会生成.xcarchive文件
    .xcarchive文件里面的内容

    exportArchive,从xcarchive中导出ipa包

    将刚才archive出来的.xcarchive文件,导出成一个ipa包。

    xcodebuild  -exportArchive \
                -archivePath ${export_archive_path} \
                -exportPath ${export_ipa_path} \
                -exportOptionsPlist ${export_options_plist_path} \
                -allowProvisioningUpdates
    

    export_archive_path就是刚才导出.xcarchive文件路径
    export_ipa_path是要导出的ipa文件的文件夹路径
    export_options_plist_path这是一个plist配置文件路径。这个plist配置文件是必须的,不加的话会报错的。经过我的测试发现,主要是需要配置method这个key。如果是手动管理的话,还需要配置bundleID和mobileprovision_name。其他的key是可以不配置的,如果不配置的话,会自动根据项目里面的配置进行生成。另外,这个文件我在脚本中用PlistBuddy命令直接生成了,不需要用户自己指定文件。
    -allowProvisioningUpdates主要目的是自动更新profile文件,在Xcode自动管理profile的时候用。
    以我们的单工程demo为例:

    export_ipa_path=/Users/mac/Desktop/简书/AutoPackageScript/project_demo/AutoPackageScriptDemo/Package/AutoPackageScriptDemo_20180108
    export_options_plist_path=/Users/mac/Desktop/简书/AutoPackageScript/project_demo/AutoPackageScriptDemo/ExportOptions.plist
    xcodebuild  -exportArchive \
                -archivePath ${export_archive_path} \
                -exportPath ${export_ipa_path} \
                -exportOptionsPlist ${export_options_plist_path} \
                -allowProvisioningUpdates
    
    其中,ExportOptions.plist配置文件我只设置了method 。 ExportOptions.plist配置文件 命令执行成功之后会生成我们需要的ipa文件和其他的几个东西。 exportArchive成功之后生成的文件

    其中,
    ipa文件就是我们需要的安装包。
    DistributionSummary.plist文件是一些详细的签名信息。
    ExportOptions.plist文件其实就是我们在exportArchive命令时要用的,但在exportArchive之后会自动生成一个完整的文件。如果不知道该怎么写这个配置文件的话,可以直接参考我demo中生成的这个plist文件。
    Packaging.log这个文件就是打包的时候产生的log了,可以查看日志记录。

    参考

    脚本和demo都放到github上面去了。https://github.com/shixueqian/AutoPackageScript
    本脚本主要参考了自动打包ipa文件并上传fir.im托管平台的shell脚本

    谦言忘语

    嗯,一个很简单的小东西,大家有时候可以尝试使用一些小脚本完成一些简单重复的工作,说不定会让生活变得更好。

    相关文章

      网友评论

      • HJD:请问上APPSTORE的话用这个脚本生成的ipa包可以吗
        HJD:@谦言忘语 刚才试了一下,确实是可以打包成功的,可是我想问下大神,好像脚本中没有处理对证书这块进行处理吧,不是应该要对证书进行处理吗
        HJD:@谦言忘语 好的
        谦言忘语:@HJD 可以的
      • 嘿你个叼买毛:请问 这个问题如何解决
        开始导出ipa文件
        File Doesn't Exist, Will Create: /Users/xiao/Desktop/移动门户分支开发/CRBR-MobilePortal_1/ExportOptions.plist
        Unrecognized Type:
        2018-07-16 17:17:59.402 xcodebuild[9590:4630644] [MT] PluginLoading: Required plug-in compatibility UUID 426A087B-D3AA-431A-AFDF-F135EC00DE1C for plug-in at path '~/Library/Application Support/Developer/Shared/Xcode/Plug-ins/VVDocumenter-Xcode.xcplugin' not present in DVTPlugInCompatibilityUUIDs
        2018-07-16 17:17:59.655 xcodebuild[9590:4630644] [MT] IDEDistribution: -[IDEDistributionLogging _createLoggingBundleAtPath:]: Created bundle at path '/var/folders/bn/4gv4hf2j50b2h35yf22d63m00000gn/T/移动门户_2018-07-16_17-17-59.653.xcdistributionlogs'.
        2018-07-16 17:18:00.004 xcodebuild[9590:4630644] [MT] IDEDistribution: Step failed: <IDEDistributionOptionsStep: 0x7fab7e095120>: Error Domain=IDEFoundationErrorDomain Code=1 "exportOptionsPlist error for key 'iCloudContainerEnvironment': expected one of {Development, Production}, but no value was provided" UserInfo={NSLocalizedDescription=exportOptionsPlist error for key 'iCloudContainerEnvironment': expected one of {Development, Production}, but no value was provided}
        error: exportArchive: exportOptionsPlist error for key 'iCloudContainerEnvironment': expected one of {Development, Production}, but no value was provided

        Error Domain=IDEFoundationErrorDomain Code=1 "exportOptionsPlist error for key 'iCloudContainerEnvironment': expected one of {Development, Production}, but no value was provided" UserInfo={NSLocalizedDescription=exportOptionsPlist error for key 'iCloudContainerEnvironment': expected one of {Development, Production}, but no value was provided}

        ** EXPORT FAILED **

        exportArchive ipa包失败 😢 😢 😢
      • boxKUn:老哥,一直提示are installed是怎么回事?
      • 漆黑烈焰武士G:我的哥,你这个有bug
        第99行 project_name 应为 workspace_name
        谦言忘语:感谢指正,目前已修正了
      • McIntosh:这个脚本Swift项目无法运行
        报错:
        Unrecognized Type:
        2018-03-16 15:13:19.919 xcodebuild[55675:307847] [MT] IDEDistribution: -[IDEDistributionLogging _createLoggingBundleAtPath:]: Created bundle at path '/var/folders/jp/pvg2rtgd6nb897x0jbk5bv7c0000gn/T/TestAutoPSwift_2018-03-16_15-13-19.918.xcdistributionlogs'.
        2018-03-16 15:13:21.145 xcodebuild[55675:307847] [MT] IDEDistribution: Step failed: <IDEDistributionPackagingStep: 0x7ff44c523f90>: Error Domain=NSCocoaErrorDomain Code=3840 "No value." UserInfo={NSDebugDescription=No value., NSFilePath=/var/folders/jp/pvg2rtgd6nb897x0jbk5bv7c0000gn/T/ipatool-json-filepath-7rbNBv}
        error: exportArchive: The data couldn’t be read because it isn’t in the correct format.

        Error Domain=NSCocoaErrorDomain Code=3840 "No value." UserInfo={NSDebugDescription=No value., NSFilePath=/var/folders/jp/pvg2rtgd6nb897x0jbk5bv7c0000gn/T/ipatool-json-filepath-7rbNBv}

        请问这个怎么解决?

      本文标题:【iOS】分享一个ipa打包脚本

      本文链接:https://www.haomeiwen.com/subject/zoqbnxtx.html