iOS自动化编译

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

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

  1. Shell脚本的编写。通过xcodebuild和xcrun实现自动编译并打包。
  2. 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文件所在目录及目录下的所有文件都提升权限,否则脚本会报权限错误。具体步骤如下:

  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>
时间: 2024-10-26 09:16:33

iOS自动化编译的相关文章

初识ios自动化(一)

Appium进行自动化测试有两个好处: 1. Appium在不同平台中使用了标准的自动化APIs,所以在跨平台时,不需要重新编译或者修改自己的应用. 2. Appium支持Selenium WebDriver支持的所有语言,如java.Object-C.JavaScript.Php.Python.Ruby.C#.Clojure,或者Perl语言,更可以使用Selenium WebDriver的Api.Appium支持任何一种测试框架.如果只使用Apple的UIAutomation,我们只能用ja

Mac Jenkins+fastlane 简单几步实现iOS自动化打包发布

最近在使用jenkins 实现ios自动化打包发布蒲公英过程实践遇到了一些坑,特意记录下来方便有需要的人. 进入正题: 一.安装Jenkins 1.Mac上安装Jenkins 遇到到坑 因为 Jenkins 的pkg安装包默认安装位置为shared文件夹,后续会遇到权限问题,所以放弃pkg安装.(说明:也许是安装时选择了本机所有用户可用的原因) 如果已经使用pkg文件方式安装了,卸载方法:找到该文件 /Library/Application Support/Jenkins/Uninstall.c

开启iOS自动化测试之门

开启iOS appium自动化测试之门 Appium介绍 Appium是一个开源.跨平台的测试框架,可以用来测试原生及混合的移动端应用.Appium支持iOS.Android及FirefoxOS平台.Appium使用WebDriver的json wire协议,来驱动Apple系统的UIAutomation库.Android系统的UIAutomator框架.Appium对iOS系统的支持得益于Dan Cuellar’s对于iOS自动化的研究.Appium也集成了Selendroid,来支持老and

如何把iOS代码编译为Android应用

新闻 <iPhone 6/6 Plus中国销量曝光:单月销量650万>:据iSuppli Corp.中国研究总监王阳爆料,iPhone 6和iPhone 6 Plus在国内受欢迎的情况大大超过预期,苹果在中国的单月销量在11月超过了650万台,全年在中国的销量将超过4000万台. 教程 < Date parsing performance on iOS>:文章介绍了一个关于格式化时间的”黑科技“,作者使用 sqlite 替换 NSDateFormatter,提高了16倍的转换效率.

Makeflie自动生成依赖,自动化编译

在netbeans里开发,有一个重要文件makefile,是用来编译所有的文件. 项目的目录结构如下,扁平的目录结构,如何实现自动化编译,写makefile呢? 第一版 基础版: CC = g++ CFLAGS = -O3 -DNDEBUG SOURCE =AdaBoost.cpp aodeselect.cpp sample.cpp vfan.cpp kdbext2.cpp tan_gen.cpp petal: ${SOURCE} $(CC) -o [email protected] ${SOU

Gradle Android最新自动化编译脚本教程

转自:http://blog.csdn.net/changemyself/article/details/39927381 一.前言 Gradle 是以 Groovy 语言为基础,面向Java应用为主.基于DSL(领域特定语言)语法的自动化构建工具. 上面这句话我觉得写得很官方,大家只需知道Gradle可以用来android开发中进行多个项目依赖的自动化编译脚本,知道这点也就知道我们使用它的目的: 为什么不使用Ant做自动化编译脚本,因为ant上手快,但是维护起来太不方便了,有了Gradle你可

iOS下编译ffmpeg

网络上搜索“ios ffmpeg 编译”,文章一大把,但我编译还是费了很大的功夫才编译成功.很多文章只是把步骤列了出来,但是每个人的系统环境,或者程序版本都不一样,结果出现各种的错误.我把自己编译过程中碰到错误以及解决办法写出来,希望有需要的朋友少走弯路. 1.下载ffmpegc-master包 ffmpegc-master github链接:https://github.com/lvjian700/ffmpegc 包里面已经包含了install-gas.sh和install-ffmpeg.sh

生成Makefile自动化编译文件

makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率.make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make.可见,makefile都成为了一种在工程方面的编译方法. 那么如何才能生成Makefile文件呢??好吧,让我们一起进入今天的正题吧! 1.首先生成一个目录:mkdi

转载:ios程序编译链接参数 all_load 的 ld duplicate symbol _main 的 bug及修复

转载自:http://www.cnblogs.com/dabaopku/archive/2012/12/12/2813940.html ios程序编译链接参数 all_load 的 ld duplicate symbol _main 的 bug及修复 问题 -all_load 是在Objective-C 编译时常用到的一个参数,比如这篇文章所介绍的,生成静态库的一些问题-all_load.但是我们在加入这个参数后,有时会出现"ld: duplicate symbol _main"的错误