最近有好一段時(shí)間沒(méi)有整理博客了,也不是因?yàn)槊?,就是太懶。或者說(shuō)程序員都自帶懶屬性??,表現(xiàn)在對(duì)于一些機(jī)械性重復(fù)性的工作,寧愿一次性多花些精力去封裝、去寫(xiě)出各種各樣的工具來(lái)替自己干活,也不愿意多次重復(fù)勞動(dòng)(就算那是很簡(jiǎn)單的工作)。這不,我結(jié)合最近的工作,研究著寫(xiě)了兩份Shell腳本,一是關(guān)于Xcode腳本打包,還有一個(gè)是關(guān)于crash log解析(這個(gè)將在下一篇文章說(shuō)明)。
Shell打包腳本實(shí)現(xiàn)了如下功能
- 指定打包項(xiàng)目的Build號(hào),Version版本號(hào)——Version號(hào)可選擇自增或自定義(在測(cè)試階段打包時(shí),每次手動(dòng)改變Version號(hào)很是麻煩,而且不方便管理;而如果能打包時(shí)通過(guò)腳本參數(shù)控制,so easy~)
- 導(dǎo)出xcarchive文件,通過(guò)xcarchive文件得到dSYM文件,這是很重要的。查找線(xiàn)上crash時(shí),缺它不可!crash log解析請(qǐng)看這里。
- 打包生成ipa文件,這個(gè)就是最終需要的渠道包了。
另外打包腳本可以根據(jù)項(xiàng)目配置,每一次成功打包后,都會(huì)生成對(duì)應(yīng)目錄,并生成記錄本次打包的log日志文件。
打包腳本
#!/bin/bash
#--------------------------------------------------------------------------------
# 腳本說(shuō)明:
# 1、實(shí)現(xiàn)功能:
# 1)、指定打包項(xiàng)目的Build號(hào),Version版本號(hào)(Version號(hào)可選擇自增或自定義)
# 2)、導(dǎo)出xcarchive文件
# 3)、打包生成ipa文件
# 2、使用方式:
# 1)、將ReleaseDir文件夾,放到跟所要打包的項(xiàng)目的根目錄(TestDemo)同級(jí)別的目錄下
# 2)、cd至ReleaseDir,運(yùn)行腳本./Release.sh TestDemo,并選擇輸入相關(guān)參數(shù),開(kāi)始打包
# 3)、完成打包后,生成的目標(biāo)文件在如下目錄:
# /ReleaseDir/ArchivePath/TestDemo/1.0.0/2017_03_03_10:37:34
# (/ReleaseDir/ArchivePath/打包項(xiàng)目名稱(chēng)/打包版本號(hào)/打包時(shí)間)
#--------------------------------------------------------------------------------
#
#--------------------------------------------------------------------------------
# 打包project
# ./Release.sh <Project directory name> [-s <Name>] [-e] [-d] [-a] [-b <Build number>]
# 打包workspace
# ./Release.sh <Project directory name> [-w] [-s <Name>] [-e] [-d] [-b <Build number>] [-v <Version number>]
# 參數(shù)說(shuō)明:
# <Project directory name> 第一個(gè)參數(shù):所要打包的項(xiàng)目的根目錄文件夾名稱(chēng)
# -w workspace打包,不傳默認(rèn)為project打包
# -s <Name> 對(duì)應(yīng)workspace下需要編譯的scheme(不傳默認(rèn)取xcodeproj根目錄文件名)
# -e 打包前是否先編譯工程(不傳默認(rèn)不編譯)
# -d 工程的configuration為 Debug 模式,不傳默認(rèn)為Release
# -a 打包,Version版本號(hào)自動(dòng)+1(針對(duì)多次打測(cè)試包時(shí)的版本號(hào)修改)
# -b <Build Num> Build版本號(hào),指定項(xiàng)目Build號(hào)
# -v <Version Num> Version版本號(hào),指定項(xiàng)目Version號(hào)
# 注意,參數(shù)-a 與 -v 互斥,只能選擇傳其中一種參數(shù)!!
#--------------------------------------------------------------------------------
#
#--------------------------------------------------------------------------------
# ReleaseDir文件目錄說(shuō)明:
#
# |___TestDemo 所要打包的項(xiàng)目的根目錄
# |___ReleaseDir 打包相關(guān)資源的根目錄
# |___Release.sh Release.sh打包腳本
# |___ExportOptions.plist -exportOptionsPlist 配置文件
# |___ArchivePath 打包文件輸出路徑
# |___TestDemo 打包項(xiàng)目名稱(chēng)
# |___1.0.0 打包版本號(hào)
# |___2017_03_02_16/23/28 打包時(shí)間(格式:年_月_日_時(shí)/分/秒)
# |___TestDemo_1.0.0.xcarchive 導(dǎo)出的.xcarchive文件
# |___TestDemo.ipa 導(dǎo)出的.ipa文件
# |___LogPath 打包日志
#
#--------------------------------------------------------------------------------
#計(jì)時(shí)
SECONDS=0
#--------------------------------------------
# 參數(shù)判斷
#--------------------------------------------
# 如果參數(shù)個(gè)數(shù)少于1
if [ $# -lt 1 ];then
echo -e "\033[31m第一個(gè)參數(shù)請(qǐng)輸入 Xcode project 文件所在的根目錄文件夾名稱(chēng)??!\033[0m"
exit 2
fi
# 腳本文件所在根目錄
Release_path=$(pwd)
Project_dir=$1
Project_path=""
if [ "`pwd`" != "/" ]; then
cd ..
Root_path=$(pwd)
Project_path="${Root_path}/${Project_dir}"
else
echo -e "\033[31m腳本路徑錯(cuò)誤\033[0m"
exit 1;
fi
if [ ! -d "${Project_path}" ];then
echo -e "\033[31m首參數(shù)必須是有效的文件夾名稱(chēng)!!\033[0m"
exit 2
fi
#Xcode project 文件路徑
cd ${Project_path}
Valid_dic=false
for i in `ls`;
do
#獲取文件后綴名
extension=${i##*.}
if [[ ${extension} == "xcodeproj" ]]; then
Valid_dic=true
elif [[ ${extension} == "xcworkspace" ]]; then
Valid_dic=true
fi
done
if [[ ${Valid_dic} == false ]]; then
echo -e "\033[31m請(qǐng)輸入包含xcodeproj或xcworkspace的文件夾名稱(chēng)??!\033[0m"
exit 2
fi
Archive_type="project"
Scheme_name="default"
Edit="NO"
Configuration="Release"
Auto_increment_version_num="NO"
Build_str="custom"
Version_str="custom"
# 參數(shù)處理
param_pattern=":ws:edab:v:"
OPTIND=2
while getopts $param_pattern optname
do
case "$optname" in
"w")
Archive_type="workspace"
;;
"s")
tmp_optind=$OPTIND
tmp_optname=$optname
tmp_optarg=$OPTARG
OPTIND=$OPTIND-1
if getopts $param_pattern optname ;then
echo -e "\033[31m選項(xiàng)參數(shù)錯(cuò)誤 $tmp_optname\033[0m"
exit 2
fi
OPTIND=$tmp_optind
Scheme_name=$tmp_optarg
;;
"e")
Edit="YES"
;;
"d")
Configuration="Debug"
;;
"a")
Auto_increment_version_num="YES"
;;
"b")
tmp_optind=$OPTIND
tmp_optname=$optname
tmp_optarg=$OPTARG
OPTIND=$OPTIND-1
if getopts $param_pattern optname ;then
echo -e "\033[31m選項(xiàng)參數(shù)錯(cuò)誤 $tmp_optname\033[0m"
exit 2
fi
OPTIND=$tmp_optind
Build_str=$tmp_optarg
;;
"v")
tmp_optind=$OPTIND
tmp_optname=$optname
tmp_optarg=$OPTARG
OPTIND=$OPTIND-1
if getopts $param_pattern optname ;then
echo -e "\033[31m選項(xiàng)參數(shù)錯(cuò)誤 $tmp_optname\033[0m"
exit 2
fi
OPTIND=$tmp_optind
Version_str=$tmp_optarg
;;
"?")
echo -e "\033[31m選項(xiàng)錯(cuò)誤: $OPTARG\033[0m"
exit 2
;;
":")
echo -e "\033[31m選項(xiàng) $OPTARG 必須帶參數(shù)\033[0m"
exit 2
;;
*)
echo -e "\033[31m參數(shù)錯(cuò)誤\033[0m"
exit 2
;;
esac
done
# echo "Project_path = ${Project_path}"
#app文件名稱(chēng)
appname=$(basename ./*.xcodeproj)
#通過(guò)app文件名獲得工程target名字
target_name=$(echo $appname | awk -F. '{print $1}')
#app文件中Info.plist文件路徑
App_infoplist_path=${Project_path}/${target_name}/Info.plist
# 獲取version值
Version_initial=$(/usr/libexec/PlistBuddy -c "print CFBundleShortVersionString" ${App_infoplist_path})
# 獲取build值
Build_initial=$(/usr/libexec/PlistBuddy -c "print CFBundleVersion" ${App_infoplist_path})
if [[ ${Scheme_name} == "default" ]]; then
Scheme_name=${target_name}
fi
echo "*************************************************"
echo -e "\033[36m* 打包項(xiàng)目: ${target_name}\033[0m"
echo "*************************************************"
echo ''
# 將數(shù)字123轉(zhuǎn)換為1.2.3的格式輸出
Num_str=""
caculate(){
Parameter_num=$1;
if [[ ${Parameter_num} < 10 ]]; then
if [[ ${Num_str} == "" ]]; then
Num_str="${Parameter_num}"
else
Num_str="${Parameter_num}.${Num_str}"
fi
else
#商
quotient="$[${Parameter_num}/10]"
#余數(shù)
remainder="$[${Parameter_num}%10]"
if [[ ${Num_str} == "" ]]; then
Num_str="${remainder}"
else
Num_str="${remainder}.${Num_str}"
fi
if [[ ${quotient} > 0 ]]; then
caculate ${quotient}
fi
fi
}
#--------------------------------------------
# 處理Version版本號(hào)
#--------------------------------------------
# Version版本號(hào)自增
if [[ ${Auto_increment_version_num} == "YES" ]]; then
if [[ ${Version_str} != "custom" ]]; then
echo -e "\033[31m選項(xiàng)-a 與 -v 不能同時(shí)存在\033[0m"
exit 2
else
#取Version版本號(hào)
Version_str=$(/usr/libexec/PlistBuddy -c "print CFBundleShortVersionString" ${App_infoplist_path})
#把Version_str字符串根據(jù).分割為數(shù)組
Array=($(echo $Version_str | tr '.' ' ' | tr -s ' '))
#數(shù)組長(zhǎng)度
Array_length=$(echo ${#Array[@]})
# 獲取版本號(hào)數(shù)字
Version_num=0
for (( i = 0; i < ${Array_length}; i++ )); do
index_num=${Array[$i]}
sum_num="$[10**(${Array_length}-${i}-1)]"
index_num="$[${index_num}*${sum_num}]"
Version_num="$[${Version_num}+${index_num}]"
done
Version_num="$[$Version_num+1]"
Num_str=""
caculate ${Version_num}
Version_str=${Num_str}
fi
fi
#--------------------------------------------
# 修改Version版本號(hào)以及Build號(hào)
#--------------------------------------------
if [[ ${Version_str} != "custom" ]]; then
echo "初始Version = ${Version_initial}"
/usr/libexec/Plistbuddy -c "Set CFBundleShortVersionString $Version_str" "${App_infoplist_path}"
Version_result=$(/usr/libexec/PlistBuddy -c "print CFBundleShortVersionString" ${App_infoplist_path})
echo "Version改變后 = ${Version_result}"
fi
if [[ ${Build_str} != "custom" ]]; then
echo "初始Build = ${Build_initial}"
/usr/libexec/Plistbuddy -c "Set CFBundleVersion $Build_str" "${App_infoplist_path}"
Build_result=$(/usr/libexec/PlistBuddy -c "print CFBundleVersion" ${App_infoplist_path})
echo "Build改變后 = ${Build_result}"
fi
if [ $? != 0 ]; then
/usr/libexec/Plistbuddy -c "Set CFBundleShortVersionString $Version_initial" "${App_infoplist_path}"
/usr/libexec/Plistbuddy -c "Set CFBundleVersion $Build_initial" "${App_infoplist_path}"
echo -e "\033[31m************* 修改版本號(hào)出錯(cuò) **************\033[0m"
exit 2
fi
#--------------------------------------------
# 準(zhǔn)備打包
#--------------------------------------------
ExportOptionsPlist="${Release_path}/ExportOptions.plist"
#取當(dāng)前時(shí)間字符串
Now=$(date +"%Y_%m_%d_%H:%M:%S")
Project_version=$(/usr/libexec/PlistBuddy -c "print CFBundleShortVersionString" ${App_infoplist_path})
Version_output_path="${Release_path}/ArchivePath/${target_name}/${Project_version}"
IPA_path="${Version_output_path}/${Now}"
Archive_file_name="${target_name}_${Project_version}"
Archive_path="${IPA_path}/${Archive_file_name}.xcarchive"
mkdir -p "${IPA_path}"
Log_path="${IPA_path}/LogPath"
echo "************* xcodebuild clean 項(xiàng)目 **************"
# 清除項(xiàng)目
# xcodebuild clean -configuration ${Configuration} &>/dev/null
xcodebuild clean -configuration ${Configuration} >> $Log_path
# if [[ ${Edit} == "YES" ]]; then
# cd "~/Library/Developer/Xcode"
# user=$USER
# Xcode_path="/Users/${user}/Library/Developer/Xcode"
# cd ${Xcode_path}
# for i in `ls`;
# do
# if [[ ${i} == "DerivedData" ]]; then
# if [ -d "${i}" ];then
# # 刪除~/Library/Developer/Xcode/DerivedData文件夾
# rm -rf ${i}
# fi
# fi
# done
# fi
revertVersionNum(){
/usr/libexec/Plistbuddy -c "Set CFBundleShortVersionString $Version_initial" "${App_infoplist_path}"
/usr/libexec/Plistbuddy -c "Set CFBundleVersion $Build_initial" "${App_infoplist_path}"
rm -rf "${IPA_path}"
files=`ls ${Version_output_path}`
if [ -z "$files" ]; then
# 該版本號(hào)下沒(méi)打過(guò)包,把該版本號(hào)文件夾刪除
rm -rf "${Version_output_path}"
fi
}
if [ $? = 0 ]; then
echo -e "\033[32m************* xcodebuild clean 完成 **************\033[0m"
echo ''
cd ${Project_path}
else
revertVersionNum;
echo -e "\033[31m************* xcodebuild clean 失敗 **************\033[0m"
echo ''
exit 1;
fi
Workspace_name="${Project_path}/${target_name}.xcworkspace"
Project_name="${Project_path}/${target_name}.xcodeproj"
if [[ ${Edit} == "YES" ]]; then
if [[ ${Archive_type} == "workspace" ]];then
#編譯workspace
xcodebuild -workspace "${Workspace_name}" -scheme "${Scheme_name}" -configuration "${Configuration}" >> $Log_path
else
#編譯project
xcodebuild -configuration "${Configuration}" >> $Log_path
fi
if [ $? = 0 ]; then
echo -e "\033[32m************* xcodebuild 編譯 完成 **************\033[0m"
echo ''
else
revertVersionNum;
echo -e "\033[31m************* xcodebuild 編譯 失敗 **************\033[0m"
echo ''
exit 1;
fi
fi
echo -e "\033[36m************* 打包 版本號(hào) ${Project_version} **************\033[0m"
echo "************* 開(kāi)始導(dǎo)出xcarchive文件 *************"
if [[ ${Archive_type} == "workspace" ]];then
#打包workspace
xcodebuild archive -workspace "${Workspace_name}" -scheme "${Scheme_name}" -configuration "${Configuration}" -archivePath "${Archive_path}" >> $Log_path
else
#打包project
xcodebuild archive -project "${Project_name}" -scheme "${Scheme_name}" -configuration "${Configuration}" -archivePath "${Archive_path}" >> $Log_path
fi
if [ $? = 0 ]; then
echo -e "\033[32m************* 導(dǎo)出xcarchive文件 完成 **************\033[0m"
echo ''
else
revertVersionNum;
echo -e "\033[31m************* 導(dǎo)出xcarchive文件 失敗 **************\033[0m"
echo ''
exit 1;
fi
echo "************* 開(kāi)始導(dǎo)出 ipa 文件 **************"
# xcodebuild -exportArchive -archivePath "${Archive_path}" -exportPath "${IPA_path}" -exportOptionsPlist "${ExportOptionsPlist}" &>/dev/null
xcodebuild -exportArchive -archivePath "${Archive_path}" -exportPath "${IPA_path}" -exportOptionsPlist "${ExportOptionsPlist}" >> $Log_path
if [ $? = 0 ]; then
echo -e "\033[32m************* 導(dǎo)出 ipa 包完成 **************\033[0m"
echo ''
else
revertVersionNum;
echo -e "\033[31m************* 導(dǎo)出 ipa 包失敗 **************\033[0m"
echo ''
exit 1;
fi
#輸出總用時(shí)
echo -e "\033[32m************* 打包完成. 耗時(shí): ${SECONDS}s **************\033[0m"
echo ''
腳本詳解
-
開(kāi)始部分,對(duì)輸入?yún)?shù)進(jìn)行了判斷,腳本命令:
[]表示可選參數(shù),<>表示必填參數(shù)./Release.sh <Project directory name> [-w] [-s <Name>] [-e] [-d] [-a] [-b <Build number>] [-v <Version number>] 第二步,判斷并修改Build號(hào)以及Version號(hào),
-b <Build number> -v <Version number>,分別取對(duì)應(yīng)的值;如果是-a則Version號(hào)自增,在舊版本號(hào)的基礎(chǔ)上+1,其中的主版本號(hào)、副版本號(hào)、發(fā)布號(hào)默認(rèn)采用的是10進(jìn)制規(guī)則,舉例
1.0.0執(zhí)行-a后為1.0.1
1.1.9執(zhí)行-a后為1.2.0
導(dǎo)出xcarchive文件,這里要區(qū)分是project項(xiàng)目,還是workspace項(xiàng)目,如果你使用了CocoPads管理項(xiàng)目,那么打包的就是workspace項(xiàng)目
if [[ ${Archive_type} == "workspace" ]];then
#打包workspace
xcodebuild archive -workspace "${Workspace_name}" -scheme "${Scheme_name}" -configuration "${Configuration}" -archivePath "${Archive_path}" >> $Log_path
else
#打包project
xcodebuild archive -project "${Project_name}" -scheme "${Scheme_name}" -configuration "${Configuration}" -archivePath "${Archive_path}" >> $Log_path
fi
${Workspace_name}是.xcworkspace文件的完整路徑(${Project_name}一樣),${Scheme_name}表示項(xiàng)目Scheme的名稱(chēng),${Configuration}有兩個(gè)值:Debug和Release,${Archive_path}是生成的xcarchive文件的導(dǎo)出路徑,>> $Log_path表示將log日志輸出到Log_path文件-
生成ipa文件
xcodebuild -exportArchive -archivePath "${Archive_path}" -exportPath "${IPA_path}" -exportOptionsPlist "${ExportOptionsPlist}" >> $Log_path
${ExportOptionsPlist}指向ReleaseDir文件夾下的ExportOptions.plist文件,可以在文件內(nèi)填寫(xiě)跟打包相關(guān)的配置
QQ20170303-150057@2x.png
compileBitcode:不上架App Store,Xcode是否啟用Bitcode重新編譯,默認(rèn)為YES。
method:歸檔類(lèi)型,包括app-store、ad-hoc、
package、enterprise、development以及developer-id。
uploadBitcode:上線(xiàn)App Store是否開(kāi)啟Bitcode,默認(rèn)為YES。
uploadSymbols:上線(xiàn)App Store,是否開(kāi)啟符號(hào)序列化,這是與查crash相關(guān)的,默認(rèn)為YES。
關(guān)于更多的xcodebuild指令,可以通過(guò)xcodebuild -help查看。
最后附上兩張打包成功的圖片


腳本資源??看這里。另外補(bǔ)充一點(diǎn),腳本打包前請(qǐng)先在Xcode,General—Targets—Signing中選擇好對(duì)應(yīng)的證書(shū)。
