iOS 使用腳本自動化複製target

来源:http://www.cnblogs.com/v-jing/archive/2017/12/08/8004695.html
-Advertisement-
Play Games

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


有些項目成熟以後,就會有需求自動化配置生成一個全新的項目,不需要再讓開發人員手動修改工程文件,將配置化工作直接移交給運維或者配置團隊去做

其實按照普通的做法,無非就是在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("[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'

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Android如何使用Https,這一篇文章是NoHttp系列中比較重要的,為大家介紹一下內容。 什麼是Https? HTTPS(全稱:Hyper Text Transfer Protocol over Secure Socket Layer),是以安全為目標的HTTP通道,簡單講是HTTP的安全版 ...
  • 有沒有覺得Android的findViewById挺煩人的。使用Kotlin可以讓你徹底拋棄這個煩惱 步驟1、在build.gradle(Module:app)中添加如下一句話 這個在老一點版本的Android Studio中需要手動添加,我的是Android Studio3.0的,這句話是預設加上 ...
  • Android系統中,目前Dangerous級別的許可權都需要動態申請。步驟如下; 1、AndroidManfiest.xml中申明需要的動態許可權 2、代碼中檢查許可權、申請許可權 如下方法執行之後,會彈出提示框,提示要申請該許可權 3、獲取結果 轉載請註明鏈接:http://www.cnblogs.com ...
  • MvvmCross從4.0之後plugin的註冊介面做了重構,網上例子不多,這裡給個參考。本例子使用MvvmCross.Plugins.DownloadCache和MvvmCross.Plugins.File.PluginLoader來顯示網上的一個圖片。 1,View里先給個UIKit.UIIma ...
  • 在iOS開發中、經常用到圖片的本地化。 iOS 圖片本地存儲、本地獲取、本地刪除,可以通過以下類方法實現。 //將圖片保存到本地 + (void)SaveImageToLocal:(UIImage*)image Keys:(NSString*)key { //首先,需要獲取沙盒路徑 NSString ...
  • 內容摘要:Android Handler消息傳遞機制的學習總結、問題記錄 Handler消息傳遞機制的目的: 1.實現線程間通信(如:Android平臺只允許主線程(UI線程)修改Activity里的UI組件,而實際開發時會遇到新開的線程要改變界面組件屬性的情況,這時就要有一種辦法通知主線程更新UI ...
  • 本次分析針對當下流行的中國地圖圖片處理,1億像素,就是下麵這張: 原圖尺寸:11935x8554 文件大小:22.1MB 原始載入方式 首先,我們嘗試一下直接載入的方式,看看效果會有多恐怖 首先,我們嘗試一下直接載入的方式,看看效果會有多恐怖 效果請看下麵的Gif動畫展示: 直接載入原圖記憶體占用 可 ...
  • 一、新浪微博分享規則 新浪微博支持分享類型: 應用內分享也就是網頁分享支持: 文字,文字+圖片,要分享鏈接需要鏈接添加在text里分享 客戶端分享支持:文字,圖片,文字+圖片,圖片+文字+鏈接 參數說明:text:不能超過140個漢字image:圖片最大不超過5M,僅支持JPEG、GIF、PNG格式 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...