podhelper.rb源碼解析

前言:iOS工程中引入flutter降允。我們第一步要做的事是,在podfile里面寫入類似下面代碼萍程。

flutter_app_path = '../my_flutter' #flutter工程目錄
load File.join(flutter_app_path, '.ios', 'Flutter', 'podhelper.rb') #導(dǎo)入podhelper.rb文件
install_all_flutter_pods(flutter_app_path) #調(diào)用podhelper.rb文件的install_all_flutter_pods方法

下面分析podhelper.rb都干了啥幢妄。
總體結(jié)構(gòu):1個(gè)require、6個(gè)方法


image.png

Part1茫负、require語(yǔ)法

require 'json'

require蕉鸳。類似于include和import,引入一個(gè)模塊忍法。模塊(Module)是一種把方法潮尝、類和常量組合在一起的方式。提供了命名空間饿序、實(shí)現(xiàn)了混入(mixin)
Load和require都能加載衍锚。模塊的狀態(tài)會(huì)頻繁地變化, 我們使用 load 進(jìn)行加載,獲取最新的狀態(tài)嗤堰。不變化的我們用require戴质。

Part2、6個(gè)方法

1踢匣、install_all_flutter_pods

def install_all_flutter_pods(flutter_application_path = nil)
  flutter_application_path ||= File.join('..', '..')
  install_flutter_engine_pod
  install_flutter_plugin_pods(flutter_application_path)
  install_flutter_application_pod(flutter_application_path)
end

1告匠、def:定義一個(gè)方法
2、||=:或等离唬,為空則賦值后专,不為空自身
3、File.join('..', '..'):用分割符(默認(rèn)是/)把'..'和'..'組合起來(lái)输莺,返回一個(gè)str戚哎。該例子返回一個(gè)'../..'

2、install_flutter_engine_pod

def install_flutter_engine_pod
  current_directory = File.expand_path('..', __FILE__)
  engine_dir = File.expand_path('engine', current_directory)
  if !File.exist?(engine_dir)
    # Copy the debug engine to have something to link against if the xcode backend script has not run yet.
    # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist.
    debug_framework_dir = File.join(flutter_root, 'bin', 'cache', 'artifacts', 'engine', 'ios')
    FileUtils.mkdir_p(engine_dir)
    FileUtils.cp_r(File.join(debug_framework_dir, 'Flutter.framework'), engine_dir)
    FileUtils.cp(File.join(debug_framework_dir, 'Flutter.podspec'), engine_dir)
  end

  # Keep pod path relative so it can be checked into Podfile.lock.
  # Process will be run from project directory.
  engine_pathname = Pathname.new engine_dir
  # defined_in_file is set by CocoaPods and is a Pathname to the Podfile.
  project_directory_pathname = defined_in_file.dirname
  relative = engine_pathname.relative_path_from project_directory_pathname

  pod 'Flutter', :path => relative.to_s, :inhibit_warnings => true
end

1嫂用、__FILE__型凳。這個(gè)變量代表文件自己的文件名,在podhelper.rb中是podhelper.rb嘱函。
2甘畅、File.expand_path('..', 'b')。在b路徑下追加路徑'..',此例是b/..疏唾,也就是返回b的上級(jí)目錄蓄氧。
3、flutter_root槐脏。方法
4喉童、FileUtils.mkdir_p。生成目錄及其所有上級(jí)目錄顿天。例如泄朴,F(xiàn)ileUtils.mkdir_p '/usr/local/lib/ruby'將生成下列所有目錄(若沒(méi)有的話)。 * /usr * /usr/local * /usr/local/bin * /usr/local/bin/ruby
5露氮、FileUtils.cp_r(a,b)祖灰。把文件a拷貝到b,如果目錄b存在畔规,就拷貝到b里面局扶,如果b不存在,就把a(bǔ)拷貝到b叁扫,此時(shí)b是一個(gè)沒(méi)有后綴名的文件
6三妈、Pathname.new engine_dir。等同Pathname.new(engine_dir)莫绣,或Pathname(engine_dir)畴蒲。通過(guò)str生成一個(gè)路徑類Pathname的對(duì)象
7、a.relative_path_from b对室。a相對(duì)于b的相對(duì)路徑模燥,也叫b->a的相對(duì)路徑。Pathname('/Users/bigfly/Desktop').relative_path_from Pathname('/Users/bigfly/Desktop/my_flutter')是'..'
8掩宜、.to_s蔫骂。用來(lái)將對(duì)象以字符串的格式去描述、去輸出牺汤。也就是說(shuō)辽旋,所有對(duì)象都能使用字符串的描述格式。
只要像鴨子檐迟,就能當(dāng)成鴨子补胚,這就是to_x。只有它真的是鴨子追迟,才能當(dāng)成鴨子溶其,這就是to_xxx。例如調(diào)用obj的string方法時(shí)需要先強(qiáng)制轉(zhuǎn)換為string類怔匣,用to_str握联。類似的有to_i和to_int、to_a和to_ary每瞒、to_h和to_hash

總結(jié):該方法是把flutter_root目錄/Users/xxx/flutter/bin/cache/artifacts/engine/ios 下面的Flutter.framework和Flutter.podspec拷貝到flutter模塊工程的engine目錄/Users/bigfly/Desktop/my_flutter/.ios/Flutter/engine下面金闽。然后把Flutter pod進(jìn)來(lái)。如果engine存在就直接pod剿骨,如果不存在代芜,就去flutter_root拷貝一份過(guò)來(lái)再pod。

3浓利、install_flutter_plugin_pods

def install_flutter_plugin_pods(flutter_application_path)
  flutter_application_path ||= File.join('..', '..')

  # Keep pod path relative so it can be checked into Podfile.lock.
  # Process will be run from project directory.
  current_directory_pathname = Pathname.new File.expand_path('..', __FILE__)
  # defined_in_file is set by CocoaPods and is a Pathname to the Podfile.
  project_directory_pathname = defined_in_file.dirname
  relative = current_directory_pathname.relative_path_from project_directory_pathname
  pod 'FlutterPluginRegistrant', :path => File.join(relative, 'FlutterPluginRegistrant'), :inhibit_warnings => true

  symlinks_dir = File.join(relative, '.symlinks')
  FileUtils.mkdir_p(symlinks_dir)

  plugins_file = File.expand_path('.flutter-plugins-dependencies', flutter_application_path)
  plugin_pods = flutter_parse_dependencies_file_for_ios_plugin(plugins_file)
  plugin_pods.each do |plugin_hash|
    plugin_name = plugin_hash['name']
    plugin_path = plugin_hash['path']
    if (plugin_name && plugin_path)
      symlink = File.join(symlinks_dir, plugin_name)
      FileUtils.rm_f(symlink)
      File.symlink(plugin_path, symlink)
      pod plugin_name, :path => File.join(symlink, 'ios'), :inhibit_warnings => true
    end
  end
end

1挤庇、flutter_application_path:/Users/xxx/Desktop/my_flutter
2、current_directory_pathname:/Users/xxx/Desktop/my_flutter/.ios/Flutter
3贷掖、project_directory_pathname = /Users/xxx/Desktop/my_flutter/.ios
4嫡秕、symlinks_dir = /Users/xxx/Desktop/my_flutter/.ios/Flutter/.symlinks
5、plugins_file = /Users/xxx/Desktop/my_flutter/.flutter-plugins-dependencies
6苹威、flutter_parse_dependencies_file_for_ios_plugin昆咽。方法。解析.flutter-plugins-dependencies這個(gè)json文件牙甫,返回一個(gè)key鏈plugins/ios的值
7掷酗、FileUtils.rm_f(symlink):刪除/Users/xxx/Desktop/my_flutter/.ios/Flutter/.symlinks/shared_preferences
File.symlink(plugin_path, symlink):在symlink位置創(chuàng)建一個(gè)符號(hào)鏈接,指向plugin_path窟哺,此處plugin_path的值是"/Users/xxx/flutter/.pub-cache/hosted/pub.flutter-io.cn/shared_preferences-0.5.12/"

總結(jié):該方法是泻轰,先pod進(jìn)來(lái)/Users/xxx/Desktop/my_flutter/.ios/Flutter/FlutterPluginRegistrant。然后解析.flutter-plugins-dependencies文件且轨,得到key鏈plugins/ios的值浮声,是個(gè)依賴庫(kù)數(shù)組,遍歷數(shù)組旋奢,對(duì)每個(gè)依賴庫(kù)進(jìn)行刪除實(shí)體文件阿蝶、創(chuàng)建符號(hào)鏈接、pod進(jìn)來(lái)操作黄绩。

4羡洁、install_flutter_application_pod

def install_flutter_application_pod(flutter_application_path)
  current_directory_pathname = Pathname.new File.expand_path('..', __FILE__)
  app_framework_dir = File.expand_path('App.framework', current_directory_pathname.to_path)
  app_framework_dylib = File.join(app_framework_dir, 'App')
  if !File.exist?(app_framework_dylib)
    # Fake an App.framework to have something to link against if the xcode backend script has not run yet.
    # CocoaPods will not embed the framework on pod install (before any build phases can run) if the dylib does not exist.
    # Create a dummy dylib.
    FileUtils.mkdir_p(app_framework_dir)
    `echo "static const int Moo = 88;" | xcrun clang -x c -dynamiclib -o "#{app_framework_dylib}" -`
  end

  # Keep pod and script phase paths relative so they can be checked into source control.
  # Process will be run from project directory.

  # defined_in_file is set by CocoaPods and is a Pathname to the Podfile.
  project_directory_pathname = defined_in_file.dirname
  relative = current_directory_pathname.relative_path_from project_directory_pathname
  pod 'my_flutter', :path => relative.to_s, :inhibit_warnings => true

  flutter_export_environment_path = File.join('${SRCROOT}', relative, 'flutter_export_environment.sh');
  script_phase :name => 'Run Flutter Build my_flutter Script',
    :script => "set -e\nset -u\nsource \"#{flutter_export_environment_path}\"\n\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/xcode_backend.sh build",
    :input_files => [
      File.join('${SRCROOT}', flutter_application_path, '.metadata'),
      File.join('${SRCROOT}', relative, 'App.framework', 'App'),
      File.join('${SRCROOT}', relative, 'engine', 'Flutter.framework', 'Flutter'),
      flutter_export_environment_path
    ],
    :execution_position => :before_compile
end

1、current_directory_pathname = /Users/xxx/Desktop/my_flutter/.ios/Flutter
2爽丹、app_framework_dir = /Users/xxx/Desktop/my_flutter/.ios/Flutter/App.framework
3筑煮、app_framework_dylib = /Users/xxx/Desktop/my_flutter/.ios/Flutter/App.framework/App
4、echo "static const int Moo = 88;" | xcrun clang -x c -dynamiclib -o "#{app_framework_dylib}" -粤蝎。shell命令
5真仲、${SRCROOT},ruby中全局變量用${}引用
6初澎、project_directory_pathname = /Users/xxx/Desktop/my_flutter/.ios
7秸应、flutter_export_environment_path =/Users/xxx/Desktop/my_flutter/.ios/Flutter/flutter_export_environment.sh
8虑凛、script_phase 。給target添加編譯前的shell腳本软啼,腳本內(nèi)容是

set -e #之后的代碼桑谍,一旦出錯(cuò),停止執(zhí)行并退出祸挪,避免后續(xù)一些腳本的危險(xiǎn)操作
set -u #遇到不存在的變量就會(huì)報(bào)錯(cuò)锣披,并停止執(zhí)行
source "#{flutter_export_environment_path}"
"$FLUTTER_ROOT"/packages/flutter_tools/bin/xcode_backend.sh build

總結(jié):該方法是,生成App.framework/app文件如果不存在的話贿条,執(zhí)行了一條shell命令(調(diào)用xcrun clang -o命令生成目標(biāo)文件)雹仿。pod進(jìn)來(lái)my_flutter。給target添加編譯前的shell腳本整以,腳本主要執(zhí)行了xcode_backend.sh build命令胧辽。

5、flutter_parse_dependencies_file_for_ios_plugin

def flutter_parse_dependencies_file_for_ios_plugin(file)
  file_path = File.expand_path(file)
  return [] unless File.exists? file_path

  dependencies_file = File.read(file)
  dependencies_hash = JSON.parse(dependencies_file)

  # dependencies_hash.dig('plugins', 'ios') not available until Ruby 2.3
  return [] unless dependencies_hash.has_key?('plugins')
  return [] unless dependencies_hash['plugins'].has_key?('ios')
  dependencies_hash['plugins']['ios'] || []
end

解析.flutter-plugins-dependencies文件公黑,并返回key鏈plugins/ios的值票顾。

6、flutter_root

def flutter_root
  generated_xcode_build_settings_path = File.expand_path(File.join('..', '..', 'Flutter', 'Generated.xcconfig'), __FILE__)
  unless File.exist?(generated_xcode_build_settings_path)
    raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
  end

  File.foreach(generated_xcode_build_settings_path) do |line|
    matches = line.match(/FLUTTER_ROOT\=(.*)/)
    return matches[1].strip if matches
  end
  # This should never happen...
  raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end

1帆调、File.foreach讀取文件的每一行并執(zhí)行一個(gè)block奠骄。File.readlines讀取文件的每一行到一個(gè)數(shù)組里面。
2番刊、line.match匹配結(jié)果是個(gè)數(shù)組含鳞,包括兩部分:0完整匹配部分、1匹配到的內(nèi)容

總結(jié):該方法芹务,解析Flutter/Generated.xcconfig文件蝉绷,遍歷每一行,找到FLUTTER_ROOT的值并返回枣抱。

總結(jié):該文件主要有三個(gè)方法:install_flutter_engine_pod熔吗、install_flutter_plugin_pods、install_flutter_application_pod佳晶。分別作用是:不存在就新生成并pod進(jìn)來(lái)engine/Flutter.framework/flutter桅狠、pod進(jìn)來(lái)FlutterPluginRegistrant+plugins、不存在就新生成app.framework/app并pod進(jìn)來(lái)my_flutter轿秧。

思考:

1中跌、更改flutter工程的dart代碼,重新運(yùn)行iOS工程(不是Runner),為什么代碼能生效菇篡?
pod install的時(shí)候漩符,執(zhí)行了podhelper.rb,執(zhí)行了install_flutter_application_pod方法驱还,給target添加了編譯前shell腳本嗜暴。所以pod install 過(guò)后凸克,每次運(yùn)行iOS工程都會(huì)執(zhí)行xcode_backend.sh build命令。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末闷沥,一起剝皮案震驚了整個(gè)濱河市萎战,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌狐赡,老刑警劉巖撞鹉,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疟丙,死亡現(xiàn)場(chǎng)離奇詭異颖侄,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)享郊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門览祖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人炊琉,你說(shuō)我怎么就攤上這事展蒂。” “怎么了苔咪?”我有些...
    開封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵锰悼,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我团赏,道長(zhǎng)箕般,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任舔清,我火速辦了婚禮丝里,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘体谒。我一直安慰自己杯聚,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開白布抒痒。 她就那樣靜靜地躺著幌绍,像睡著了一般。 火紅的嫁衣襯著肌膚如雪故响。 梳的紋絲不亂的頭發(fā)上纷捞,一...
    開封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音被去,去河邊找鬼主儡。 笑死,一個(gè)胖子當(dāng)著我的面吹牛惨缆,可吹牛的內(nèi)容都是我干的糜值。 我是一名探鬼主播丰捷,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼寂汇!你這毒婦竟也來(lái)了病往?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤骄瓣,失蹤者是張志新(化名)和其女友劉穎停巷,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體榕栏,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡畔勤,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了扒磁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片庆揪。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖妨托,靈堂內(nèi)的尸體忽然破棺而出缸榛,到底是詐尸還是另有隱情,我是刑警寧澤兰伤,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布内颗,位于F島的核電站,受9級(jí)特大地震影響敦腔,放射性物質(zhì)發(fā)生泄漏均澳。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一会烙、第九天 我趴在偏房一處隱蔽的房頂上張望负懦。 院中可真熱鬧,春花似錦柏腻、人聲如沸纸厉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)颗品。三九已至,卻和暖如春沃缘,著一層夾襖步出監(jiān)牢的瞬間躯枢,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工槐臀, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留锄蹂,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓水慨,卻偏偏與公主長(zhǎng)得像得糜,于是被迫代替她去往敵國(guó)和親敬扛。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348