Module: Pod::PodGenerate::Patches::UserIntegratorPatch::ParallelIntegration

Defined in:
lib/cocoapods-podgenerate/patches/user_integrator_patch.rb

Instance Method Summary collapse

Instance Method Details

#integrate_user_targetsObject

── Optimization 1: Parallel integrate_user_targets ──集成用户项目中的所有 Pod target(添加构建阶段、修改配置等)。

【竞态条件修复 H2】多个 AggregateTarget 可能属于同一个 Xcodeproj::Project 文件(例如同一个 .xcodeproj 中包含多个 native target)。TargetIntegrator#integrate! 会修改项目(添加 build phases、修改configurations),如果两个线程同时修改同一个 Xcodeproj::Project 对象,就会产生竞态条件,导致 pbxproj 文件损坏。

修复策略:

1.  user_project 将所有 target 分组
2. 同一个项目内的 target  串行集成(避免竞态)
3. 不同项目之间  并行集成(利用多核性能)


47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/cocoapods-podgenerate/patches/user_integrator_patch.rb', line 47

def integrate_user_targets
  target_integrators = targets_to_integrate.sort_by(&:name).map do |target|
    Pod::Installer::UserProjectIntegrator::TargetIntegrator.new(target, :use_input_output_paths => use_input_output_paths?)
  end

  return if target_integrators.empty?
  return target_integrators.each(&:integrate!) if target_integrators.size <= 1

  # 按 user_project 分组:同一项目的 target 共享同一个 Xcodeproj::Project 对象
  groups = target_integrators.group_by { |ti| ti.send(:target).user_project }

  if groups.size <= 1
    # 所有 target 都属于同一项目 → 串行执行,避免竞态条件
    target_integrators.each(&:integrate!)
  else
    # 不同项目 → 跨组并行,组内串行(同一项目内只有一个线程在修改)
    Pod::UI.message "- Integrating #{target_integrators.size} targets across #{groups.size} projects in parallel"
    threads = groups.map do |project, integrators|
      Thread.new do
        integrators.each(&:integrate!)
      end
    end
    threads.each(&:join)
  end
end

#save_projects(projects) ⇒ Object

── Optimization 2: Parallel save_projects ──保存修改后的用户项目文件。

使用并行线程保存多个 Xcodeproj 项目以提高性能。脏项目调用 project.save 写入 pbxproj,非脏项目则 touch pbxproj 以更新文件修改时间(确保增量构建工具能正确检测变更)。FileUtils.touch 操作用 Mutex 保护,因为 touch 不是线程安全的。



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/cocoapods-podgenerate/patches/user_integrator_patch.rb', line 80

def save_projects(projects)
  projects = projects.uniq

  if projects.size <= 1
    projects.each do |project|
      if project.dirty?
        project.save
      else
        FileUtils.touch(project.path + 'project.pbxproj')
      end
    end
    return
  end

  Pod::UI.message "- Saving #{projects.size} user projects in parallel"
  mutex = Mutex.new
  threads = projects.map do |project|
    Thread.new do
      begin
        if project.dirty?
          project.save
        else
          mutex.synchronize do
            FileUtils.touch(project.path + 'project.pbxproj')
          end
        end
      rescue StandardError => e
        Pod::UI.warn "[cocoapods-podgenerate] Project save error: #{e.message}"
      end
    end
  end
  threads.each(&:join)
end

#warn_about_xcconfig_overridesObject

── Optimization 3: Parallel warn_about_xcconfig_overrides ──检查并警告用户项目中 xcconfig 构建设置的覆盖情况。

通过 prepend 覆盖原始方法,在 integrate! 内部被自动调用。使用 Concurrent::FixedThreadPool 线程池并行检查多个 target,池大小由 compute_pool_size 计算(CPU 核心数 - 1,范围 2..16)。如果 NameError(例如 concurrent-ruby 不可用),回退到串行执行。

注意:此方法操作的是 targets_to_integrate(AggregateTarget 数组),不同 target 属于不同项目(或部分属于同一项目),但由于只是读取xcconfig 设置并打印警告,不修改项目,因此不存在竞态条件。



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/cocoapods-podgenerate/patches/user_integrator_patch.rb', line 125

def warn_about_xcconfig_overrides
  targets = targets_to_integrate
  return if targets.empty?

  if targets.size <= 1
    warn_single_target(targets.first)
    return
  end

  pool_size = compute_pool_size
  Pod::UI.message "- Checking xcconfig overrides for #{targets.size} targets (pool: #{pool_size})"
  pool = Concurrent::FixedThreadPool.new(pool_size)
  targets.each do |aggregate_target|
    pool.post do
      warn_single_target(aggregate_target)
    rescue StandardError => e
      Pod::UI.warn "[cocoapods-podgenerate] Xcconfig warning error: #{e.message}"
    end
  end
  pool.shutdown
  unless pool.wait_for_termination(Pod::PodGenerate::Parallel::ThreadPool::DEFAULT_TIMEOUT)
    Pod::UI.warn '[cocoapods-podgenerate] UserIntegratorPatch: timed out waiting for xcconfig override checks'
  end
rescue NameError
  targets.each { |t| warn_single_target(t) }
end