From a40c1d0ca3ece1e383e6d208d7f4ef1c690cb4bf Mon Sep 17 00:00:00 2001 From: chenxGen Date: Thu, 9 Sep 2021 22:36:43 +0800 Subject: [PATCH 1/3] fix: target defined module or in black list should not be removed from header search path. --- lib/cocoapods-project-hmap/hmap_generator.rb | 3 ++ lib/cocoapods-project-hmap/pod_target.rb | 45 +++++++++++++++++-- lib/cocoapods-project-hmap/xcconfig.rb | 16 +++++-- lib/cocoapods_plugin.rb | 46 +++----------------- 4 files changed, 62 insertions(+), 48 deletions(-) diff --git a/lib/cocoapods-project-hmap/hmap_generator.rb b/lib/cocoapods-project-hmap/hmap_generator.rb index 8aef540..cb4bf89 100644 --- a/lib/cocoapods-project-hmap/hmap_generator.rb +++ b/lib/cocoapods-project-hmap/hmap_generator.rb @@ -52,5 +52,8 @@ def save_to(path) false end end + def empty? + @hmap.empty? + end end end diff --git a/lib/cocoapods-project-hmap/pod_target.rb b/lib/cocoapods-project-hmap/pod_target.rb index 8a069a9..bf5568f 100644 --- a/lib/cocoapods-project-hmap/pod_target.rb +++ b/lib/cocoapods-project-hmap/pod_target.rb @@ -1,14 +1,35 @@ # !/usr/bin/env ruby require 'cocoapods-project-hmap/xcconfig' require 'cocoapods-project-hmap/hmap_generator' +require 'cocoapods-project-hmap/podfile_dsl' + +SAVED_HMAP_DIR='prebuilt-hmaps' module Pod + class Target + def save_hmap(hmap) + if hmap.empty? == false + target_hmap_name="#{name}.hmap" + relative_hmap_path = "#{SAVED_HMAP_DIR}/#{target_hmap_name}" + target_hmap_path = sandbox.root.to_s + "/#{relative_hmap_path}" + hmaps_dir = sandbox.root.to_s + "/#{SAVED_HMAP_DIR}" + unless File.exist?(hmaps_dir) + Dir.mkdir(hmaps_dir) + end + if hmap.save_to(target_hmap_path) + reset_header_search_with_relative_hmap_path(relative_hmap_path) + end + end + end + end + class PodTarget + attr_accessor :unused_targets def reset_header_search_with_relative_hmap_path(hmap_path) if build_settings.instance_of?(Hash) build_settings.each do |config_name, setting| config_file = setting.xcconfig - config_file.reset_header_search_with_relative_hmap_path(hmap_path) + config_file.reset_header_search_with_relative_hmap_path(hmap_path, @unused_targets.uniq) # https://github.com/CocoaPods/CocoaPods/issues/1216 # just turn off private xcconfig's USE_HEADERMAP flag config_file.set_use_hmap(false) @@ -17,7 +38,7 @@ def reset_header_search_with_relative_hmap_path(hmap_path) end elsif build_settings.instance_of?(BuildSettings::PodTargetSettings) config_file = build_settings.xcconfig - config_file.reset_header_search_with_relative_hmap_path(hmap_path) + config_file.reset_header_search_with_relative_hmap_path(hmap_path, @unused_targets.uniq) # https://github.com/CocoaPods/CocoaPods/issues/1216 # just turn off private xcconfig's USE_HEADERMAP flag config_file.set_use_hmap(false) @@ -30,8 +51,24 @@ def reset_header_search_with_relative_hmap_path(hmap_path) def recursively_add_dependent_headers_to_hmap(hmap, generate_type) dependent_targets.each do |depend_target| # set public header for dependent target - hmap.add_hmap_with_header_mapping(depend_target.public_header_mappings_by_file_accessor, generate_type, depend_target.name, depend_target.product_module_name) - depend_target.recursively_add_dependent_headers_to_hmap(hmap, generate_type) + depend_target.generate_hmap(hmap, generate_type, true, true) if depend_target.respond_to?(:generate_hmap) + @unused_targets.concat(depend_target.unused_targets) unless depend_target.unused_targets.empty? + end + end + + def generate_hmap(hmap, generate_type, only_public_headers=true, add_dependency=false) + @unused_targets = Array.new if @unused_targets == nil + # There is no need to add headers of target defines module to hmap. + unless defines_module? + unless $hmap_black_pod_list.include?(name) + Pod::UI.message "- hanlding headers of target :#{name}" + # Create hmap for current target if not in black list. + hmap.add_hmap_with_header_mapping(only_public_headers ? public_header_mappings_by_file_accessor : header_mappings_by_file_accessor, generate_type, name, product_module_name) + # Recursively add dependent targets if needed. + recursively_add_dependent_headers_to_hmap(hmap, generate_type) if add_dependency + end + else + @unused_targets << name end end end diff --git a/lib/cocoapods-project-hmap/xcconfig.rb b/lib/cocoapods-project-hmap/xcconfig.rb index 26b5efe..0d59f6c 100644 --- a/lib/cocoapods-project-hmap/xcconfig.rb +++ b/lib/cocoapods-project-hmap/xcconfig.rb @@ -7,13 +7,21 @@ def remove_attr_with_key(key) @attributes.delete(key) end end - def remove_header_search_path + def remove_header_search_path(white_list=nil) header_search_paths = @attributes['HEADER_SEARCH_PATHS'] if header_search_paths new_paths = Array.new header_search_paths.split(' ').each do |p| - unless p.include?('${PODS_ROOT}/Headers') + if p.include?('${PODS_ROOT}/Headers') == false + # retain path not in normal `pod headers` path new_paths << p + elsif white_list != nil && white_list.empty? == false + white_list.each do |white_target_name| + if p.include?(white_target_name) + new_paths << p + break + end + end end end if new_paths.size > 0 @@ -50,9 +58,9 @@ def remove_system_option_in_other_cflags end end end - def reset_header_search_with_relative_hmap_path(hmap_path) + def reset_header_search_with_relative_hmap_path(hmap_path, white_list=nil) # remove all search paths - remove_header_search_path + remove_header_search_path(white_list) # add build flags new_paths = Array["${PODS_ROOT}/#{hmap_path}"] header_search_paths = @attributes['HEADER_SEARCH_PATHS'] diff --git a/lib/cocoapods_plugin.rb b/lib/cocoapods_plugin.rb index 57f3915..611057f 100644 --- a/lib/cocoapods_plugin.rb +++ b/lib/cocoapods_plugin.rb @@ -1,55 +1,21 @@ # !/usr/bin/env ruby -require 'cocoapods-project-hmap/podfile_dsl' require 'cocoapods-project-hmap/pod_target' require 'cocoapods-project-hmap/post_install_hook_context' module ProjectHeaderMap Pod::HooksManager.register('cocoapods-project-hmap', :post_install) do |post_context| generate_type = $strict_mode ? HmapGenerator::ANGLE_BRACKET : HmapGenerator::BOTH - hmaps_dir=post_context.sandbox_root + '/prebuilt-hmaps' - unless File.exist?(hmaps_dir) - Dir.mkdir(hmaps_dir) - end - post_context.aggregate_targets.each do |one| pods_hmap = HmapGenerator.new - Pod::UI.message "- hanlding headers of aggregate target :#{one.name}".green + Pod::UI.message "- hanlde headers of aggregate target :#{one.name}" one.pod_targets.each do |target| - Pod::UI.message "- hanlding headers of target :#{target.name}" - # There is no need to add headers of dynamic framework to hmap. - unless target.defines_module? && target.requires_frameworks? - pods_hmap.add_hmap_with_header_mapping(target.public_header_mappings_by_file_accessor, generate_type, target.name, target.product_module_name) - else - Pod::UI.message "- skip dynamic framework: #{target.name}" - end - - unless $hmap_black_pod_list.include?(target.name) || $prebuilt_hmap_for_pod_targets == false - target_hmap = HmapGenerator.new - # set project header for current target - target_hmap.add_hmap_with_header_mapping(target.header_mappings_by_file_accessor, HmapGenerator::BOTH, target.name, target.product_module_name) - if target.respond_to?(:recursively_add_dependent_headers_to_hmap) - target.recursively_add_dependent_headers_to_hmap(target_hmap, generate_type) - end - - target_hmap_name="#{target.name}.hmap" - target_hmap_path = hmaps_dir + "/#{target_hmap_name}" - relative_hmap_path = "prebuilt-hmaps/#{target_hmap_name}" - if target_hmap.save_to(target_hmap_path) - target.reset_header_search_with_relative_hmap_path(relative_hmap_path) - end - else - Pod::UI.message "- skip handling headers of target :#{target.name}" - end - end - - pods_hmap_name = "#{one.name}.hmap" - pods_hmap_path = hmaps_dir + "/#{pods_hmap_name}" - relative_hmap_path = "prebuilt-hmaps/#{pods_hmap_name}" - if pods_hmap.save_to(pods_hmap_path) - # override xcconfig - one.reset_header_search_with_relative_hmap_path(relative_hmap_path) + target.generate_hmap(pods_hmap, generate_type, true, false) + target_hmap = HmapGenerator.new + target.generate_hmap(target_hmap, HmapGenerator::BOTH, false, true) + target.save_hmap(target_hmap) end + one.save_hmap(pods_hmap) end end end From 8f388bb1632a73765ba43e18042e68e3962fe973 Mon Sep 17 00:00:00 2001 From: chenxGen Date: Wed, 22 Sep 2021 20:23:12 +0800 Subject: [PATCH 2/3] feat: record pod target prebuilt hmap --- README.md | 65 ++++++----------------- lib/cocoapods-project-hmap/gem_version.rb | 2 +- lib/cocoapods-project-hmap/pod_target.rb | 27 ++++++---- lib/cocoapods-project-hmap/podfile_dsl.rb | 2 +- lib/cocoapods-project-hmap/xcconfig.rb | 15 +++--- lib/cocoapods_plugin.rb | 3 +- 6 files changed, 44 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index ea66b28..7fa5c81 100644 --- a/README.md +++ b/README.md @@ -1,57 +1,20 @@ # cocoapods-project-hmap -此插件思路来源于[《一款可以让大型iOS工程编译速度提升50%的工具》](https://tech.meituan.com/2021/02/25/cocoapods-hmap-prebuilt.html)。通过使用hmap代替文件路径搜索优化预处理阶段中头文件搜索的性能实现编译速度提升。 +此插件思路来源于[《一款可以让大型iOS工程编译速度提升50%的工具》](https://tech.meituan.com/2021/02/25/cocoapods-hmap-prebuilt.html)。通过使用 header map (以下简称 hmap ) 代替文件路径搜索优化预处理阶段中头文件搜索的性能实现编译速度提升。 [English](./README_en.md) -## 效果测试 - -我在另一个项目 [hmap-benchmark](https://github.com/chenxGen/hmap-benchmark/) 写了几个测试用例,统计并输出增加M个源文件,N个第三方库在使用和不使用插件的情况下的编译时间. - -最新一次跑 `run_benchmark.rb` 脚本的时间统计为: - -- Mac mini (Intel i7/16g) : - - ``` - +--------------------------------------+--------------------+------------------------------------------------------------------------------------------------------------------------+ - | Case | Average(s) | Detail(s) | - +--------------------------------------+--------------------+------------------------------------------------------------------------------------------------------------------------+ - | 100 source files & 125 pods (origin) | 192.43606980641684 | [218.57447242736816, 178.7542200088501, 179.97951698303223] | - | 100 source files & 125 pods (plugin) | 165.76690363883972 | [166.8555600643158, 165.40182876586914, 165.04332208633423] | - | > optimization (speed) | 16.09% | | - | > optimization (time cost) | 13.86% | | - | 1 source files & 125 pods (origin) | 170.00553512573242 | [175.31463813781738, 173.79285717010498, 160.9091100692749] | - | 1 source files & 125 pods (plugin) | 124.49473492304485 | [123.54309391975403, 124.4949209690094, 125.4461898803711] | - | > optimization (speed) | 36.56% | | - | > optimization (time cost) | 26.77% | | - | Total (origin) | 181.22080246607462 | [218.57447242736816, 178.7542200088501, 179.97951698303223, 175.31463813781738, 173.79285717010498, 160.9091100692749] | - | Total (plugin) | 145.1308192809423 | [166.8555600643158, 165.40182876586914, 165.04332208633423, 123.54309391975403, 124.4949209690094, 125.4461898803711] | - | > optimization (speed) | 24.87% | | - | > optimization (time cost) | 19.91% | | - +--------------------------------------+--------------------+------------------------------------------------------------------------------------------------------------------------+ - ``` -- Mac air (Apple M1/16g) : - - ``` - +--------------------------------------+-------------------+--------------------------------------------------------------------------------------------------------------------+ - | Case | Average(s) | Detail(s) | - +--------------------------------------+-------------------+--------------------------------------------------------------------------------------------------------------------+ - | 100 source files & 125 pods (origin) | 95.07198365529378 | [91.36949586868286, 96.10968923568726, 97.73676586151123] | - | 100 source files & 125 pods (plugin) | 91.2074584166289 | [90.87663986448735, 90.77357686752014, 91.97326111793518] | - | > optimization (speed) | 4.24% | | - | > optimization (time cost) | 4.06% | | - | 1 source files & 125 pods (origin) | 81.564133644104 | [80.95829105377197, 82.07278513988386, 81.66132473945618] | - | 1 source files & 125 pods (plugin) | 79.28314812668217 | [78.21958923339844, 80.21097787748413, 79.17887886892395] | - | > optimization (speed) | 2.98% | | - | > optimization (time cost) | 2.89% | | - | Total (origin) | 88.3180586496989 | [91.36949586868286, 96.10968923568726, 97.73676586151123, 80.95829105377197, 82.07278513988386, 81.66132473945618] | - | Total (plugin) | 85.2053037/161153 | [90.87663986448735, 90.77357686752014, 91.97326111793518, 78.21958923339844, 80.21097787748413, 79.17887886892395] | - | > optimization (speed) | 3.65% | | - | > optimization (time cost) | 3.52% | | - +--------------------------------------+-------------------+--------------------------------------------------------------------------------------------------------------------+ - ``` - -从上面的输出日志可以看出,插件可以带来3%-36%的编译速度提升,在使用Intel芯片的机器上优化效果还是挺好的,但是在使用Apple M1芯片的机器上效果就约等于没有了,只能说是M1的性能实在牛批。**如果你用的是M1,这里建议直接 `return`** +## 首先,什么样的项目适合使用这个插件? + +1. **仅适合使用 objective-c 作为主要开发语言项目**,因为 swift 没有头文件的概念,从其编译原理上看并没有什么帮助; +2. **不适合 Podfile 中 开启了 `use_frameworks!` or `use_modular_headers!` 的项目使用**,由于为了兼容 clang module 特性,采取的策略是不对开启了 DEFINES_MODULE 的项目生成 hmap; +3. **不适用于 CPU 为 M1 以及后续 M 系列芯片的 Mac**,因为使用之后提升也很小; + +综上,比较适合 old school 的项目使用此插件,如果你的项目满足以上条件推荐使用此插件,不然可能收效甚微,不建议继续往下看了。 + +## 插件名的由来 + +最初版本的插件仅仅为了给 Pod Project 和 Host Project 提供一个可行的跨项目 hmap 方案,填补 Xcode 自带的仅支持 Project 内部的 hmap 的空白,由此得名:cocoapods-project-hmap. ## 环境要求 @@ -92,6 +55,10 @@ target 'app' do end ``` +## 联系方式 + +QQ: 930565063 + ## License cocoapods-project-hmap is released under the MIT license. See LICENSE for details. diff --git a/lib/cocoapods-project-hmap/gem_version.rb b/lib/cocoapods-project-hmap/gem_version.rb index d9f553c..3c3962f 100644 --- a/lib/cocoapods-project-hmap/gem_version.rb +++ b/lib/cocoapods-project-hmap/gem_version.rb @@ -1,3 +1,3 @@ module CocoapodsProjectHmap - VERSION = "0.0.4" + VERSION = "0.0.5" end diff --git a/lib/cocoapods-project-hmap/pod_target.rb b/lib/cocoapods-project-hmap/pod_target.rb index bf5568f..a39b28f 100644 --- a/lib/cocoapods-project-hmap/pod_target.rb +++ b/lib/cocoapods-project-hmap/pod_target.rb @@ -7,6 +7,7 @@ module Pod class Target + attr_accessor :prebuilt_hmap_target_names def save_hmap(hmap) if hmap.empty? == false target_hmap_name="#{name}.hmap" @@ -21,15 +22,22 @@ def save_hmap(hmap) end end end + def add_prebuilt_hmap_target(name) + @prebuilt_hmap_target_names = Array.new if @prebuilt_hmap_target_names == nil + @prebuilt_hmap_target_names << name + end + def concat_prebuilt_hmap_targets(names) + @prebuilt_hmap_target_names = Array.new if @prebuilt_hmap_target_names == nil + @prebuilt_hmap_target_names.concat(names) if names + end end class PodTarget - attr_accessor :unused_targets def reset_header_search_with_relative_hmap_path(hmap_path) if build_settings.instance_of?(Hash) build_settings.each do |config_name, setting| config_file = setting.xcconfig - config_file.reset_header_search_with_relative_hmap_path(hmap_path, @unused_targets.uniq) + config_file.reset_header_search_with_relative_hmap_path(hmap_path, @prebuilt_hmap_target_names.uniq) # https://github.com/CocoaPods/CocoaPods/issues/1216 # just turn off private xcconfig's USE_HEADERMAP flag config_file.set_use_hmap(false) @@ -38,37 +46,36 @@ def reset_header_search_with_relative_hmap_path(hmap_path) end elsif build_settings.instance_of?(BuildSettings::PodTargetSettings) config_file = build_settings.xcconfig - config_file.reset_header_search_with_relative_hmap_path(hmap_path, @unused_targets.uniq) + config_file.reset_header_search_with_relative_hmap_path(hmap_path, @prebuilt_hmap_target_names.uniq) # https://github.com/CocoaPods/CocoaPods/issues/1216 # just turn off private xcconfig's USE_HEADERMAP flag config_file.set_use_hmap(false) config_path = xcconfig_path config_file.save_as(config_path) else - puts 'Unknown build settings'.red + Pod::UI.notice 'Unknown build settings' end end def recursively_add_dependent_headers_to_hmap(hmap, generate_type) dependent_targets.each do |depend_target| # set public header for dependent target depend_target.generate_hmap(hmap, generate_type, true, true) if depend_target.respond_to?(:generate_hmap) - @unused_targets.concat(depend_target.unused_targets) unless depend_target.unused_targets.empty? + concat_prebuilt_hmap_targets(depend_target.prebuilt_hmap_target_names) if depend_target.prebuilt_hmap_target_names end end def generate_hmap(hmap, generate_type, only_public_headers=true, add_dependency=false) - @unused_targets = Array.new if @unused_targets == nil # There is no need to add headers of target defines module to hmap. unless defines_module? unless $hmap_black_pod_list.include?(name) - Pod::UI.message "- hanlding headers of target :#{name}" + add_prebuilt_hmap_target(name) # Create hmap for current target if not in black list. hmap.add_hmap_with_header_mapping(only_public_headers ? public_header_mappings_by_file_accessor : header_mappings_by_file_accessor, generate_type, name, product_module_name) # Recursively add dependent targets if needed. recursively_add_dependent_headers_to_hmap(hmap, generate_type) if add_dependency + else + Pod::UI.message "- skip target in black list :#{name}" end - else - @unused_targets << name end end end @@ -76,7 +83,7 @@ class AggregateTarget def reset_header_search_with_relative_hmap_path(hmap_path) # override xcconfig xcconfigs.each do |config_name, config_file| - config_file.reset_header_search_with_relative_hmap_path(hmap_path) + config_file.reset_header_search_with_relative_hmap_path(hmap_path, @prebuilt_hmap_target_names.uniq) config_path = xcconfig_path(config_name) config_file.save_as(config_path) end diff --git a/lib/cocoapods-project-hmap/podfile_dsl.rb b/lib/cocoapods-project-hmap/podfile_dsl.rb index 33d0166..b5988df 100644 --- a/lib/cocoapods-project-hmap/podfile_dsl.rb +++ b/lib/cocoapods-project-hmap/podfile_dsl.rb @@ -2,7 +2,7 @@ # built-in black list pods (long import path not supported # you can use hmap_black_pod_list to add other pods $hmap_black_pod_list = [ - 'GoogleUtilities', + # 'GoogleUtilities', 'MeshPipe', 'GoogleDataTransport', 'FirebaseCoreDiagnostics', diff --git a/lib/cocoapods-project-hmap/xcconfig.rb b/lib/cocoapods-project-hmap/xcconfig.rb index 0d59f6c..85e5136 100644 --- a/lib/cocoapods-project-hmap/xcconfig.rb +++ b/lib/cocoapods-project-hmap/xcconfig.rb @@ -7,20 +7,19 @@ def remove_attr_with_key(key) @attributes.delete(key) end end - def remove_header_search_path(white_list=nil) + def remove_header_search_path(prebuilt_hmap_target_names=nil) header_search_paths = @attributes['HEADER_SEARCH_PATHS'] if header_search_paths new_paths = Array.new header_search_paths.split(' ').each do |p| - if p.include?('${PODS_ROOT}/Headers') == false + if p.include?('${PODS_ROOT}/Headers') == false or p.end_with?('/Private"') or p.end_with?('/Public"') # retain path not in normal `pod headers` path + # and "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" new_paths << p - elsif white_list != nil && white_list.empty? == false - white_list.each do |white_target_name| - if p.include?(white_target_name) - new_paths << p - break - end + elsif prebuilt_hmap_target_names != nil && prebuilt_hmap_target_names.empty? == false + # add path not prebuilt hmap + if prebuilt_hmap_target_names.select { |name| p.include?(name) }.empty? + new_paths << p end end end diff --git a/lib/cocoapods_plugin.rb b/lib/cocoapods_plugin.rb index 611057f..099bb16 100644 --- a/lib/cocoapods_plugin.rb +++ b/lib/cocoapods_plugin.rb @@ -8,12 +8,13 @@ module ProjectHeaderMap generate_type = $strict_mode ? HmapGenerator::ANGLE_BRACKET : HmapGenerator::BOTH post_context.aggregate_targets.each do |one| pods_hmap = HmapGenerator.new - Pod::UI.message "- hanlde headers of aggregate target :#{one.name}" + Pod::UI.message "- hanlding headers of aggregate target :#{one.name}" one.pod_targets.each do |target| target.generate_hmap(pods_hmap, generate_type, true, false) target_hmap = HmapGenerator.new target.generate_hmap(target_hmap, HmapGenerator::BOTH, false, true) target.save_hmap(target_hmap) + one.concat_prebuilt_hmap_targets(target.prebuilt_hmap_target_names) end one.save_hmap(pods_hmap) end From 1b4c8d242c5552b2e961732e2d44c394912363d9 Mon Sep 17 00:00:00 2001 From: chenxGen Date: Thu, 23 Sep 2021 14:26:51 +0800 Subject: [PATCH 3/3] feat: update README and delete built-in black pod list --- README.md | 18 ++++--- README_en.md | 60 ++++----------------- lib/cocoapods-project-hmap/podfile_dsl.rb | 14 +---- lib/cocoapods-project-hmap/xcconfig.rb | 66 +++++++++++++++-------- 4 files changed, 66 insertions(+), 92 deletions(-) diff --git a/README.md b/README.md index 7fa5c81..8f05150 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,15 @@ 此插件思路来源于[《一款可以让大型iOS工程编译速度提升50%的工具》](https://tech.meituan.com/2021/02/25/cocoapods-hmap-prebuilt.html)。通过使用 header map (以下简称 hmap ) 代替文件路径搜索优化预处理阶段中头文件搜索的性能实现编译速度提升。 -[English](./README_en.md) +[English Version](./README_en.md) ## 首先,什么样的项目适合使用这个插件? -1. **仅适合使用 objective-c 作为主要开发语言项目**,因为 swift 没有头文件的概念,从其编译原理上看并没有什么帮助; -2. **不适合 Podfile 中 开启了 `use_frameworks!` or `use_modular_headers!` 的项目使用**,由于为了兼容 clang module 特性,采取的策略是不对开启了 DEFINES_MODULE 的项目生成 hmap; -3. **不适用于 CPU 为 M1 以及后续 M 系列芯片的 Mac**,因为使用之后提升也很小; +- **仅适合使用 objective-c 作为主要开发语言项目**,因为 swift 没有头文件的概念,从其编译原理上看并没有什么帮助; + +- **不适合 Podfile 中 开启了 `use_frameworks!` or `use_modular_headers!` 的项目使用**;为了兼容 clang module 特性,采取的策略是不对开启了 DEFINES_MODULE 的项目生成 hmap; + +- **不适用于 CPU 为 M1 以及后续 M 系列芯片的 Mac**;因为使用之后提升也很小; 综上,比较适合 old school 的项目使用此插件,如果你的项目满足以上条件推荐使用此插件,不然可能收效甚微,不建议继续往下看了。 @@ -28,15 +30,15 @@ ## 使用 -只需要在你的`Podfile`中调用:`plugin 'cocoapods-project-hmap'` 声明使用该插件。 +只需要在你的`Podfile`中添加如下行:`plugin 'cocoapods-project-hmap'` 声明使用该插件。 同时插件还为`Podfile`提供了一下几个可选的方法调用: -- **set_hmap_black_pod_list:** 开发插件的时候发现有些Pod target使用预生成的hmap编译会出错,比如使用了`#import "a/very/very/long/path/to/header.h"`,暂时没想到好的解决方案,所以针对这种情况需要手动添加到黑名单,不对该target的进行处理,如:`set_hmap_black_pod_list(['PodA','PodB'])`,插件内置了一些这种情况的三方库,具体见:[built-in black list](/lib/cocoapods-project-hmap/podfile_dsl.rb)。如果你还有其他的三方库由于其他原因编译失败,也可以把它添加到黑名单。。。 +- **set\_hmap\_black\_pod\_list:** 如果你有第三方库在使用插件后编译失败,可以尝试把它添加到黑名单中 -- **turn_prebuilt_hmap_off_for_pod_targets:** 如果你发现有太多的三方库需要添加到黑名单,你可以直接通过调用这个方法开启“纯净模式”,关闭插件对Pod Project内部所有target的header处理,仅仅对提供给主项目使用的target处理hmap +- **turn\_prebuilt\_hmap\_off\_for\_pod\_targets:** 如果你发现有太多的三方库需要添加到黑名单,你可以直接通过调用这个方法开启“纯净模式”,关闭插件对 Pod Project 内部所有 target 的 header 处理,仅仅对提供给主项目使用的 target 处理 hmap -- **set_hmap_use_strict_mode:** 在一个target中引用另一个target的header,严格意义上来说应该使用`#import `的方式,但是有些是通过`#import "Header.h"`,这种情况如果设置了对应的header search path编译是可以成功的,比如使用原生的cocoapods情况下,在项目中使用`#import "Masonry.h"`、`#import `和`#import `三种方式引入都是可以成功的,如果你使用这个插件并且开启这个选项后只有`#import `可以编译成功。默认为关闭。 +- **set\_hmap\_use\_strict\_mode:** 在一个 target 中引用另一个 target 的 header,严格意义上来说应该使用`#import `的方式,但是有些是通过`#import "Header.h"`,这种情况如果设置了对应的 header search path 编译是可以成功的,比如使用原生的 cocoapods 情况下,在项目中使用`#import "Masonry.h"`、`#import `和`#import `三种方式引入都是可以成功的,如果你使用这个插件并且开启这个选项后只有`#import `可以编译成功。默认为关闭。 最终你的Podfile看起来会是这样的 : diff --git a/README_en.md b/README_en.md index b8e207b..ee2d834 100644 --- a/README_en.md +++ b/README_en.md @@ -2,56 +2,14 @@ A cocoapods plugin to improve compilation speed at preprosessor phase by using hmap instead of file paths for header searching. The idea comes from [《一款可以让大型iOS工程编译速度提升50%的工具》](https://tech.meituan.com/2021/02/25/cocoapods-hmap-prebuilt.html) -## Benchmark - -There are some test cases in the benchmark project : [hmap-benchmark](https://github.com/chenxGen/hmap-benchmark/). - -The latest outputs by running `run_benchmark.rb` are: - -- Mac mini (Intel i7/16g) : - - ``` - +--------------------------------------+--------------------+------------------------------------------------------------------------------------------------------------------------+ - | Case | Average(s) | Detail(s) | - +--------------------------------------+--------------------+------------------------------------------------------------------------------------------------------------------------+ - | 100 source files & 125 pods (origin) | 192.43606980641684 | [218.57447242736816, 178.7542200088501, 179.97951698303223] | - | 100 source files & 125 pods (plugin) | 165.76690363883972 | [166.8555600643158, 165.40182876586914, 165.04332208633423] | - | > optimization (speed) | 16.09% | | - | > optimization (time cost) | 13.86% | | - | 1 source files & 125 pods (origin) | 170.00553512573242 | [175.31463813781738, 173.79285717010498, 160.9091100692749] | - | 1 source files & 125 pods (plugin) | 124.49473492304485 | [123.54309391975403, 124.4949209690094, 125.4461898803711] | - | > optimization (speed) | 36.56% | | - | > optimization (time cost) | 26.77% | | - | Total (origin) | 181.22080246607462 | [218.57447242736816, 178.7542200088501, 179.97951698303223, 175.31463813781738, 173.79285717010498, 160.9091100692749] | - | Total (plugin) | 145.1308192809423 | [166.8555600643158, 165.40182876586914, 165.04332208633423, 123.54309391975403, 124.4949209690094, 125.4461898803711] | - | > optimization (speed) | 24.87% | | - | > optimization (time cost) | 19.91% | | - +--------------------------------------+--------------------+------------------------------------------------------------------------------------------------------------------------+ - ``` -- Mac air (Apple M1/16g) : - - ``` - +--------------------------------------+-------------------+--------------------------------------------------------------------------------------------------------------------+ - | Case | Average(s) | Detail(s) | - +--------------------------------------+-------------------+--------------------------------------------------------------------------------------------------------------------+ - | 100 source files & 125 pods (origin) | 95.07198365529378 | [91.36949586868286, 96.10968923568726, 97.73676586151123] | - | 100 source files & 125 pods (plugin) | 91.2074584166289 | [90.87663986448735, 90.77357686752014, 91.97326111793518] | - | > optimization (speed) | 4.24% | | - | > optimization (time cost) | 4.06% | | - | 1 source files & 125 pods (origin) | 81.564133644104 | [80.95829105377197, 82.07278513988386, 81.66132473945618] | - | 1 source files & 125 pods (plugin) | 79.28314812668217 | [78.21958923339844, 80.21097787748413, 79.17887886892395] | - | > optimization (speed) | 2.98% | | - | > optimization (time cost) | 2.89% | | - | Total (origin) | 88.3180586496989 | [91.36949586868286, 96.10968923568726, 97.73676586151123, 80.95829105377197, 82.07278513988386, 81.66132473945618] | - | Total (plugin) | 85.2053037/161153 | [90.87663986448735, 90.77357686752014, 91.97326111793518, 78.21958923339844, 80.21097787748413, 79.17887886892395] | - | > optimization (speed) | 3.65% | | - | > optimization (time cost) | 3.52% | | - +--------------------------------------+-------------------+--------------------------------------------------------------------------------------------------------------------+ - ``` - -The outputs indicate that this plugin has about 3%-36% build speed improvement, the improvement is not significant on mac using M1 processor because of Apple M1 processor's high IO performance (I GUESS...). - -**So if you are using Mac with Apple M1 processor, There may be no need to use this plugin.** +## First + +What kind of projects are recommended to use this plugin? + +- **Project using objective-c as their main develop language** +- **Project not using `use_frameworks!` and `use_modular_headers!` in their Podfile** + +and Developer not using Mac with M series CPU. ## Requirement @@ -69,7 +27,7 @@ In your `Podfile`, add this line : `plugin 'cocoapods-project-hmap'` And this plugin also provides Podfile DSL bellow: -- `set_hmap_black_pod_list`: There are some unsolved situation in develping this plugin, such as a 'pod' using a long path import in their code, like `#import "a/very/very/long/path/to/header.h"`, I did not think of a suitable strategy to handle this, so I provide a method to adding then to black list, you can add then with code `set_hmap_black_pod_list(['PodA','PodB'])`, and there are some built-in 'pod' in black list, see : [built-in black list](/lib/cocoapods-project-hmap/podfile_dsl.rb). And if you have some other build error because of this plugin, adding then to black list... +- `set_hmap_black_pod_list`: If you have some compilation error for pod targets because of this plugin, adding the target name to black list... - `turn_prebuilt_hmap_off_for_pod_targets`: If you have to many build error after using this plugin, or have to add to many 'pod' to black list, I provides a most non-intrusive way to use, call this method `turn_prebuilt_hmap_off_for_pod_targets` to ignore hmap prebuilt for most of the pod target (excepting the 'main' pods, named `Pods-${YOUR SCHEME}`). - `set_hmap_use_strict_mode`: Import a header in other library(PodA), strictly speaking, we should use `#import `, but not all library developer do like that, if you turn it on, you can find then. diff --git a/lib/cocoapods-project-hmap/podfile_dsl.rb b/lib/cocoapods-project-hmap/podfile_dsl.rb index b5988df..564d072 100644 --- a/lib/cocoapods-project-hmap/podfile_dsl.rb +++ b/lib/cocoapods-project-hmap/podfile_dsl.rb @@ -1,17 +1,7 @@ # !/usr/bin/env ruby -# built-in black list pods (long import path not supported +# built-in black list pods # you can use hmap_black_pod_list to add other pods -$hmap_black_pod_list = [ - # 'GoogleUtilities', - 'MeshPipe', - 'GoogleDataTransport', - 'FirebaseCoreDiagnostics', - 'FirebaseCore', - 'FirebaseCrashlytics', - 'FirebaseInstallations', - 'CoreDragon', - 'Objective-LevelDB' -] +$hmap_black_pod_list = [] $strict_mode = false $prebuilt_hmap_for_pod_targets = true diff --git a/lib/cocoapods-project-hmap/xcconfig.rb b/lib/cocoapods-project-hmap/xcconfig.rb index 85e5136..44a59d2 100644 --- a/lib/cocoapods-project-hmap/xcconfig.rb +++ b/lib/cocoapods-project-hmap/xcconfig.rb @@ -12,15 +12,8 @@ def remove_header_search_path(prebuilt_hmap_target_names=nil) if header_search_paths new_paths = Array.new header_search_paths.split(' ').each do |p| - if p.include?('${PODS_ROOT}/Headers') == false or p.end_with?('/Private"') or p.end_with?('/Public"') - # retain path not in normal `pod headers` path - # and "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" + unless search_path_should_be_deleted?(p, prebuilt_hmap_target_names) new_paths << p - elsif prebuilt_hmap_target_names != nil && prebuilt_hmap_target_names.empty? == false - # add path not prebuilt hmap - if prebuilt_hmap_target_names.select { |name| p.include?(name) }.empty? - new_paths << p - end end end if new_paths.size > 0 @@ -29,27 +22,58 @@ def remove_header_search_path(prebuilt_hmap_target_names=nil) remove_attr_with_key('HEADER_SEARCH_PATHS') end end - remove_system_option_in_other_cflags + remove_system_option_in_other_cflags(prebuilt_hmap_target_names) end - def remove_system_option_in_other_cflags + def search_path_should_be_deleted?(search_path, prebuilt_hmap_target_names=nil) + # Check if the path should be deleted from search list + # 1. It must be at the ${PODS_ROOT} directory + # 2. It has generated hmap + ret = false + if search_path.include?('${PODS_ROOT}/Headers') + if prebuilt_hmap_target_names + ret = prebuilt_hmap_target_names.select { |name| search_path.include?(name) }.empty? == false + end + end + ret + end + def remove_system_option_in_other_cflags(prebuilt_hmap_target_names=nil) + # ---------------------------------------------- + # -I, --include-directory , --include-directory= + # Add directory to include search path. For C++ inputs, if there are multiple -I options, + # these directories are searched in the order they are given before the standard system directories are searched. + # If the same directory is in the SYSTEM include search paths, for example if also specified with -isystem, the -I option will be ignored + # + # -isystem + # Add directory to SYSTEM include search path + # ---------------------------------------------- flags = @attributes['OTHER_CFLAGS'] if flags new_flags = '' - skip = false + is_isystem_flag = false flags.split(' ').each do |substr| - if skip - skip = false - next + append_str = substr + # Previous flag is `isystem` + if is_isystem_flag + is_isystem_flag = false + if search_path_should_be_deleted?(append_str, prebuilt_hmap_target_names) + next + else + # recover + append_str = "-isystem #{append_str}" + end end - if substr == '-isystem' - skip = true + + if append_str == '-isystem' + is_isystem_flag = true next end + if new_flags.length > 0 new_flags += ' ' end - new_flags += substr + new_flags += append_str end + if new_flags.length > 0 @attributes['OTHER_CFLAGS'] = new_flags else @@ -57,10 +81,10 @@ def remove_system_option_in_other_cflags end end end - def reset_header_search_with_relative_hmap_path(hmap_path, white_list=nil) - # remove all search paths - remove_header_search_path(white_list) - # add build flags + def reset_header_search_with_relative_hmap_path(hmap_path, prebuilt_hmap_target_names=nil) + # Delete associate search paths + remove_header_search_path(prebuilt_hmap_target_names) + # Add hmap file to search path new_paths = Array["${PODS_ROOT}/#{hmap_path}"] header_search_paths = @attributes['HEADER_SEARCH_PATHS'] if header_search_paths