iOS自动化编译

王朝学院·作者佚名  2016-08-27
窄屏简体版  字體: 小  |  中  |  大  |  超大  

最近研究了一下iOS的自动化编译,目的是为了简化测试和开发的同学沟通协调的次数,实现测试同学可以随时从网页操作编译SVN最新源码并打包ipa进行测试。

具体思路是通过从配置文件读取需要编译的项目配置列表展示在网页上,测试同学选择需要编译的项目,确定后将选择项目的相关参数传入shell脚本运行,编译完成自动跳转至下载页面。

主要流程包括:

Shell脚本的编写。通过xcodebuild和xcrun实现自动编译并打包。

php调用脚本。开启OS X自带的Apache服务器,编写PHP来调用shell。编写shell脚本自动编译其实就是使用了xcodebuild的相关命令来实现编译功能,再使用xcrun来将app打包成ipa。

xcodebuild的官方文档见这里。

用到的关键命令编译workspace

xcodebuild -workspace workspacename -scheme schemename -configuration [-configuration configurationname] clean build SYMROOT=(SYMROOT)

编译PRoject

xcodebuild -target targetname -configuration [-configuration configurationname] clean build SYMROOT=(SYMROOT)

查看配置信息

xcodebuild -list

xcrun打包ipa

xcrun -sdk iphoneos Packageapplication-v projectName.app -o ipaName.ipa

完整的shell脚本稍长放在文章的最后,该脚本改自BashShell。

需要注意,脚本中的路径最好使用绝对路径。

配置Apache和PHP启动Apache启动:sudo apachectl start

停止:sudo apachectl stop

重启:sudo apachectl restart

文件根目录系统级的根目录http://localhosts/

对应的文件目录是:

/Library/WebServer/Documents/

系统级根目录默认没有开启目录列表,开启方法:

编辑/etc/apache2/httpd.conf文件

搜索找到<Directory "/Library/WebServer/Documents">

将Options Multiviews修改为Options Indexes Multiviews

用户级根目录另一个 Web 根目录默认是~/Sites,10.9 中你需要手动创建这个Sites目录。

检查这个目录下是否有username.conf文件

/etc/apache2/users/

如果没有,则需要新建一个,username需要是你的账户名字,建议使用终端创建这个文件:

cd /etc/apache2/users

sudo vi username.conf

贴入以下内容,注意修改username为你的账户名字

<Directory "/Users/username/Sites/">

Options Indexes MultiViews FollowSymLinksAllowOverride AllOrder allow,denyAllow from allRequire all granted

</Directory>

这个文件的权限应该是:

-rw-r--r-- 1 root wheel 298 Jun 28 16:47 username.conf

如果不是,请修改

sudo chmod 644 username.conf

编辑/etc/apache2/httpd.conf文件,删除下列这些代码前的注释符号:#

Include /private/etc/apache2/extra/httpd-userdir.conf

LoadModule authz_core_module libexec/apache2/mod_authz_core.soLoadModule authz_host_module libexec/apache2/mod_authz_host.soLoadModule userdir_module libexec/apache2/mod_userdir.so

编辑/etc/apache2/extra/httpd-userdir.conf文件,删除下列这些代码前的注释符号:#

Include /private/etc/apache2/users/*.conf

重启Apache

sudo apachectl restart

这时,这个网址应该已经可以用了:

http://localhost/~username/

PHP调用shell脚本这里主要用到了PHP的system命令:system($cmd)

PHP调用shell的权限是比较低的,我们的shell里会需要创建文件及文件夹的权限,解决办法是通过命令行将PHP文件所在目录及目录下的所有文件都提升权限,否则脚本会报权限错误。具体步骤如下:

打开目录

/private/etc/apache2

打开文件httpd.conf

找到

User _www

Group _www

修改_www为你的登录用户名

User <登录用户名>

从命令行重启Apache

sudo apachectl restart

提升网站目录权限。因为我的网站根目录就是上文提到的用户的Sites文件,因此执行以下命令

sudo chmod 775 ~/Sites

sudo chmod 775 ~/Sites/*

好了,权限问题解决了。当满怀信心看到从网页调用脚本输出信息的时候,结果又报了无法找到证书的错误,OMG,但从终端调用脚本就可以成功,起初以为权限不够导致无法调用证书,绕了一大圈后发现这个问题只是因为钥匙串中的证书一般安装在登录下,只需要移动到系统下就行了。

关于从SVN仓库获取源码的部分就不写了,既然都可以调用脚本了,这部分就也很简单了。

这个过程还是比较折腾的,希望这篇文章能够save your time :)

最后奉上相关文件的源码。

Shell脚本文件(buildtool.sh)#!/bin/sh

export LC_ALL=zh_CN.GB2312;export LANG=zh_CN.GB2312

username=用户名

###############配置项目名称和路径等相关参数

projectName=$1 #项目所在目录的名称

isWorkSpace=$2 #判断是用的workspace还是直接project,workspace设置为true,否则设置为false

projectDir=/Users/${username}/workspace/projects/$3/ #项目所在目录的绝对路径

buildConfig=$4 #编译的方式,默认为Release,还有Debug等

###############配置下载的文件名称和路径等相关参数

wwwIPADir=/Users/${username}/Sites/$projectName #html,ipa,icon,plist最后所在的目录绝对路径

url="http://localhost/${projectName}" #下载路径

##########################################################################################

##############################以下部分为自动生产部分,不需要手动修改############################

##########################################################################################

####################### FUCTION START #######################

replaceString(){

local inputString=$1

result=${inputString//(/}

result=${result//)/}

echo $result

}

date_Y_M_D_W_T()

{

WEEKDAYS=(星期日 星期一 星期二 星期三 星期四 星期五 星期六)

WEEKDAY=$(date +%w)

DT="$(date +%Y年%m月%d日) ${WEEKDAYS[$WEEKDAY]} $(date "+%H:%M:%S")"

echo "$DT"

}

####################### FUCTION END #######################

###Log的路径,如果发现log里又乱码请在终端执行:export LC_ALL=zh_CN.GB2312;export LANG=zh_CN.GB2312

logDir=/Users/${username}/workspace/xcodebuild

mkdir -pv $logDir

logPath=$logDir/$projectName-$buildConfig.log

echo "~~~~~~~~~~~~~~~~~~~开始编译~~~~~~~~~~~~~~~~~~~" >>$logPath

loginInfo=`who am i`

loginUser=`echo $loginInfo |awk '{print $1}'`

echo "登陆用户:$loginUser" >>$logPath

loginDate=`echo $loginInfo |awk '{print $3,$4,$5}'`

echo "登陆时间:$loginDate" >>$logPath

loginServer=`echo $loginInfo |awk '{print $6}'`

if [ -n "$loginServer" ]; then

echo "登陆用户IP:$(replaceString $loginServer)" >>$logPath

else

echo "登陆用户IP:localhost(127.0.0.1)" >>$logPath

fi

if [ -d "$logDir" ]; then

echo "${logDir}文件目录存在"

else

echo "${logDir}文件目录不存在,创建${logDir}目录成功"

echo "创建${logDir}目录成功" >>$logPath

fi

echo "<br />"

###############检查html等文件放置目录是否存在,不存在就创建

echo "开始时间:$(date_Y_M_D_W_T)" >>$logPath

echo "项目名称:$projectName" >>$logPath

echo "编译模式:$buildConfig" >>$logPath

echo "开始目录检查........" >>$logPath

if [ -d "$wwwIPADir" ]; then

echo "文件目录存在" >>$logPath

else

echo "文件目录不存在" >>$logPath

mkdir -pv $wwwIPADir

echo "创建${wwwIPADir}目录成功" >>$logPath

fi

###############进入项目目录

rm -rf ./build

buildAPPToDir=/Users/${username}/workspace/build/$projectName #编译打包完成后.archive .ipa文件存放的目录

###############获取版本号,bundleID

infoPlist="${projectDir}${projectName}/$projectName-Info.plist"

bundleDisplayName=`/usr/libexec/PlistBuddy -c "Print CFBundleDisplayName" $infoPlist`

bundleVersion=`/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" $infoPlist`

bundleIdentifier=`/usr/libexec/PlistBuddy -c "Print CFBundleIdentifier" $infoPlist`

bundleBuildVersion=`/usr/libexec/PlistBuddy -c "Print CFBundleVersion" $infoPlist`

###############在网页上显示的名字和bundleDisplayName一致

appName=$bundleDisplayName

echo "$bundleDisplayName"

###############开始编译app

if $isWorkSpace ; then #判断编译方式

echo "开始编译workspace...." >>$logPath

echo "$projectDir$projectName.xcworkspace"

xcodebuild -workspace ${projectDir}$projectName.xcworkspace -scheme $projectName -configuration $buildConfig clean build SYMROOT=$buildAppToDir

else

echo "开始编译target...." >>$logPath

cd ${projectDir}

xcodebuild -target $projectName -configuration $buildConfig clean build SYMROOT=$buildAppToDir

fi

#判断编译结果

if test $? -eq 0

then

echo "~~~~~~~~~~~~~~~~~~~编译成功~~~~~~~~~~~~~~~~~~~"

else

echo "~~~~~~~~~~~~~~~~~~~编译失败~~~~~~~~~~~~~~~~~~~" >>$logPath

echo "\n" >>$logPath

exit 1

fi

###############开始打包成.ipa

ipaName=`echo $projectName | tr "[:upper:]" "[:lower:]"` #将项目名转小写

appDir=$buildAppToDir/$buildConfig-iphoneos #app所在路径

echo "开始打包$projectName.xcarchive成$projectName.ipa....." >>$logPath

xcrun -sdk iphoneos PackageApplication -v $appDir/$projectName.app -o $appDir/$ipaName.ipa #将app打包成ipa

###############开始拷贝到目标下载目录

iconName="icon.png" #icon名称

iconSize=100 #icon大小

#unzipAppDir=$appDir/$projectName.app

unzipAppDir=$projectDir

iconImages=($(find $unzipAppDir -path "$buildAppToDir" -prune -o -type f -size +1k -name "*[iI]con*.png" |xargs ls -lSar| grep ^-)) #查找带Icon或icon的图标,取最大的图片,忽略build目录,按大小排序输出

#iconImages=($(find $unzipAppDir -size +1k -name "*[iI]con*.png")) #查找带Icon或icon的图标,取最大的图片

iconImagesLength=${#iconImages[@]} #获取数组的count

cp -f -p ${iconImages[iconImagesLength-1]} $wwwIPADir/$iconName #拷贝icon.png文件

#检查文件是否存在

if [ -f "$appDir/$ipaName.ipa" ]

then

echo "打包$ipaName.ipa成功." >>$logPath

else

echo "打包$ipaName.ipa失败." >>$logPath

exit 1

fi

cp -f -p $appDir/$ipaName.ipa $wwwIPADir/$ipaName.ipa #拷贝ipa文件

echo "复制$ipaName.ipa到${wwwIPADir}成功" >>$logPath

###############计算文件大小和最后更新时间

fileSize=`stat $appDir/$ipaName.ipa |awk '{if($8!=4096){size=size+$8;}} END{print "文件大小:", size/1024/1024,"M"}'`

lastUpdateDate=`stat $appDir/$ipaName.ipa | awk '{print "最后更新时间:",$13,$14,$15,$16}'`

echo "$fileSize" >>$logPath

echo "$lastUpdateDate" >>$logPath

plistDir=${wwwIPADir}/$ipaName.plist #plist文件的路径

htmlDir=${wwwIPADir}/index.html #html文件的路径

###############生成PLIST文件

cat << EOF > $plistDir

<?xmlversion="1.0" encoding="UTF-8"?>

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

<plist version="1.0">

<dict>

<key>items</key>

<array>

<dict>

<key>assets</key>

<array>

<dict>

<key>kind</key>

<string>software-package</string>

<key>url</key>

<string>$url/$ipaName.ipa</string>

</dict>

</array>

<key>metadata</key>

<dict>

<key>bundle-identifier</key>

<string>$bundleIdentifier</string>

<key>bundle-version</key>

<string>$bundleVersion</string>

<key>kind</key>

<string>software</string>

<key>title</key>

<string>$appName</string>

</dict>

</dict>

</array>

</dict>

</plist>

EOF

echo "生成plist文件到$plistDir成功" >>$logPath

###############生成html下载页面

cat << EOF > $htmlDir

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

<head>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

<meta id="viewport" name="viewport" content="width=device-width; height=device-height; initial-scale=1.0; "/>

<title>安装$appName</title>

<style type="text/CSS">

</style>

</head>

<body>

<h2>$appName</h2>

<img src="./$iconName" width=$iconSize height = $iconSize>

<ul>

<li><h2><a href="itms-services://?action=download-manifest&amp;url=$url/$ipaName.plist">手机安装$appName(V$bundleVersion.$bundleBuildVersion)</a></h2></li>

<li><h2><a href="$url/$ipaName.ipa">电脑下载IPA包</a></h2></li>

</ul>

<p>

$fileSize

<p>

$lastUpdateDate

</body>

</html>

EOF

echo "生成html文件到$htmlDir成功" >>$logPath

echo "结束时间:$(date_Y_M_D_W_T)" >>$logPath

echo "~~~~~~~~~~~~~~~~~~~结束编译~~~~~~~~~~~~~~~~~~~" >>$logPath

echo "~~~~~~~~~~~~~~~~~~~结束编译,处理成功~~~~~~~~~~~~~~~~~~~"

echo "\n" >>$logPath

echo "$url"

配置文件格式(data.json)[

{

"name":"项目展示的名称1",

"projectname":"项目名称1",

"isworkspace":"是否是workspace",

"foldername":"项目文件夹名1",

"buildconfig":"Release/Debug/其他自定义编译名称"

},

{

"name":"项目展示的名称2",

"projectname":"项目名称2",

"isworkspace":"是否是workspace",

"foldername":"项目文件夹名2",

"buildconfig":"Release/Debug/其他自定义编译名称"

}

]

PHP文件(index.php)<html>

<head>

<title>iOS应用打包</title>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>

</head>

<body style="text-align:center">

<h1>请选择编译项目</h1>

<form name="form1" method="post" action="">

<?php

$filename ='data.json';

$jsonstring = file_get_contents($filename);

$jsondecode = json_decode($jsonstring, true);

for ($i= 0;$i< count($jsondecode); $i++) {

$name = $jsondecode[$i]["name"];

echo "<label> <input type='radio' name='radio' value='$i'> ${name} </label> <br />";

}

if($_POST) {

$value = $_POST['radio'];

$name = $jsondecode[$value]["name"];

$projectname = $jsondecode[$value]["projectname"];

$isworkspace = $jsondecode[$value]["isworkspace"];

$foldername = $jsondecode[$value]["foldername"];

$buildconfig = $jsondecode[$value]["buildconfig"];

echo '<br />即将编译:',$name;

echo '<br />编译完成自动跳转至下载页面<br /><br /><br />';

$cmd = "./buildtool.sh $projectname $isworkspace $foldername $buildconfig";

$url = system($cmd);

echo "<script language=\"javascript\">";

echo "location.href=\"$url\"";

echo "</script>";

}

?>

<br />

<input type="submit" name="Submit" value="提交" />

</form>

</body>

</html>

 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
© 2005- 王朝網路 版權所有 導航