iOS自动化编译

http://www.cnblogs.com/brycezhang/p/4097487.htmljavascript

最近研究了一下iOS的自动化编译,目的是为了简化测试和开发的同窗沟通协调的次数,实现测试同窗能够随时从网页操做编译SVN最新源码并打包ipa进行测试。
具体思路是经过从配置文件读取须要编译的项目配置列表展现在网页上,测试同窗选择须要编译的项目,肯定后将选择项目的相关参数传入shell脚本运行,编译完成自动跳转至下载页面。
主要流程包括:php

  1. Shell脚本的编写。经过xcodebuild和xcrun实现自动编译并打包。
  2. PHP调用脚本。开启OS X自带的Apache服务器,编写PHP来调用shell。

编写shell脚本

自动编译其实就是使用了xcodebuild的相关命令来实现编译功能,再使用xcrun来将app打包成ipa。
xcodebuild的官方文档见这里css

用到的关键命令

  • 编译workspacehtml

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

    xcodebuild -target targetname -configuration [-configuration configurationname] clean build SYMROOT=(SYMROOT)
  • 查看配置信息python

    xcodebuild -list
  • xcrun打包ipanginx

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

完整的shell脚本稍长放在文章的最后,该脚本改自BashShell
须要注意,脚本中的路径最好使用绝对路径。git

配置Apache和PHP

启动Apache

启动:sudo apachectl start 中止:sudo apachectl stop 重启:sudo apachectl restart

文件根目录系统级的根目录

http://localhosts/github

对应的文件目录是:
/Library/WebServer/Documents/docker

系统级根目录默认没有开启目录列表,开启方法:
编辑 /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文件所在目录及目录下的全部文件都提高权限,不然脚本会报权限错误。具体步骤以下:

  1. 打开目录 /private/etc/apache2
  2. 打开文件 httpd.conf
    找到

    User _www Group _www

    修改_www为你的登陆用户名

    User <登陆用户名>
  3. 从命令行重启Apache

    sudo apachectl restart
  4. 提高网站目录权限。由于个人网站根目录就是上文提到的用户的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 <?xml version="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>

Posted by Bryce Zhang 
版权声明:自由转载-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0

 
分类:  iOS
标签:  iOS
好文要顶  关注我  收藏该文   
7
0
 
(请您对文章作出评价)
 
« 上一篇: Model-View-ViewModel for iOS [译]
» 下一篇: 使用CocoaPods开发并打包静态库
posted @  2014-11-14 15:54 brycezhang 阅读(3965) 评论(7) 编辑 收藏

 
 
#1楼   2015-05-16 03:22 IOS应用开发  
y请问一下,怎么在打包请,修改 plist 的版本号呢!!!
#2楼   2015-05-16 03:22 IOS应用开发  
请问一下,怎么在打包前,修改 plist 的版本号呢!!!
#3楼   2015-06-23 14:59 heyuan110.com  
同窗,这打包的脚本是来自个人github,写个引用出去啊~
https://github.com/heyuan110/BashShell
#4楼 [ 楼主] 2015-06-23 15:15 brycezhang  
@醉吻花颜
文中有给出提示和连接,“完整的shell脚本稍长放在文章的最后,该脚本改自BashShell。”,可能写的不够明显,给你形成困惑请谅解。由于对原脚本有修改,因此文中贴出了调整后的源码。另外感谢你的开源,当时已star表示了支持。
#5楼   2015-06-23 15:30 heyuan110.com  
@brycezhang
^0^,你总结的很不错啦,没注意看star哈,这个已经优化了部分地方,好比路径不须要手动设置了,能够自动搜索到项目路径,还有打包ipa时查找名称可能不对,打包出错,已经修改了,新的我忘更新了-_-

如今改为了传到fir上了,由于咱们的部分测试在国外,只内网不行。fir.im这个平台够简洁我喜欢~,感受比那个蒲公英强很多,最方便的是提供命令行直接上传。对了若是搭在内网感受用nginx好像比你上面讲用apache要方便很多,你能够试试。

编译完成打开下载页面本身看感受没什么卵用啊~,最好仍是自动发个邮件给全部测试人员来干活啦~0~
#6楼 [ 楼主] 2015-06-23 15:55 brycezhang  
@醉吻花颜
抽空去翻一下你的新源码,最近我也正在考虑使用fir.im,既然你已经实现了,看来我又要拿来主义了,哈哈。
#7楼   2015-06-23 19:15 heyuan110.com  
@brycezhang

已经更新了,把python发邮件的也一块儿放进去了。
 
 
发表评论

昵称:

评论内容:
引用  粗体 
相关文章
相关标签/搜索