iOS多target下的自動構建
專案中測試,開發,本地各種環境每次都需要註釋或者手動切換環境,每天都需要打各種不同環境的包,這些重複而繁瑣的工作,有什麼方法可以避免呢? 本文或許可以幫到你。
一,新建專案
1.1 這裡以Autobuild為例
1.2 配置info.plist檔案,後續複製TARGET會依據此配置
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>NSCameraUsageDescription</key>
<string>App需要您的同意,才能訪問相機</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>App需要您的同意,才能始終訪問位置</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>App需要您的同意,才能在使用期間訪問位置</string>
<key>NSMicrophoneUsageDescription</key>
<string>App需要您的同意,才能訪問麥克風</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>App需要您的同意,才能訪問相簿</string>
1.3 建立不同的TARGET時,有個細節需要注意,那就是你的專案如果是xcworkspace時,先pod install後再建立三個TARGET(釋出,開發,測試),選中當前TARGET,右鍵Duplicate複製TARGET
1,4 選擇第二個Duplicate Only,複製單個
1.5 將Autobuild-live copy更改為Autobuild-live-dev
1,6 重複上述步驟,如下圖依次建立並改名為
Autobuild-live-live(釋出)
Autobuild-live-dev(開發)
Autobuild-live-test(測試)
1.7 將生成的info.plist檔案統一放入Plist檔案並命名,不同的info.plist檔案在右側皮膚選擇不同的TARGET
1.8 Bulid Settings下配置所有TARGET Info.plist的路徑,確保切換所有TARGET均無報錯
$(PROJECT_DIR)/Autobuild/Plist/Autobuild-xxx-Info.plist
1.9 選擇shemes, 點選Manage Schemes
2.0 選中所有shemes,點選 一 刪除,依次重新建立對應每一個TARGET的shemes
2.1 操作完成後如下圖,注意勾選右側Shared共享,不然指令碼沒有許可權讀取相應的scheme許可權。
2.2 到這裡三個TARGET(釋出,開發,測試)已部署完畢,注意以後往專案拖入檔案,如果需要所有環境都能讀取,需要勾選所有TARGET,否則切換到對應的TARGET會報找不到檔案的錯誤!
二,配置預編譯巨集
2.0 Build Setting –> Preprocessor Macros 配置預編譯巨集,這裡命名為STRUCTURE_ENVIRONMENT,Debug與Release模組均需要配置
Autobuild-live-live(釋出) STRUCTURE_ENVIRONMENT=0
Autobuild-live-dev(開發) STRUCTURE_ENVIRONMENT=1
Autobuild-live-test(測試) STRUCTURE_ENVIRONMENT=2
2.1 確保所有環境預編譯巨集均配置正確
2.2 新建ServiceApIConst類, 新增測試程式碼,注意勾選所有環境TARGET
#import <Foundation/Foundation.h>
@interface ServiceApIConst : NSObject
extern NSString *const ServiceApI_Url;
extern NSString *const ServiceWeb_Url;
@end
@implementation ServiceApIConst
/**
環境切換需使用預編譯巨集配置STRUCTURE_ENVIRONMENT
環境統一配置名為:STRUCTURE_ENVIRONMENT = 0為生產環境、1為開發環境、2為測試環境
*/
#ifdef STRUCTURE_ENVIRONMENT
#if STRUCTURE_ENVIRONMENT == 0
NSString *const ServiceApI_Url = @"http://www.api.releaseUrl.xxxxx";
NSString *const ServiceWeb_Url = @"http://www.web.releaseUrl.xxxxx";
#elif STRUCTURE_ENVIRONMENT == 1
NSString *const ServiceApI_Url = @"http://www.api.developmentUrl.xxxxx";
NSString *const ServiceWeb_Url = @"http://www.web.developmentUrl.xxxxx";
#elif STRUCTURE_ENVIRONMENT == 2
NSString *const ServiceApI_Url = @"http://www.api.testUrl.xxxxx";
NSString *const ServiceWeb_Url = @"http://www.web.testUrl.xxxxx";
#endif
#else
#warning"為找到匹配的預編譯巨集"
#endif
@end
2.3 測試輸入結果
#import "ViewController.h"
#import "ServiceApIConst.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"ServiceApI_Url: %@",ServiceApI_Url);
NSLog(@"ServiceWeb_Url: %@",ServiceWeb_Url);
}
@end
2.4 依次切換TARGET,控制檯列印為
2017-05-08 21:40:17.442 Autobuild-live[17950:338866] ServiceApI_Url: http://www.api.releaseUrl.xxxxx
2017-05-08 21:40:17.442 Autobuild-live[17950:338866] ServiceWeb_Url: http://www.web.releaseUrl.xxxxx
2017-05-08 21:40:56.082 Autobuild-dev[18015:340799] ServiceApI_Url: http://www.api.developmentUrl.xxxxx
2017-05-08 21:40:56.082 Autobuild-dev[18015:340799] ServiceWeb_Url: http://www.web.developmentUrl.xxxxx
2017-05-08 21:41:26.510 Autobuild-test[18082:342321] ServiceApI_Url: http://www.api.testUrl.xxxxx
2017-05-08 21:41:26.510 Autobuild-test[18082:342321] ServiceWeb_Url: http://www.web.testUrl.xxxxx
三,自動打包
3.0 指令碼地址:https://github.com/carya/Util.git 解壓過後將autobuild資料夾複製到專案根目錄下
3.1 複製autobuild.py,分別命名為autobuild-dev.py,autobuild-test.py,分別開啟對應的.py檔案修改如下內容
CONFIGURATION = "Release"
.ipa包輸出路徑
autobuild.py: EXPORT_MAIN_DIRECTORY = "~/Desktop/ipa-live/"
autobuild-dev.py: EXPORT_MAIN_DIRECTORY = "~/Desktop/ipa-dev/"
autobuild-test.py: EXPORT_MAIN_DIRECTORY = "~/Desktop/ipa-test/"
如果不需要上傳到蒲公英,將 def uploadIpaToPgyer(ipaPath):程式碼塊內的程式碼註釋
3.2 配置exportOptions.plist
method表示包的用途,上線使用app-store 測試使用 ad-hoc,企業使用 enterprise,預設值為development,需要配置不同的證照環境。
<?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>compileBitcode</key>
<false/>
<key>method</key>
<string>ad-hoc</string>
<key>uploadBitcode</key>
<false/>
<key>uploadSymbols</key>
<false/>
</dict>
</plist>
3.3 配置證照,更改scheme為Release
3.4 確保所有證照配置完畢,關閉Xcode,終端cd到autobuildScript下,根據專案型別選擇下列命令
//xcodeproj專案:
python autobuild.py -p ../youproject.xcodeproj -s schemename
//xcworkspace專案
python autobuild.py -w ../youproject.xcworkspace -s schemename
這裡專案為Autobuild.xcodeproj,你需要指定專案與scheme的名稱,這裡使用釋出環境TARGET對應的Autobuild-live,終端執行如下命令,注意這裡的autobuild.py為上面配置好對應環境的.py檔案,如你需要打測試包,需要使用
python autobuild-test.py ...... 並且將-s後的scheme名稱替換為Autobuild-test
//先切換使用系統的rvm,否則會報錯 error: exportArchive: No applicable devices found
命令1: [[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm" && rvm use system
命令2: python autobuild.py -p ../Autobuild.xcodeproj -s Autobuild-live
3.5 打包成功,終端輸出** EXPORT SUCCEEDED **
3.6 成功後會在上面3.1下設定 EXPORT_MAIN_DIRECTORY的路徑下生成對應的.ipa檔案
四,注意
4.1 拖入專案的檔案如果所有環境都要使用,勾選所有TARGET
4.2 由於切換到系統的rvm會導致pod命令失效或報錯,重啟終端即可
4.3 使用了三個TARGET,每個TARGET都需要在Podfile檔案配置對應的cocopods庫
# Uncomment the next line to define a global platform for your project
platform :ios, '8.0'
# ignoring warning from pods
inhibit_all_warnings!
target 'Autobuild-dev' do
pod 'AFNetworking', '3.1.0'
end
target 'Autobuild-live' do
pod 'AFNetworking', '3.1.0'
end
target 'Autobuild-test' do
pod 'AFNetworking', '3.1.0'
end
五,補充
4.1 報錯找不到request module.
import requests
ImportError: No module named requests
使用 $ sudo pip install requests或者sudo easy_install -U requests
4.2 檢視 project 中的 targets 和 configurations,或者 workspace 中 schemes
xcodebuild -list
4.3 exportOptions.plist檔案配置詳細資訊
xcodebuild --help
4.4 指令碼示例
#!/usr/bin/env python
# -- coding:utf-8 --
#./autobuild.py -p youproject.xcodeproj -s schemename
#./autobuild.py -w youproject.xcworkspace -s schemename
import argparse
import subprocess
import requests
import os
import datetime
#configuration for iOS build setting
CONFIGURATION = "Release"
EXPORT_OPTIONS_PLIST = "exportOptions.plist"
#要打包的TARGET名字
TARGET = 'ipa-live'
#存放路徑以時間命令
DATE = datetime.datetime.now().strftime('%Y-%m-%d_%H.%M.%S')
#1,會在桌面建立輸出ipa檔案的目錄
EXPORT_MAIN_DIRECTORY = "~/Desktop/" + TARGET + DATE
# configuration for pgyer http://www.pgyer.com/doc/api
PGYER_UPLOAD_URL = "http://www.pgyer.com/apiv1/app/upload"
DOWNLOAD_BASE_URL = "http://www.pgyer.com"
USER_KEY = "xxxxx"
API_KEY = "x'x'x'x"
#設定從蒲公英下載應用時的密碼
PYGER_PASSWORD = ""
def cleanArchiveFile(archiveFile):
cleanCmd = "rm -r %s" %(archiveFile)
process = subprocess.Popen(cleanCmd, shell = True)
process.wait()
print "cleaned archiveFile: %s" %(archiveFile)
def parserUploadResult(jsonResult):
resultCode = jsonResult['code']
if resultCode == 0:
downUrl = DOWNLOAD_BASE_URL +"/"+jsonResult['data']['appShortcutUrl']
print "Upload Success"
print "DownUrl is:" + downUrl
else:
print "Upload Fail!"
print "Reason:"+jsonResult['message']
def uploadIpaToPgyer(ipaPath):
print "成功"
#註釋蒲公英上傳
#def uploadIpaToPgyer(ipaPath):
# print "ipaPath:"+ipaPath
# ipaPath = os.path.expanduser(ipaPath)
# ipaPath = unicode(ipaPath, "utf-8")
# files = {'file': open(ipaPath, 'rb')}
# headers = {'enctype':'multipart/form-data'}
# payload = {'uKey':USER_KEY,'_api_key':API_KEY,'publishRange':'2','isPublishToPublic':'2', 'password':PYGER_PASSWORD}
# print "uploading...."
# r = requests.post(PGYER_UPLOAD_URL, data = payload ,files=files,headers=headers)
# if r.status_code == requests.codes.ok:
# result = r.json()
# parserUploadResult(result)
# else:
# print 'HTTPError,Code:'+r.status_code
#建立輸出ipa檔案路徑: ~/Desktop/{scheme}{2016-12-28_08-08-10}
def buildExportDirectory(scheme):
dateCmd = 'date "+%Y-%m-%d_%H-%M-%S"'
process = subprocess.Popen(dateCmd, stdout=subprocess.PIPE, shell=True)
(stdoutdata, stderrdata) = process.communicate()
exportDirectory = "%s" %(EXPORT_MAIN_DIRECTORY)
return exportDirectory
def buildArchivePath(tempName):
process = subprocess.Popen("pwd", stdout=subprocess.PIPE)
(stdoutdata, stderrdata) = process.communicate()
archiveName = "%s.xcarchive" %(tempName)
archivePath = stdoutdata.strip() + '/' + archiveName
return archivePath
def getIpaPath(exportPath):
cmd = "ls %s" %(exportPath)
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
(stdoutdata, stderrdata) = process.communicate()
ipaName = stdoutdata.strip()
ipaPath = exportPath + "/" + ipaName
return ipaPath
def exportArchive(scheme, archivePath):
exportDirectory = buildExportDirectory(scheme)
exportCmd = "xcodebuild -exportArchive -archivePath %s -exportPath %s -exportOptionsPlist %s" %(archivePath, exportDirectory, EXPORT_OPTIONS_PLIST)
process = subprocess.Popen(exportCmd, shell=True)
(stdoutdata, stderrdata) = process.communicate()
signReturnCode = process.returncode
if signReturnCode != 0:
print "export %s failed" %(scheme)
return ""
else:
return exportDirectory
def buildProject(project, scheme):
archivePath = buildArchivePath(scheme)
print "archivePath: " + archivePath
archiveCmd = 'xcodebuild -project %s -scheme %s -configuration %s archive -archivePath %s -destination generic/platform=iOS' %(project, scheme, CONFIGURATION, archivePath)
process = subprocess.Popen(archiveCmd, shell=True)
process.wait()
archiveReturnCode = process.returncode
if archiveReturnCode != 0:
print "archive workspace %s failed" %(workspace)
cleanArchiveFile(archivePath)
else:
exportDirectory = exportArchive(scheme, archivePath)
cleanArchiveFile(archivePath)
if exportDirectory != "":
ipaPath = getIpaPath(exportDirectory)
uploadIpaToPgyer(ipaPath)
def buildWorkspace(workspace, scheme):
archivePath = buildArchivePath(scheme)
print "archivePath: " + archivePath
archiveCmd = 'xcodebuild -workspace %s -scheme %s -configuration %s archive -archivePath %s -destination generic/platform=iOS' %(workspace, scheme, CONFIGURATION, archivePath)
process = subprocess.Popen(archiveCmd, shell=True)
process.wait()
archiveReturnCode = process.returncode
if archiveReturnCode != 0:
print "archive workspace %s failed" %(workspace)
cleanArchiveFile(archivePath)
else:
exportDirectory = exportArchive(scheme, archivePath)
cleanArchiveFile(archivePath)
if exportDirectory != "":
ipaPath = getIpaPath(exportDirectory)
uploadIpaToPgyer(ipaPath)
def xcbuild(options):
project = options.project
workspace = options.workspace
scheme = options.scheme
if project is None and workspace is None:
pass
elif project is not None:
buildProject(project, scheme)
elif workspace is not None:
buildWorkspace(workspace, scheme)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("-w", "--workspace", help="Build the workspace name.xcworkspace.", metavar="name.xcworkspace")
parser.add_argument("-p", "--project", help="Build the project name.xcodeproj.", metavar="name.xcodeproj")
parser.add_argument("-s", "--scheme", help="Build the scheme specified by schemename. Required if building a workspace.", metavar="schemename")
options = parser.parse_args()
print "options: %s" % (options)
xcbuild(options)
if __name__ == '__main__':
main()
相關文章
- iOS 自動構建命令——xcodebuildiOSXCodeUI
- iOS 配置多targetiOS
- iOS自動構建打包釋出指令碼iOS指令碼
- hexo配合github action 自動構建(多種形式)HexoGithub
- 【IDL】 自動構建泰森多邊形(Voronoi Polygon)Go
- iOS 自動化釋出 Fastlane 本地構建 IPA 並分發iOSAST
- 用 Jenkins 自動化構建 Android 和 iOS 應用JenkinsAndroidiOS
- [IOS]要多簡單有多簡單的IOS自動化calabash-iosiOS
- iOS的MVC框架之控制層的構建(下)iOSMVC框架
- Jenkins + GitHub 自動構建JenkinsGithub
- Jenkins github自動構建JenkinsGithub
- 自動化構建映象:Packer
- 構建高效的自動化測試框架框架
- ispriter自動構建css-spriteCSS
- 新版的Django Docker部署方案,多階段構建、自動處理前端依賴DjangoDocker前端
- 使用Gym和CNN構建多智慧體自動駕駛馬里奧賽車CNN智慧體自動駕駛
- Mac 環境下 Android 使用 Jenkins 構建自動化打包MacAndroidJenkins
- Window下采用ant 指令碼構建Android自動化編譯指令碼Android編譯
- 如何構建自動化的前端開發流程前端
- Webpack自動化構建實踐指南Web
- 淺談自動化構建之grunt
- 淺談自動化構建之gulp
- git+jenkins自動構建二GitJenkins
- Grunt自動化構建環境搭建
- jenkins流水線自動構建配置Jenkins
- 動態構建的多頁面vue-cli模版Vue
- 利用fastlane進行專案的自動化構建AST
- 自動平衡二叉樹的構建-AVL樹二叉樹
- Jenkins自動化前端專案構建Jenkins前端
- Android Jenkins自動化構建之路AndroidJenkins
- 使用Hudson搭建自動構建伺服器伺服器
- 使用Gulp構建前端自動化解決方案前端
- 使用ChatGPT自動構建知識圖譜ChatGPT
- 知識圖譜構建下的自動問答KBQA系統實戰-文輝
- 前端自動化:Node 命令列前端自動構建釋出系統前端命令列
- 關於自動化構建的思維導圖解析圖解
- 教你如何搭建一個自動化構建的部落格
- 總結下 ui 自動化驅動架構UI架構