cocoapods-podgenerate 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a9b3c14400566dae090f4ce380901a448e6aed75b5aab2a9b83e2629c043e547
4
- data.tar.gz: f8ce64ec3991bf97c28aac639a056cb566103cade356a2baf5d3fbac81083573
3
+ metadata.gz: aba68b23582a229140c6580a31d506a3298d8389b6946f432e6429e1fbfe5edd
4
+ data.tar.gz: 2cd7a8c3bf26165fcc425f00bc03a9bffd566946d58cf126ff6c0ee6e47d97aa
5
5
  SHA512:
6
- metadata.gz: 2e524501b0080aab551e23973570c0057570c4a0ddf72ec20996c7a0ff0f7de623d2138edbf667e363d3fd9ad22bb40c91d17fd48091c365e1fcfa953e388a1f
7
- data.tar.gz: 7e2d6b08360a4c1d22ec27f0447634293b7d69f992ab13677829d73c997c00819c4fa99f04c346766b35e7541a25d313de80aba78c7a789c08ab1ba3531490c7
6
+ metadata.gz: d631266d74333c37296f116a0bedb11e31989ab3a6307d531ed4c59a2a55f7f7e8e553294c87cab2eceaa22006ceda20efaa733c123566f6cdf2314b3176e42a
7
+ data.tar.gz: 402636b400e0db9a80b5240730063d23215ac5c58ed87c3ac044a52839eb5a4a5d15600e48a71bf1f6363de3aa85aeae6aff9c466f512f2aa49d6de9522f5621
@@ -2,7 +2,7 @@
2
2
 
3
3
  # [cocoapods-podgenerate]
4
4
  # Performance profiler. Hooks into Pod::Installer to time each phase.
5
- # Output: per-phase wall-clock timing breakdown.
5
+ # Output: per-phase wall-clock timing breakdown with sub-step detail.
6
6
 
7
7
  module Pod
8
8
  module PodGenerate
@@ -23,16 +23,13 @@ module Pod
23
23
  def install
24
24
  return unless enabled?
25
25
  Pod::Installer.prepend(ProfilerHooks)
26
+ Pod::Installer.prepend(ProfilerSubSteps)
26
27
  end
27
28
 
28
29
  def record_phase(name, duration)
29
30
  @phase_timings << [name, duration]
30
31
  end
31
32
 
32
- def swap_or_default(phase_name)
33
- # Called from hooks: returns a timing helper or nil
34
- end
35
-
36
33
  def report
37
34
  return if @phase_timings.empty?
38
35
  total = @phase_timings.map(&:last).sum
@@ -43,9 +40,11 @@ module Pod
43
40
  end
44
41
  Pod::UI.puts " #{'─' * 50}"
45
42
  Pod::UI.puts " #{format('%-35s', 'TOTAL')} #{format('%.2f', total)}s"
43
+ @phase_timings.clear
46
44
  end
47
45
  end
48
46
 
47
+ # Top-level step hooks (v0.1.0)
49
48
  module ProfilerHooks
50
49
  def install!
51
50
  t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
@@ -88,6 +87,50 @@ module Pod
88
87
  Profiler.record_phase(' Integrate user project', elapsed)
89
88
  end
90
89
  end
90
+
91
+ # Sub-step timing hooks (v0.1.2)
92
+ module ProfilerSubSteps
93
+ def stage_sandbox(sandbox, pod_targets)
94
+ t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
95
+ super
96
+ ensure
97
+ elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0
98
+ Profiler.record_phase(' Stage sandbox', elapsed) if elapsed > 0.01
99
+ end
100
+
101
+ def analyze_project_cache
102
+ t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
103
+ result = super
104
+ ensure
105
+ elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0
106
+ Profiler.record_phase(' Analyze project cache', elapsed) if elapsed > 0.01
107
+ result
108
+ end
109
+
110
+ def create_and_save_projects(*args)
111
+ t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
112
+ super
113
+ ensure
114
+ elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0
115
+ Profiler.record_phase(' Create and save projects', elapsed) if elapsed > 0.01
116
+ end
117
+
118
+ def update_project_cache(cache_analysis_result, target_installation_results)
119
+ t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
120
+ super
121
+ ensure
122
+ elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0
123
+ Profiler.record_phase(' Update project cache', elapsed) if elapsed > 0.01
124
+ end
125
+
126
+ def write_lockfiles
127
+ t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
128
+ super
129
+ ensure
130
+ elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0
131
+ Profiler.record_phase(' Write lockfiles', elapsed)
132
+ end
133
+ end
91
134
  end
92
135
  end
93
136
  end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ # [cocoapods-podgenerate]
4
+ # Monkey-patches ProjectCacheAnalyzer to parallelize cache key computation.
5
+ #
6
+ # v0.1.2 Optimization:
7
+ # 1. Parallel MD5 cache key computation across all pod targets
8
+ #
9
+ # TargetCacheKey.from_pod_target computes MD5 checksums of build settings
10
+ # and collects resource dependencies per target. Each computation is fully
11
+ # independent and can run in parallel.
12
+ #
13
+ # Reference: CocoaPods — lib/cocoapods/installer/project_cache/project_cache_analyzer.rb
14
+
15
+ require 'concurrent'
16
+ require 'etc'
17
+
18
+ module Pod
19
+ module PodGenerate
20
+ module Patches
21
+ module CacheAnalyzerPatch
22
+ def self.apply
23
+ Pod::UI.message '[cocoapods-podgenerate] Applying CacheAnalyzerPatch (parallel cache key computation)'
24
+ Pod::Installer::ProjectCache::ProjectCacheAnalyzer.prepend(ParallelCacheKeyComputation)
25
+ end
26
+
27
+ module ParallelCacheKeyComputation
28
+ # Override create_cache_key_mappings to parallelize MD5 computation
29
+ # The original iterates target_by_label sequentially, computing cache keys.
30
+ # Since each target is independent, we use a thread pool.
31
+ def create_cache_key_mappings(target_by_label)
32
+ UI.message '- Creating cache key mappings (parallel)' do
33
+ pool_size = compute_pool_size
34
+ pool = Concurrent::FixedThreadPool.new(pool_size)
35
+ mutex = Mutex.new
36
+ results = {}
37
+
38
+ target_by_label.each do |label, target|
39
+ pool.post do
40
+ key = compute_cache_key(target, target_by_label)
41
+ mutex.synchronize { results[label] = key }
42
+ rescue StandardError => e
43
+ # Bug fix v0.1.3: compute fallback key synchronously to avoid nil
44
+ # entries that would crash ProjectCacheAnalyzer#analyze downstream
45
+ Pod::UI.warn "[cocoapods-podgenerate] Cache key computation error, retrying sync: #{e.message}"
46
+ fallback_key = compute_cache_key(target, target_by_label)
47
+ mutex.synchronize { results[label] = fallback_key }
48
+ end
49
+ end
50
+
51
+ pool.shutdown
52
+ pool.wait_for_termination
53
+ results
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def compute_cache_key(target, target_by_label)
60
+ case target
61
+ when PodTarget
62
+ local = sandbox.local?(target.pod_name)
63
+ checkout_options = sandbox.checkout_sources[target.pod_name]
64
+ TargetCacheKey.from_pod_target(sandbox, target_by_label, target,
65
+ :is_local_pod => local,
66
+ :checkout_options => checkout_options)
67
+ when AggregateTarget
68
+ TargetCacheKey.from_aggregate_target(sandbox, target_by_label, target)
69
+ else
70
+ raise "[BUG] Unknown target type #{target}"
71
+ end
72
+ end
73
+
74
+ def compute_pool_size
75
+ [[Etc.nprocessors - 1, 2].max, 16].min
76
+ rescue NameError
77
+ 4
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -8,14 +8,21 @@
8
8
  # 2. Skip project generation entirely when nothing changed
9
9
  # 3. Parallelize PodTargetIntegrator integration
10
10
  #
11
+ # v0.1.2 Optimization:
12
+ # 4. Parallelize configure_schemes across projects
13
+ #
11
14
  # Reference: CocoaPods — lib/cocoapods/installer.rb
15
+ # lib/cocoapods/installer/xcode/pods_project_generator.rb
16
+
17
+ require 'concurrent'
18
+ require 'etc'
12
19
 
13
20
  module Pod
14
21
  module PodGenerate
15
22
  module Patches
16
23
  module InstallerPatch
17
24
  def self.apply
18
- Pod::UI.message '[cocoapods-podgenerate] Applying InstallerPatch v2'
25
+ Pod::UI.message '[cocoapods-podgenerate] Applying InstallerPatch v3'
19
26
  Pod::Installer.prepend(ForceIncrementalInstall)
20
27
  Pod::Installer::Xcode::PodsProjectGenerator.prepend(ParallelInstall)
21
28
  end
@@ -57,6 +64,74 @@ module Pod
57
64
  Pod::Installer::SandboxDirCleaner.new(sandbox, pod_targets, aggregate_targets).clean!
58
65
  update_project_cache(cache_analysis_result, target_installation_results)
59
66
  end
67
+
68
+ # ── Optimization 3+4: create_and_save_projects with parallel configure_schemes ──
69
+ def create_and_save_projects(pod_targets_to_generate, aggregate_targets_to_generate,
70
+ build_configurations, project_object_version)
71
+ UI.section 'Generating Pods project' do
72
+ generator = create_generator(pod_targets_to_generate, aggregate_targets_to_generate,
73
+ build_configurations, project_object_version,
74
+ installation_options.generate_multiple_pod_projects)
75
+
76
+ pod_project_generation_result = generator.generate!
77
+ @target_installation_results = pod_project_generation_result.target_installation_results
78
+ @pods_project = pod_project_generation_result.project
79
+ @pod_target_subprojects = pod_project_generation_result.projects_by_pod_targets.keys
80
+ @generated_projects = ([pods_project] + pod_target_subprojects || []).compact
81
+ @generated_pod_targets = pod_targets_to_generate
82
+ @generated_aggregate_targets = aggregate_targets_to_generate || []
83
+ projects_by_pod_targets = pod_project_generation_result.projects_by_pod_targets
84
+
85
+ predictabilize_uuids(generated_projects) if installation_options.deterministic_uuids?
86
+ stabilize_target_uuids(generated_projects)
87
+
88
+ projects_writer = Pod::Installer::Xcode::PodsProjectWriter.new(sandbox, generated_projects,
89
+ target_installation_results.pod_target_installation_results,
90
+ installation_options)
91
+ projects_writer.write! do
92
+ run_podfile_post_install_hooks
93
+ end
94
+
95
+ # Parallel configure_schemes (each project is independent)
96
+ pods_project_pod_targets = pod_targets_to_generate - projects_by_pod_targets.values.flatten
97
+ all_projects_by_pod_targets = {}
98
+ if pods_project
99
+ all_projects_by_pod_targets[pods_project] = pods_project_pod_targets
100
+ end
101
+ all_projects_by_pod_targets.merge!(projects_by_pod_targets) if projects_by_pod_targets
102
+
103
+ if all_projects_by_pod_targets.size > 1
104
+ parallel_configure_schemes(all_projects_by_pod_targets, generator, pod_project_generation_result)
105
+ else
106
+ all_projects_by_pod_targets.each do |project, pts|
107
+ generator.configure_schemes(project, pts, pod_project_generation_result)
108
+ end
109
+ end
110
+ end
111
+ end
112
+
113
+ private
114
+
115
+ def parallel_configure_schemes(projects_by_pod_targets, generator, generation_result)
116
+ pool_size = [[Etc.nprocessors - 1, 2].max, 16].min
117
+ Pod::UI.message "- Configuring schemes across #{projects_by_pod_targets.size} projects (pool: #{pool_size})"
118
+
119
+ pool = Concurrent::FixedThreadPool.new(pool_size)
120
+ projects_by_pod_targets.each do |project, pts|
121
+ pool.post do
122
+ generator.configure_schemes(project, pts, generation_result)
123
+ rescue StandardError => e
124
+ Pod::UI.warn "[cocoapods-podgenerate] Scheme configuration error: #{e.message}"
125
+ end
126
+ end
127
+ pool.shutdown
128
+ pool.wait_for_termination
129
+ rescue NameError
130
+ # Fallback: sequential
131
+ projects_by_pod_targets.each do |project, pts|
132
+ generator.configure_schemes(project, pts, generation_result)
133
+ end
134
+ end
60
135
  end
61
136
 
62
137
  # ── Optimization 3: Parallelize PodTargetIntegrator ──
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ # [cocoapods-podgenerate]
4
+ # Monkey-patches MultiPodsProjectGenerator to parallelize pod target installation.
5
+ #
6
+ # v0.1.2 Optimization:
7
+ # 1. Parallel install_all_pod_targets — each pod target's install is independent I/O
8
+ #
9
+ # Architecture:
10
+ # Instead of overriding create_pods_project (which has Ruby constant resolution issues
11
+ # in prepended modules for PodsProjectGenerator's inner classes), we keep the original
12
+ # project creation + file references sequential, and only parallelize the pod target
13
+ # installation step which is the heaviest I/O operation.
14
+ #
15
+ # Reference: CocoaPods — lib/cocoapods/installer/xcode/multi_pods_project_generator.rb
16
+ # lib/cocoapods/installer/xcode/pods_project_generator.rb
17
+
18
+ require 'concurrent'
19
+ require 'etc'
20
+
21
+ module Pod
22
+ module PodGenerate
23
+ module Patches
24
+ module MultiProjectGeneratorPatch
25
+ def self.apply
26
+ Pod::UI.message '[cocoapods-podgenerate] Applying MultiProjectGeneratorPatch (parallel pod target install)'
27
+ Pod::Installer::Xcode::MultiPodsProjectGenerator.prepend(ParallelMultiProjectGenerator)
28
+ end
29
+
30
+ module ParallelMultiProjectGenerator
31
+ # ── Optimization: Parallel install_all_pod_targets ──
32
+ # Each project has independent pod targets (different xcodeproj directories),
33
+ # so PodTargetInstaller operations can run concurrently without locking.
34
+ def install_all_pod_targets(projects_by_pod_targets)
35
+ UI.message '- Installing Pod Targets (parallel)' do
36
+ pool_size = compute_pool_size
37
+ mutex = Mutex.new
38
+ all_results = {}
39
+
40
+ pool = Concurrent::FixedThreadPool.new(pool_size)
41
+ projects_by_pod_targets.each do |project, pts|
42
+ pool.post do
43
+ target_results = install_pod_targets(project, pts)
44
+ mutex.synchronize { all_results.merge!(target_results) }
45
+ rescue StandardError => e
46
+ mutex.synchronize do
47
+ Pod::UI.warn "[cocoapods-podgenerate] Pod target install: #{e.message}"
48
+ end
49
+ end
50
+ end
51
+
52
+ pool.shutdown
53
+ pool.wait_for_termination
54
+ all_results
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def compute_pool_size
61
+ [[Etc.nprocessors - 1, 2].max, 16].min
62
+ rescue NameError
63
+ 4
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -7,14 +7,21 @@
7
7
  # 1. SHA256 digest — skip sort+save for unchanged projects
8
8
  # 2. Parallel save — multiple xcodeproj files saved in threads
9
9
  #
10
+ # v0.1.2 Optimizations:
11
+ # 3. Parallel cleanup_projects — empty group removal across projects
12
+ # 4. Parallel recreate_user_schemes — scheme file creation across projects
13
+ #
10
14
  # Reference: CocoaPods — lib/cocoapods/installer/xcode/pods_project_generator/pods_project_writer.rb
11
15
 
16
+ require 'concurrent'
17
+ require 'etc'
18
+
12
19
  module Pod
13
20
  module PodGenerate
14
21
  module Patches
15
22
  module ProjectWriterPatch
16
23
  def self.apply
17
- Pod::UI.message '[cocoapods-podgenerate] Applying ProjectWriterPatch v2 (incremental + parallel save)'
24
+ Pod::UI.message '[cocoapods-podgenerate] Applying ProjectWriterPatch v3 (incremental + parallel save + parallel write steps)'
18
25
  Pod::Installer::Xcode::PodsProjectWriter.prepend(IncrementalAndParallelSave)
19
26
  end
20
27
 
@@ -27,7 +34,20 @@ module Pod
27
34
  compute_initial_digests
28
35
  end
29
36
 
30
- # ── Optimization: SHA256 skip + parallel save ──
37
+ # ── Optimizations 3+4+2: Parallel write! with parallel cleanup + schemes + save ──
38
+ def write!
39
+ # Parallel cleanup (each project is independent)
40
+ parallel_cleanup_projects(@projects)
41
+
42
+ # Parallel recreate_user_schemes (each project is independent)
43
+ parallel_recreate_user_schemes(@projects)
44
+
45
+ yield if block_given?
46
+
47
+ save_projects(@projects)
48
+ end
49
+
50
+ # ── Optimization 1: SHA256 skip + parallel save ──
31
51
  def save_projects(projects)
32
52
  # Filter: skip projects whose pbxproj is unchanged
33
53
  to_save = projects.select do |project|
@@ -68,6 +88,98 @@ module Pod
68
88
 
69
89
  private
70
90
 
91
+ # ── Optimization 3: Parallel cleanup_projects ──
92
+
93
+ def parallel_cleanup_projects(projects)
94
+ pool_size = compute_pool_size
95
+ Pod::UI.message "- Cleaning up #{projects.size} projects (pool: #{pool_size})"
96
+
97
+ pool = begin
98
+ Concurrent::FixedThreadPool.new(pool_size)
99
+ rescue NameError
100
+ nil
101
+ end
102
+
103
+ if pool
104
+ projects.each do |project|
105
+ pool.post do
106
+ cleanup_single_project(project)
107
+ rescue StandardError => e
108
+ Pod::UI.warn "[cocoapods-podgenerate] Cleanup error: #{e.message}"
109
+ end
110
+ end
111
+ pool.shutdown
112
+ pool.wait_for_termination
113
+ else
114
+ # Fallback: sequential (Concurrent not available)
115
+ projects.each { |p| cleanup_single_project(p) }
116
+ end
117
+ end
118
+
119
+ def cleanup_single_project(project)
120
+ [project.pods, project.support_files_group,
121
+ project.development_pods, project.dependencies_group].each do |group|
122
+ group.remove_from_project if group.respond_to?(:empty?) && group.empty?
123
+ end
124
+ end
125
+
126
+ # ── Optimization 4: Parallel recreate_user_schemes ──
127
+
128
+ def parallel_recreate_user_schemes(projects)
129
+ library_product_types = [:framework, :dynamic_library, :static_library]
130
+
131
+ # Pre-build results_by_native_target once (shared read-only cache)
132
+ results_by_native_target = build_native_target_cache
133
+
134
+ pool_size = compute_pool_size
135
+ Pod::UI.message "- Recreating user schemes for #{projects.size} projects (pool: #{pool_size})"
136
+
137
+ pool = begin
138
+ Concurrent::FixedThreadPool.new(pool_size)
139
+ rescue NameError
140
+ nil
141
+ end
142
+
143
+ if pool
144
+ projects.each do |project|
145
+ pool.post do
146
+ recreate_schemes_for_project(project, library_product_types, results_by_native_target)
147
+ rescue StandardError => e
148
+ Pod::UI.warn "[cocoapods-podgenerate] Scheme recreation error: #{e.message}"
149
+ end
150
+ end
151
+ pool.shutdown
152
+ pool.wait_for_termination
153
+ else
154
+ # Fallback: sequential (Concurrent not available)
155
+ projects.each do |project|
156
+ recreate_schemes_for_project(project, library_product_types, results_by_native_target)
157
+ end
158
+ end
159
+ end
160
+
161
+ def recreate_schemes_for_project(project, library_product_types, results_by_native_target)
162
+ project.recreate_user_schemes(false) do |scheme, target|
163
+ next unless target.respond_to?(:symbol_type)
164
+ next unless library_product_types.include?(target.symbol_type)
165
+ installation_result = results_by_native_target[target]
166
+ next unless installation_result
167
+ installation_result.test_native_targets.each do |test_native_target|
168
+ scheme.add_test_target(test_native_target)
169
+ end
170
+ end
171
+ end
172
+
173
+ def build_native_target_cache
174
+ cache = {}
175
+ @pod_target_installation_results.each do |_, result|
176
+ cache[result.native_target] = result if result.respond_to?(:native_target)
177
+ end
178
+ cache
179
+ end
180
+
181
+ # ── Digest helpers (from v0.1.1) ──
182
+
71
183
  def compute_initial_digests
72
184
  @projects.each do |project|
73
185
  update_digest(project)
@@ -115,6 +227,12 @@ module Pod
115
227
  rescue StandardError
116
228
  nil
117
229
  end
230
+
231
+ def compute_pool_size
232
+ [[Etc.nprocessors - 1, 2].max, 16].min
233
+ rescue NameError
234
+ 4
235
+ end
118
236
  end
119
237
  end
120
238
  end
@@ -4,39 +4,43 @@
4
4
  # Monkey-patches UserProjectIntegrator to parallelize and optimize the
5
5
  # "Integrating client project" step (step 4 of pod install).
6
6
  #
7
- # For projects with many user targets / aggregate targets, the integration
8
- # step runs serially. This patch:
7
+ # v0.1.1:
9
8
  # 1. Parallelizes integrate_user_targets using threads
10
9
  # 2. Parallelizes save_projects using threads
11
- # 3. Caches user_project references to avoid redundant project parsing
10
+ #
11
+ # v0.1.2:
12
+ # 3. Parallelizes warn_about_xcconfig_overrides using threads
12
13
  #
13
14
  # Reference: CocoaPods source
14
15
  # - lib/cocoapods/installer/user_project_integrator.rb
15
- # - lib/cocoapods/installer/user_project_integrator/target_integrator.rb
16
+
17
+ require 'concurrent'
18
+ require 'etc'
16
19
 
17
20
  module Pod
18
21
  module PodGenerate
19
22
  module Patches
20
23
  module UserIntegratorPatch
24
+ IGNORED_KEYS = %w(CODE_SIGN_IDENTITY).freeze
25
+ INHERITED_FLAGS = %w($(inherited) ${inherited}).freeze
26
+
21
27
  def self.apply
22
- Pod::UI.message '[cocoapods-podgenerate] Applying UserIntegratorPatch (parallel client integration)'
28
+ Pod::UI.message '[cocoapods-podgenerate] Applying UserIntegratorPatch v2 (parallel client integration + xcconfig warnings)'
23
29
  Pod::Installer::UserProjectIntegrator.prepend(ParallelIntegration)
24
30
  end
25
31
 
26
32
  module ParallelIntegration
27
- # Override integrate_user_targets to use parallel execution
33
+ # ── Optimization 1: Parallel integrate_user_targets ──
28
34
  def integrate_user_targets
29
35
  target_integrators = targets_to_integrate.sort_by(&:name).map do |target|
30
36
  Pod::Installer::UserProjectIntegrator::TargetIntegrator.new(target, :use_input_output_paths => use_input_output_paths?)
31
37
  end
32
38
 
33
39
  if target_integrators.size <= 1
34
- # Single target — no need for threads
35
40
  target_integrators.each(&:integrate!)
36
41
  return
37
42
  end
38
43
 
39
- # Multiple targets — integrate in parallel
40
44
  Pod::UI.message "- Integrating #{target_integrators.size} targets in parallel"
41
45
  threads = target_integrators.map do |integrator|
42
46
  Thread.new do
@@ -50,7 +54,7 @@ module Pod
50
54
  threads.each(&:join)
51
55
  end
52
56
 
53
- # Override save_projects to use parallel saving
57
+ # ── Optimization 2: Parallel save_projects ──
54
58
  def save_projects(projects)
55
59
  projects = projects.uniq
56
60
 
@@ -65,7 +69,6 @@ module Pod
65
69
  return
66
70
  end
67
71
 
68
- # Save multiple projects in parallel
69
72
  Pod::UI.message "- Saving #{projects.size} user projects in parallel"
70
73
  mutex = Mutex.new
71
74
  threads = projects.map do |project|
@@ -85,6 +88,62 @@ module Pod
85
88
  end
86
89
  threads.each(&:join)
87
90
  end
91
+
92
+ # ── Optimization 3: Parallel warn_about_xcconfig_overrides ──
93
+ # Overrides the original method by prepend — called automatically from integrate!
94
+ def warn_about_xcconfig_overrides
95
+ targets = targets_to_integrate
96
+ return if targets.empty?
97
+
98
+ if targets.size <= 1
99
+ warn_single_target(targets.first)
100
+ return
101
+ end
102
+
103
+ pool_size = compute_pool_size
104
+ Pod::UI.message "- Checking xcconfig overrides for #{targets.size} targets (pool: #{pool_size})"
105
+ pool = Concurrent::FixedThreadPool.new(pool_size)
106
+ targets.each do |aggregate_target|
107
+ pool.post do
108
+ warn_single_target(aggregate_target)
109
+ rescue StandardError => e
110
+ Pod::UI.warn "[cocoapods-podgenerate] Xcconfig warning error: #{e.message}"
111
+ end
112
+ end
113
+ pool.shutdown
114
+ pool.wait_for_termination
115
+ rescue NameError
116
+ targets.each { |t| warn_single_target(t) }
117
+ end
118
+
119
+ private
120
+
121
+ # Per-target xcconfig override check. Runs inside a thread pool slot.
122
+ # NOTE: `print_override_warning` is a private method on the original
123
+ # UserProjectIntegrator class. Ruby allows implicit-receiver calls to
124
+ # private methods from prepended modules (no explicit `self.` prefix).
125
+ def warn_single_target(aggregate_target)
126
+ aggregate_target.user_targets.each do |user_target|
127
+ user_target.build_configurations.each do |config|
128
+ xcconfig = aggregate_target.xcconfigs[config.name]
129
+ next unless xcconfig
130
+
131
+ (xcconfig.to_hash.keys - UserIntegratorPatch::IGNORED_KEYS).each do |key|
132
+ target_values = config.build_settings[key]
133
+ if target_values &&
134
+ !UserIntegratorPatch::INHERITED_FLAGS.any? { |flag| target_values.include?(flag) }
135
+ print_override_warning(aggregate_target, user_target, config, key)
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
141
+
142
+ def compute_pool_size
143
+ [[Etc.nprocessors - 1, 2].max, 16].min
144
+ rescue NameError
145
+ 4
146
+ end
88
147
  end
89
148
  end
90
149
  end
@@ -5,6 +5,8 @@ require 'cocoapods-podgenerate/patches/project_patch'
5
5
  require 'cocoapods-podgenerate/patches/project_writer_patch'
6
6
  require 'cocoapods-podgenerate/patches/analyzer_patch'
7
7
  require 'cocoapods-podgenerate/patches/user_integrator_patch'
8
+ require 'cocoapods-podgenerate/patches/multi_project_generator_patch'
9
+ require 'cocoapods-podgenerate/patches/cache_analyzer_patch'
8
10
  require 'cocoapods-podgenerate/parallel/thread_pool'
9
11
  require 'cocoapods-podgenerate/parallel/batch_processor'
10
12
  require 'cocoapods-podgenerate/benchmark/profiler'
@@ -18,6 +20,8 @@ module Pod
18
20
  Pod::PodGenerate::Patches::ProjectWriterPatch.apply
19
21
  Pod::PodGenerate::Patches::AnalyzerPatch.apply
20
22
  Pod::PodGenerate::Patches::UserIntegratorPatch.apply
23
+ Pod::PodGenerate::Patches::MultiProjectGeneratorPatch.apply
24
+ Pod::PodGenerate::Patches::CacheAnalyzerPatch.apply
21
25
 
22
26
  # Install hook for profiler
23
27
  Pod::PodGenerate::Benchmark::Profiler.install
@@ -28,8 +32,6 @@ module Pod
28
32
  end
29
33
 
30
34
  # Auto-activate when loaded via `plugin` directive in Podfile
31
- # The :pre_install hook is set up by hooks.rb, but we also support
32
- # direct `plugin` activation which defers to when CocoaPods is loaded.
33
35
  if defined?(Pod::HooksManager)
34
36
  Pod::PodGenerate.activate
35
37
  else
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cocoapods-podgenerate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - PodGenerate Team
@@ -93,7 +93,9 @@ files:
93
93
  - lib/cocoapods-podgenerate/parallel/batch_processor.rb
94
94
  - lib/cocoapods-podgenerate/parallel/thread_pool.rb
95
95
  - lib/cocoapods-podgenerate/patches/analyzer_patch.rb
96
+ - lib/cocoapods-podgenerate/patches/cache_analyzer_patch.rb
96
97
  - lib/cocoapods-podgenerate/patches/installer_patch.rb
98
+ - lib/cocoapods-podgenerate/patches/multi_project_generator_patch.rb
97
99
  - lib/cocoapods-podgenerate/patches/project_patch.rb
98
100
  - lib/cocoapods-podgenerate/patches/project_writer_patch.rb
99
101
  - lib/cocoapods-podgenerate/patches/user_integrator_patch.rb