有些项目成熟以后,就会有需求自动化配置生成一个全新的项目,不需要再让开发人员手动修改工程文件,将配置化工作直接移交给运维或者配置团队去做

其实按照普通的做法,无非就是在xcode里将目标target duplicate一下,然后修改相关的项目名称、target名称、bundleid等等,这些内容其实在xcodeproj文件中都有对应的配置信息,所以我们可以通过直接修改Xcodeproj直接文件的方式实现

首先感谢虾神 提供了详细的原理解说和工具介绍,在实现时少走了很多弯路,有兴趣的同学可以前往 虾神 的文章学习,这里我把最终实现贴出来供大家参考~~

希望进一步学习ios工程配置和脚本相关的同学建议去这里:

ruby doc

xcodeproj相关API文档

github示例

巧用脚本解决 Target 管理问题

#!/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的更多相关文章

  1. shell脚本自动化部署服务

    shell脚本自动化部署 !/bin/bash #export PATH=$PATH:/export/maven/bin run_flag_dir="/data0/shell/deploy_ ...

  2. Cocos2d-x3.0 iOS 一键编译多个target并打包ipa。

    1.编写app打包为ipa的 shell脚本.将以下代码保存为app2ipa.sh. #!/bin/sh m_appPath="" m_ipaPath="" m ...

  3. fdisk分区硬盘并shell脚本自动化

    最近工作需要用到对硬盘进行shell脚本自动化分区和mount的操作,google了一些资料,下面做个总结. 如果硬盘没有进行分区(逻辑分区或者扩展分区,关于两者概念,自行google),我们将无法将 ...

  4. Modelsim调用用do脚本自动化仿真

    前言 EDA发展的趋势是自动化,使用脚本自动化仿真可以减少不必要的时间浪费. 流程 在windows下新建批处理脚本bat文件(linux下可用shell脚本或者其他,注意给脚本运行权限即可:chmo ...

  5. MySQL数据库主从切换脚本自动化

    MySQL数据库主从切换脚本自动化 本文转载自:https://blog.csdn.net/weixin_36135773/article/details/79514507 在一些实际环境中,如何实现 ...

  6. iOS开发系列-自动化分发测试打包

    概述 项目在测试阶段需要频繁打包给测试人员,对于这些固定化的操作我们可以使用自动化的手段去解决,将时间放在有意义的事情上. xcodebuild 是苹果发布自动构建的工具. Shell脚本打包 xcr ...

  7. iOS使用fastlane自动化打包到fir(最全最详细流程)

    # iOS使用fastlane自动化打包到fir(最全最详细流程)1. **首先确认是否安装了ruby,终端查看下ruby版本**> ruby -v终端输出:ruby 2.4.1p111 (20 ...

  8. shell脚本自动化部署

    由于公司技术部团队较小,没有专门的运维团队,所以运维工作技术部承包了. 一.纯人工部署是这样的: 1. 本地打包:一般 maven clean package 2. 借助xftp上传到服务器对应目录 ...

  9. appium ios真机自动化环境搭建&运行(送源码)

    appium ios真机自动化环境搭建&运行(送源码) 原创: f i n  测试开发社区  6天前 Appium测试环境的搭建相对比较烦琐,不少初学者在此走过不少弯路 首先是熟悉Mac的使用 ...

随机推荐

  1. adb命令--之查看进程及Kill进程

    adb shell kill [PID]       //杀死进程 adb 命令查看程序进程方便简洁高效 adb shell ps       //查看所有进程列表,Process Status ad ...

  2. P1025小飞侠的游园方案

    描述 经过抽签选择,小智将军第一个进入考场. 菜虫:(身上散射出华贵(?)的光芒)欢迎你,第一位挑战者!! 小智:……(走到菜虫身后,关灯)女王陛下,虽然我们国家现在很富裕,但也请您不要浪费电来用这么 ...

  3. Redis位图法记录在线用户的状态

    Redis位图法记录在线用户的状态 位图 Redis官方文档对于位图的介绍如下: 位图不是一个真实的数据类型,而是定义在字符串类型上的面向位的操作的集合.由于字符串类型是二进制安全的二进制大对象,并且 ...

  4. 修改python的pip下载源

    推荐两个源: 豆瓣:http://pypi.douban.com/simple/ 清华:https://pypi.tuna.tsinghua.edu.cn/simple 使用方法有两种,一种为临时使用 ...

  5. 什么是 HTML5?

    HTML5 是下一代的 HTML. 什么是 HTML5? HTML5 将成为 HTML.XHTML 以及 HTML DOM 的新标准. HTML 的上一个版本诞生于 1999 年.自从那以后,Web ...

  6. (链接)IDEA 2018 激活 IDEA 2018.3激活教程 最新的(三种)—2018.11.26亲测

    破解不成功的请注意时效性,写于2019/2/8,以下第一种激活方法亲测可用, 不过有时候破解成功了可能过几天突然就打不开了,双击无反应的说,这时候再按顺序 操作一遍就是了: 1)把idea64.exe ...

  7. Android 通用Dialog中设置RecyclerView

    通用Dialog public class IOSRecyclerViewDialog{ private Context context; private Dialog dialog; private ...

  8. (转载)android开发常见编程错误总结

    首页 › 安卓开发 › android开发 android开发常见编程错误总结 泡在网上的日子 / 文 发表于2013-09-07 13:07  第771次阅读 android,异常 0 编辑推荐:稀 ...

  9. 【华科考研机试题】最长&最短文本

    题目 输入多行字符串,请按照原文本中的顺序输出其中最短和最长的字符串,如果最短和最长的字符串不止一个,请全部输出. 解题思路 1.输入所有字符串(有空格不另算字符串). 2.将char*字符串转换成s ...

  10. size_t与size_type的使用

    size_t 是为了方便系统之间的移植而定义的 在32位系统上 定义为 unsigned int在64位系统上 定义为 unsigned long 更准确地说法是 在 32位系统上是32位无符号整形在 ...