zoukankan      html  css  js  c++  java
  • 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 "
    " >>$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 "
    " >>$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

     
  • 相关阅读:
    React视角下的轮播图
    深入理解JSX
    React.js入门笔记(再续):评论框的实现
    React.js入门笔记(续):用React的方式来思考
    React.js入门笔记
    jquery实现简单瀑布流布局(续):图片懒加载
    jquery实现简单瀑布流布局
    Node.js入门笔记(6):web开发方法
    使用X-UA-Compatible来设置IE浏览器兼容模式
    $(window).load(function() {})和$(document).ready(function(){})的区别
  • 原文地址:https://www.cnblogs.com/Keys/p/4937093.html
Copyright © 2011-2022 走看看