iOS 使用脚本自动化复制target
有些项目成熟以后,就会有需求自动化配置生成一个全新的项目,不需要再让开发人员手动修改工程文件,将配置化工作直接移交给运维或者配置团队去做
其实按照普通的做法,无非就是在xcode里将目标target duplicate一下,然后修改相关的项目名称、target名称、bundleid等等,这些内容其实在xcodeproj文件中都有对应的配置信息,所以我们可以通过直接修改Xcodeproj直接文件的方式实现
首先感谢虾神 提供了详细的原理解说和工具介绍,在实现时少走了很多弯路,有兴趣的同学可以前往 虾神 的文章学习,这里我把最终实现贴出来供大家参考~~
希望进一步学习ios工程配置和脚本相关的同学建议去这里:
#!/usr/bin/env ruby
require 'rubygems'
require 'xcodeproj'
require 'fileutils' #----------------------------------- 目标项目配置内容----------------------------#
name = "newyorktoon"
displayname = "纽约通"
target_toonType = 10001
target_pushType = "hello"
target_channel = "hello"
target_mapKey = "hello"
target_schemeType = "hello"
#----------------------------------- 目标项目配置内容----------------------------# # 模板项目
# srcname = "tzhqtoon"
# srcdisplayname = "后勤通" #project
project_path = "Hello.xcodeproj"
# 复制资源文件,注意:
# 1. 复制资源文件时需要排除源资源文件
# 2. 在此文件的最后面将复制出来的资源文件添加到目标target
targetdir = "TNTarget/#{name}"
srcroot = "TNTarget/#{srcname}" # 复制资源文件夹,将源target下的图片资源文件夹复制到目标target目录
if !Dir.exists?(targetdir)
Dir.mkdir(targetdir)
end
codeDirs = [
"#{srcroot}/Resources",
"#{srcroot}/NetWork",
"#{srcroot}/TabbarSetDataSource",
"#{srcroot}/TNHQHome"
]
#复制源target目录下的定制化代码目录到目标target目录
hasAllListFiles = false
codeDirs.each do |d|
hasAllListFiles = Dir.exists?(d)#-> 此处假设所有的code file为一个整体,一有具有
if hasAllListFiles
FileUtils.cp_r d, targetdir
end
end # 寻找模板target
proj = Xcodeproj::Project.open(project_path)
src_target = proj.targets.find { |item| item.to_s == srcname }
# 创建目标target
target = proj.new_target(src_target.symbol_type, name, src_target.platform_name, src_target.deployment_target)
target.product_name = name # create scheme
scheme = Xcodeproj::XCScheme.new
scheme.add_build_target(target)
scheme.set_launch_target(target)
scheme.save_as(project_path, name) # build_configurations
target.build_configurations.map do |item| #设置target相关配置
item.build_settings.update(src_target.build_settings(item.name))
# puts "-"*30 + "#{item.build_settings}" +"_"*30
item.build_settings["PRODUCT_BUNDLE_IDENTIFIER"] = "com.abc.aa.#{name}"
item.build_settings["PRODUCT_NAME"] =displayname targetInfoPlist = item.build_settings["INFOPLIST_FILE"]
item.build_settings["INFOPLIST_FILE"] = targetInfoPlist.sub(srcname, name) puts "-"*30 + "#{item.build_settings['PRODUCT_BUNDLE_IDENTIFIER']}" +"_"*30
puts "-"*30 + "#{item.build_settings['PRODUCT_NAME']}" +"_"*30
end # build_phases
phases = src_target.build_phases.reject { |x| x.instance_of? Xcodeproj::Project::Object::PBXShellScriptBuildPhase }.collect(&:class) #复制源target引用的source和resource文件引用
phases.each do |klass|
puts "||---------------------> copy phases #{klass}--------------------||"
src = src_target.build_phases.find { |x| x.instance_of? klass }
dst = target.build_phases.find { |x| x.instance_of? klass } unless dst
dst ||= proj.new(klass)
target.build_phases << dst
end
dst.files.map { |x| x.remove_from_project } idx = 1
src.files.each do |f|
# 排除文件,将源target中的文件排除,不引用该文件
if f.file_ref and f.file_ref.hierarchy_path.index(srcroot) != nil
puts "\n................... ignore file: #{f.file_ref}, #{f.file_ref.hierarchy_path}...................\n"
next
end file_ref = proj.new(Xcodeproj::Project::Object::PBXFileReference)
if f.settings
puts ">>file.settings: #{idx} > file: " + f.file_ref.to_s + " settings: " + f.settings.to_s
end idx = idx+1
if f.file_ref
if f.file_ref.name
puts ">> file_ref name: #{f.file_ref.name} path: #{f.file_ref.path} source_tree: #{f.file_ref.source_tree}"
end
# puts ">> file path: #{f.file_ref.hierarchy_path}-- #{f.file_ref.real_path}" file_ref.name = f.file_ref.name
file_ref.path = f.file_ref.path
file_ref.source_tree = f.file_ref.source_tree
file_ref.last_known_file_type = f.file_ref.last_known_file_type
# file_ref.fileEncoding = f.file_ref.fileEncoding
begin
file_ref.move(f.file_ref.parent)
rescue
end end build_file = proj.new(Xcodeproj::Project::Object::PBXBuildFile)
build_file.file_ref = f.file_ref
# 文件属性配置,如no-arc
if f.settings
build_file.settings = f.settings
end
dst.files << build_file
end
end #设置目标target文件组
projTargetGroup = proj.main_group.groups.find { |x| x.path == 'TNTarget' }
targetGroup = projTargetGroup.new_group(name, name)
# resource
resourceGroup = targetGroup.new_group("Resources", "./Resources")
supportingGroup=resourceGroup.new_group("Supporting Files") # 添加资源文件引用,注意和代码文件引用方式不同
target.add_resources(
[
resourceGroup.new_reference("areaCode.plist"),
resourceGroup.new_reference("login_toon_bg@2x.png"),
resourceGroup.new_reference("login_toon_bg@3x.png"),
resourceGroup.new_reference("tab_item_home_highlight@2x.png"),
resourceGroup.new_reference("tab_item_home_highlight@3x.png"),
resourceGroup.new_reference("tab_item_home_normal@2x.png"),
resourceGroup.new_reference("tab_item_home_normal@3x.png"),
resourceGroup.new_reference("Toon_logo@2x.png"),
resourceGroup.new_reference("Toon_logo@3x.png"),
resourceGroup.new_reference("toon_serviceProtocol.html"),
resourceGroup.new_reference("user_protocol.html"),
resourceGroup.new_reference("NewFunction.html"), supportingGroup.new_reference("Supporting Files/configuration.plist"),
supportingGroup.new_reference("Supporting Files/Info.plist"),
supportingGroup.new_reference("Supporting Files/Images.xcassets"),
supportingGroup.new_reference("Supporting Files/InfoPlist.strings"),
supportingGroup.new_reference("Supporting Files/Localizable.strings")
]) if hasAllListFiles
# 添加代码文件组
code1 = targetGroup.new_group("NetWork", "./NetWork")
code2 = targetGroup.new_group("TabbarSetDataSource", "./TabbarSetDataSource")
code3 = targetGroup.new_group("TNHQHome", "./TNHQHome") # 添加代码文件引用
target.add_file_references(
[
code1.new_reference("NetworkRequestURL.h"),
code1.new_reference("NetworkRequestURL.m"), code2.new_reference("TNTabSettingDataSource.h"),
code2.new_reference("TNTabSettingDataSource.m"), code3.new_reference("TNHomeViewController.m")
])
end # 修改文件通用内容
infoplistfile = "#{targetdir}/Resources/Supporting Files/Info.plist"
files = [
"#{targetdir}/Resources/areaCode.plist",
"#{targetdir}/Resources/toon_serviceProtocol.html",
"#{targetdir}/Resources/user_protocol.html",
"#{targetdir}/Resources/NewFunction.html",
infoplistfile,
"#{targetdir}/Resources/Supporting Files/InfoPlist.strings",
"#{targetdir}/Resources/Supporting Files/Localizable.strings" ]
if hasAllListFiles
files << "#{targetdir}/TabbarSetDataSource/TNTabSettingDataSource.m"
end
files.each do |f1|
File.open(f1) do |fr|
buffer = fr.read.gsub(srcdisplayname, displayname)
buffer= buffer.gsub("项目名", displayname)
buffer= buffer.gsub("大同", displayname)
File.open(f1, "w") { |fw| fw.write(buffer) }
end
end # 修改info.plist
File.open(infoplistfile) do |fr|
if hasAllListFiles
puts "*************************** 1"
buffer = fr.read.gsub("<string>10024</string>", "<string>#{target_pushType}</string>")
buffer= buffer.gsub("<integer>124</integer>", "<integer>#{target_toonType}</integer>")
buffer= buffer.gsub("<string>1241002</string>", "<string>#{target_channel}</string>")
buffer= buffer.gsub("<string>8058bda8c0ad5a7cfb8742cfbac4ecb8</string>", "<string>#{target_mapKey}</string>")
buffer= buffer.gsub("<string>toon124</string>", "<string>#{target_schemeType}</string>")
else
puts "*************************** 2"
buffer = fr.read.gsub("<string>10016</string>", "<string>#{target_pushType}</string>")
buffer= buffer.gsub("<integer>116</integer>", "<integer>#{target_toonType}</integer>")
buffer= buffer.gsub("<string>10035</string>", "<string>#{target_channel}</string>")
buffer= buffer.gsub("<string>e851d7df83d59f143bff1ad5a3a8e554</string>", "<string>#{target_mapKey}</string>")
buffer= buffer.gsub("<string>toon116</string>", "<string>#{target_schemeType}</string>")
end
puts "*************************** updating InfoPlist" File.open(infoplistfile, "w") { |fw| fw.write(buffer) } end
proj.save # 修改Podfile
puts ">> prepare loading pods ..."
podTarget = "target '#{name}' do shared_pods end"
File.open("Podfile") do |file|
if file.read().index(podTarget) ==nil
File.open(infoplistfile, "w") { |fw| fw.puts podTarget }
puts ">> add pod item"
else
puts ">> pod has been added"
end end # file.close # 更新pod依赖
exec 'pod install'
iOS 使用脚本自动化复制target的更多相关文章
- shell脚本自动化部署服务
shell脚本自动化部署 !/bin/bash #export PATH=$PATH:/export/maven/bin run_flag_dir="/data0/shell/deploy_ ...
- Cocos2d-x3.0 iOS 一键编译多个target并打包ipa。
1.编写app打包为ipa的 shell脚本.将以下代码保存为app2ipa.sh. #!/bin/sh m_appPath="" m_ipaPath="" m ...
- fdisk分区硬盘并shell脚本自动化
最近工作需要用到对硬盘进行shell脚本自动化分区和mount的操作,google了一些资料,下面做个总结. 如果硬盘没有进行分区(逻辑分区或者扩展分区,关于两者概念,自行google),我们将无法将 ...
- Modelsim调用用do脚本自动化仿真
前言 EDA发展的趋势是自动化,使用脚本自动化仿真可以减少不必要的时间浪费. 流程 在windows下新建批处理脚本bat文件(linux下可用shell脚本或者其他,注意给脚本运行权限即可:chmo ...
- MySQL数据库主从切换脚本自动化
MySQL数据库主从切换脚本自动化 本文转载自:https://blog.csdn.net/weixin_36135773/article/details/79514507 在一些实际环境中,如何实现 ...
- iOS开发系列-自动化分发测试打包
概述 项目在测试阶段需要频繁打包给测试人员,对于这些固定化的操作我们可以使用自动化的手段去解决,将时间放在有意义的事情上. xcodebuild 是苹果发布自动构建的工具. Shell脚本打包 xcr ...
- iOS使用fastlane自动化打包到fir(最全最详细流程)
# iOS使用fastlane自动化打包到fir(最全最详细流程)1. **首先确认是否安装了ruby,终端查看下ruby版本**> ruby -v终端输出:ruby 2.4.1p111 (20 ...
- shell脚本自动化部署
由于公司技术部团队较小,没有专门的运维团队,所以运维工作技术部承包了. 一.纯人工部署是这样的: 1. 本地打包:一般 maven clean package 2. 借助xftp上传到服务器对应目录 ...
- appium ios真机自动化环境搭建&运行(送源码)
appium ios真机自动化环境搭建&运行(送源码) 原创: f i n 测试开发社区 6天前 Appium测试环境的搭建相对比较烦琐,不少初学者在此走过不少弯路 首先是熟悉Mac的使用 ...
随机推荐
- oc1
// zs.h #ifndef __day11__zs__ #define __day11__zs__ #include <stdio.h> int sum(int v1, int v2) ...
- Android开机动画、logo、字样的定制过程【转】
本文转载自:http://blog.csdn.net/yinhaide/article/details/43668401 Android开机画面总共有三屏 一.第一屏:开机logo 1.选张png格式 ...
- navicat数据库同步
10.168.95.246
- 抽象类(abrstract class)与接口(interface)有何异同
抽象类:如果一个类中包含抽象方法(用abstract修饰的方法),那么这个类就是抽象类 接口:是指一个方法的集合,接口中的所有方法都没有方法体 相同点: 1)都不能被实例化 2)接口的实现类或抽象类的 ...
- win10 + vs2017 + vcpkg —— VC++ 打包工具
vcpkg 是微软 C++ 团队开发的在 Windows 上运行的 C/C++ 项目包管理工具,可以帮助您在 Windows 平台上获取 C 和 C++ 库. vcpkg 自身也是使用 C++ 开发的 ...
- os.clock()导致的bug
os.clock () 功能:返回一个程序使用CPU时间的一个近似值 最近做了一个功能,这个功能需要统计时间间隔,例如每隔0.5秒做一次调用. 我用了os.clock()去统计时间,结果在pc机上都没 ...
- BZOJ3573: [Hnoi2014]米特运输(树上乱搞)
Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 1669 Solved: 1031[Submit][Status][Discuss] Descript ...
- js中获取浏览器和屏幕高度
Javascript: IE中: document.body.clientWidth ==> BODY对象宽度 document.body.clientHeight ==> BODY对象高 ...
- JS排序之冒泡排序
冒泡排序的两种策略: <script>// 第一种思路:// 一个数组中的数据,拿第一个和剩下的依次进行对比,数值小的赋值给第一个,一轮比较过后,则数值小的放在最前边.// 第二轮比较,则 ...
- 「Redis 笔记」数据类型
REmote DIctionary Server(Redis),一个 key-value 存储系统. 数据类型 Redis 支持五种数据类型:string(字符串),hash(哈希),list(列表) ...