有些項目成熟以後,就會有需求自動化配置生成一個全新的項目,不需要再讓開發人員手動修改工程文件,將配置化工作直接移交給運維或者配置團隊去做 其實按照普通的做法,無非就是在xcode里將目標target duplicate一下,然後修改相關的項目名稱、target名稱、bundleid等等,這些內容其實 ...
有些項目成熟以後,就會有需求自動化配置生成一個全新的項目,不需要再讓開發人員手動修改工程文件,將配置化工作直接移交給運維或者配置團隊去做
其實按照普通的做法,無非就是在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("[email protected]"), resourceGroup.new_reference("[email protected]"), resourceGroup.new_reference("[email protected]"), resourceGroup.new_reference("[email protected]"), resourceGroup.new_reference("[email protected]"), resourceGroup.new_reference("[email protected]"), resourceGroup.new_reference("[email protected]"), resourceGroup.new_reference("[email protected]"), 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'