cocoapods-yaml 1.0.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 82dbb70f4e4aed183d28e3155d99048f2fb4fab56b2c6937d705c0ff2254216f
4
+ data.tar.gz: 9dd087ceaa7cb278628592b8941965cee7e8708c543b5a6f889ad074d06a8f85
5
+ SHA512:
6
+ metadata.gz: 3559c6d25653e343f7c6776fdd0f7a7cd047ea24482f4171759e272a9cedb5cc6c922d8a9414e9971a7b3b0946c5ddb40e827e49545764f5a4bc176e3d1cbd6b
7
+ data.tar.gz: 05acafb2d925b7a0c9d785ceb623fe8d4a24264d2b3d1c090170db54d11061ae8c1e7567de6867e792493febcf58ed93460ee704ca067970ea0fcc5ff62a343b
data/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ # Changelog
2
+
3
+ ## 1.0.0 - 2026-10-16
4
+
5
+ 以 YAML 文件按 target 管理 CocoaPods 依赖的插件首个正式版本,安装后由 CocoaPods 自动加载。
6
+
7
+ - 每个 App/扩展 target 一个依赖文件,二方/三方组件统一维护,支持源地址变量复用
8
+ - 本地覆盖文件支持按组件名覆盖任意依赖做联调,新组件直接写入即自动集成
9
+ - 支持按源码引用关系自动裁剪未使用的依赖,加速编译,兼容 Xcode 16 与 Swift 工程
10
+ - 自动注册私有 specs 源并屏蔽官方 CDN 源,避免网络问题与多版本告警
11
+ - 内置 import 写法检查、依赖完整性检查、模拟器架构检查与三方库编译兼容补丁
12
+ - Debug 包内自动生成依赖清单,便于排查组件版本问题
13
+ - 提供依赖关系查询等辅助命令
data/README.md ADDED
@@ -0,0 +1,129 @@
1
+ # cocoapods-yaml
2
+
3
+ 以 YAML 文件按 target 管理 CocoaPods 依赖的插件。符合 `cocoapods-*` 插件命名约定,安装后由 CocoaPods 自动加载,Podfile 直接使用 `yaml_dependencies!` DSL
4
+
5
+ 与 [pod-binary-pack](https://rubygems.org/gems/pod-binary-pack) 配套使用:本插件负责壳工程依赖管理与裁剪安装,后者负责组件二进制化打包与双私有源发布。
6
+
7
+ ## 功能特性
8
+
9
+ - **按 target 管理依赖**:每个 App/扩展 target 一个依赖文件,二方/三方组件统一维护,支持 YAML 锚点变量复用源地址
10
+ - **本地覆盖联调**:`LocalDependencies.yaml` 按 pod 名覆盖任意依赖(path 联调),不提交 git;全新组件直接写入即自动集成主 App target
11
+ - **未使用依赖裁剪**:按源码 import 引用关系裁剪未使用依赖,加速编译;兼容 Xcode 16+ 同步分组与 Swift import
12
+ - **私有源管理**:自动注册私有 specs 源并屏蔽官方 CDN trunk,间接依赖优先命中二进制源
13
+ - **内置检查**:import 写法、podspec 依赖完整性、模拟器架构兼容、三方库源码编译兼容补丁,post_install 自动执行
14
+ - **Debug 依赖清单**:依赖清单 json 仅打入 Debug 包,便于排查组件版本
15
+
16
+ ## 接入
17
+
18
+ ```ruby
19
+ # Podfile
20
+ platform :ios, '15.0'
21
+
22
+ yaml_dependencies! do |target_name|
23
+ # target_name: 当前集成的 target 名称(App/扩展),每个声明了依赖的 target 回调一次
24
+ # 可按 target 定制,例如扩展用静态库:
25
+ # use_frameworks! :linkage => :static if target_name == 'ShareExtension'
26
+ use_frameworks!
27
+ end
28
+ ```
29
+
30
+ 未接入的工程可执行 `pod yaml init` 自动生成模板 Podfile 与依赖文件(已有 Podfile 会先备份为 `Podfile.bak.时间戳` 再替换)。
31
+
32
+ ## 依赖文件
33
+
34
+ 放在工程根目录,两类:
35
+
36
+ | 文件 | 是否提交 git | 用途 |
37
+ | --- | --- | --- |
38
+ | `<Target>Dependencies.yaml` | 是 | 各 target 的依赖清单,二方/三方统一维护 |
39
+ | `LocalDependencies.yaml` | 否(自动加入 .gitignore) | 本地覆盖联调、临时引入新组件;`TRIM_UNUSED_POD`/`ENABLE_TRUNK` 开关 |
40
+
41
+ `<Target>Dependencies.yaml` 示例:
42
+
43
+ ```yaml
44
+ SOURCES:
45
+ - &binary git@gitee.com:xxx/specs.git # 二进制源(第一个,默认)
46
+ - &source git@gitee.com:xxx/specs.git # 源码源(备选)
47
+
48
+ PODS:
49
+ - pod: Masonry # 默认源 + 版本号引用
50
+ version: 1.1.0
51
+ - pod: BSDebugPlugins # 锚点引用源码源 + 仅 Debug 集成
52
+ version: 1.0.0-SNAPSHOT
53
+ source: *source
54
+ configurations: [Debug]
55
+ - pod: CocoaService # git 分支引用
56
+ git: git@gitee.com:xxx/CocoaService.git
57
+ branch: master
58
+ ```
59
+
60
+ ### PODS 字段
61
+
62
+ | 字段 | 说明 |
63
+ | --- | --- |
64
+ | `pod` | 必填,组件名 |
65
+ | `version` / `path` / `podspec` / `git`+`tag` / `git`+`branch` | 引用方式,五选一 |
66
+ | `source` | 显式指定 specs 源,默认取 `SOURCES` 第一个 |
67
+ | `subspecs` | 仅集成指定 subspec(数组);与 `configurations` 互斥(CocoaPods 限制,插件自动展开为多条声明) |
68
+ | `configurations` | 仅在指定 Build Configuration 集成,如 `[Debug]` |
69
+ | `inhibit_warnings` | 屏蔽警告,target 文件默认 true、本地覆盖默认 false |
70
+
71
+ `LocalDependencies.yaml` 的引用方式与字段规则相同,并额外支持顶层开关:
72
+
73
+ ```yaml
74
+ TRIM_UNUSED_POD: false # 是否裁剪未使用依赖
75
+ ENABLE_TRUNK: false # 是否恢复官方 CDN trunk 源
76
+ PODS: []
77
+ ```
78
+
79
+ ## 本地覆盖与联调
80
+
81
+ - **同名覆盖**:LocalDependencies 中按 pod 名声明,覆盖任意 `<Target>Dependencies.yaml` 中的同名组件(通常写 `path` 做本地联调)
82
+ - **全新组件**:target 依赖文件都未声明的组件写入 LocalDependencies 后自动加入主 App target(不会被裁剪);App 扩展 target 不自动注入,需在对应依赖文件显式声明
83
+ - path 引用的本地仓库自动开启 SNAPSHOT tag 强制同步(`git fetch/pull` 自动前移被删除重建的 tag)
84
+
85
+ ## 未使用依赖裁剪
86
+
87
+ 开发模式下可开启,移除未被源码引用的依赖库以加速编译:
88
+
89
+ ```bash
90
+ pod install --trim # 单次开启(优先于配置文件)
91
+ pod install --full # 单次关闭
92
+ pod update --trim # update 同样支持
93
+ ```
94
+
95
+ 或在 `LocalDependencies.yaml` 中设置 `TRIM_UNUSED_POD: true` 长期开启。
96
+
97
+ ## 私有源管理
98
+
99
+ 插件按各 target 依赖文件 `SOURCES` 的声明顺序注册 Podfile 顶层 source(二进制源优先,间接依赖优先命中二进制产物),并屏蔽官方 CDN trunk——trunk 既会因网络问题导致 `pod install` 失败,也会与私有源同名 podspec 产生多版本告警。确需 trunk 时在 `LocalDependencies.yaml` 设置 `ENABLE_TRUNK: true`。
100
+
101
+ ## 内置检查(post_install 自动执行)
102
+
103
+ | 检查 | 说明 |
104
+ | --- | --- |
105
+ | import 检查 | 引号形式 import 在本组件找不到头文件时给出建议写法;多组件同名头文件列出候选;GBK 源文件容错 |
106
+ | podspec 检查 | 代码实际需要的模块 vs podspec 声明,报告可能缺失/未使用的依赖(含嵌套 subspec 与平台级声明);`DISABLE_CHECK_PODSPEC: true` 可关闭 |
107
+ | 模拟器架构检查 | 报告缺 arm64 模拟器 slice(需 Rosetta)或完全无模拟器 slice 的二进制依赖,Apple Silicon 链接失败根因预警 |
108
+ | 源码兼容补丁 | 三方库源码微调兼容新版 Xcode(YYText 链式比较、废弃头 `<netinet6/in6.h>`、FBRetainCycleDetector 等);命中文件警告输出,`COCOAPODS_YAML_SKIP_SOURCE_PATCH=1` 整体关闭 |
109
+
110
+ 另自动完成:间接依赖及其引用方输出、Debug 依赖清单 json 生成与注入(Release 不入包)、yaml 文件加入 Pods 工程引用、孤儿依赖文件清理提示。
111
+
112
+ ## 命令
113
+
114
+ ```bash
115
+ pod yaml init # 初始化/备份替换 Podfile 与依赖文件模板
116
+ pod yaml relation [组件名] # 从 Podfile.lock 查询正/反向依赖
117
+ # --forward 只看正向;--sort 按依赖数量排序
118
+ ```
119
+
120
+ ## 环境变量
121
+
122
+ | 变量 | 说明 |
123
+ | --- | --- |
124
+ | `COCOAPODS_YAML_VERBOSE` | 非空时输出调试细节(等效 `pod install --verbose`) |
125
+ | `COCOAPODS_YAML_SKIP_SOURCE_PATCH` | 非空时跳过全部三方库源码兼容补丁 |
126
+
127
+ ## License
128
+
129
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../core_helper'
4
+ require_relative '../version'
5
+ core_path = Core::CoreHelper.core_path
6
+
7
+ require "#{core_path}/logger"
8
+ require "#{core_path}/yaml/yaml_dependencies"
9
+
10
+ # 裁剪未使用依赖的下载流程补丁,是否裁剪由TRIM_UNUSED_POD/pod install/update --trim判定
11
+ require "#{core_path}/thin/installer"
12
+
13
+ module Pod
14
+ class Podfile
15
+ # DSL入口: 在Podfile中通过 yaml_dependencies! 接入
16
+ # block的参数为当前集成的target名(App/扩展),可按target定制 use_frameworks! 等配置
17
+ def yaml_dependencies!
18
+ plugin('cocoapods-yaml')
19
+
20
+ yaml_dep = Pod::YamlDependencies.new
21
+ CocoaPods::Yaml.log.info("cocoapods-yaml版本:#{CocoaPods::Yaml::VERSION}")
22
+ yaml_dep.install_yaml_dep(self) do |target_name|
23
+ yield(target_name) if block_given?
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ # CocoaPods 插件入口:gem 以 cocoapods- 开头时,本文件会被 pod 命令自动加载,
4
+ # 因此 Podfile 无需手动 require,直接使用 yaml_dependencies! DSL 即可。
5
+
6
+ require 'cocoapods'
7
+
8
+ require_relative 'cocoapods/yaml'
9
+
10
+ require "#{Core::CoreHelper.core_path}/command/yaml_init"
11
+ require "#{Core::CoreHelper.core_path}/command/yaml_relation"
12
+ require "#{Core::CoreHelper.core_path}/command/trim_mode"
13
+ require "#{Core::CoreHelper.core_path}/check/project"
14
+ require "#{Core::CoreHelper.core_path}/check/podspec"
15
+ require "#{Core::CoreHelper.core_path}/check/simulator_arch"
16
+ require "#{Core::CoreHelper.core_path}/modify/source_patcher"
17
+ require "#{Core::CoreHelper.core_path}/reference/project"
18
+ require "#{Core::CoreHelper.core_path}/indirect/indirect"
19
+ require "#{Core::CoreHelper.core_path}/reference/unused"
20
+ require "#{Core::CoreHelper.core_path}/check/import"
21
+
22
+ # pod install/update 在工程目录但缺少Podfile时自动生成模板(零风险初始化);
23
+ # 已有Podfile但未接入时仅提示 pod yaml init,不自动替换
24
+ if ARGV.include?('install') || ARGV.include?('update')
25
+ CocoaPods::Yaml::PodfileSetup.auto_on_install
26
+ end
27
+
28
+ module CocoapodsPlugin
29
+
30
+ Pod::HooksManager.register('cocoapods-yaml', :post_install) do |context|
31
+
32
+ project = Xcodeproj::XCodeProject.new
33
+
34
+ # 检查import
35
+ checker1 = Xcodeproj::ImportChecker.new(project)
36
+ checker1.check_import
37
+
38
+ # 检查podspec
39
+ checker2 = Xcodeproj::PodspecChecker.new(project)
40
+ checker2.check_podspec
41
+
42
+ # 模拟器架构兼容检查:找出模拟器产物不含arm64 slice的组件
43
+ # (Apple Silicon 上按默认 arm64 编译模拟器时链接失败的根因)
44
+ Xcodeproj::SimulatorArchChecker.new.check
45
+
46
+ # 三方库源码编译兼容补丁(netinet6 头替换、FBRetainCycleDetector 历史兼容等),
47
+ # 同时覆盖普通 pod 与打包期 :path 开发 pod
48
+ ModifyCode::SourcePatcher.patch(context.sandbox)
49
+
50
+ # 间接依赖
51
+ PodfileLock::Indirect.new.log_indirect
52
+
53
+ # 添加yaml到Pods工程引用和gitignore
54
+ Project::ProjectManager.yaml_reference
55
+
56
+ # 生成Debug包内依赖清单json并注入资源脚本(仅Debug)
57
+ Project::ProjectManager.dependency_manifest(context)
58
+
59
+ # 裁剪未使用依赖的开关提示
60
+ Unused::UnusedManager.check_unused
61
+
62
+ end
63
+
64
+ end
@@ -0,0 +1,74 @@
1
+ require_relative 'project'
2
+
3
+ module Xcodeproj
4
+ class ImportChecker
5
+
6
+ # @return [XCodeProject]
7
+ attr_accessor :project
8
+
9
+ # @param project::[XCodeProject]
10
+ def initialize(project)
11
+ @project = project
12
+ end
13
+
14
+ # 检查import规范
15
+ def check_import
16
+ CocoaPods::Yaml.log.step("import检查中...")
17
+ start = (Time.now.to_f * 1000).to_i
18
+ module_count = 0
19
+ file_count = 0
20
+
21
+ project.module_items.each do |module_name, module_item|
22
+ unless module_item.is_development_module
23
+ next
24
+ end
25
+ module_count += 1
26
+
27
+ messages = []
28
+
29
+ did_check_imports = {}
30
+ module_item.init_import_info
31
+
32
+ module_item.import_items.each do |import_item|
33
+ # 同一组件内同名import只检查一次(无论本组件能否找到头文件),避免重复告警与重复全量扫描
34
+ next if did_check_imports.key?(import_item.import_file_name)
35
+ did_check_imports[import_item.import_file_name] = true
36
+ file_count += 1
37
+
38
+ need_module_item = project.module_items[import_item.module_name]
39
+ real_path = need_module_item&.header_files&.[](import_item.import_file_name)
40
+ next if real_path
41
+
42
+ # 本组件找不到,去其他组件查找所属模块
43
+ expect_modules = find_modules_with_header_file(import_item.import_file_name)
44
+ if expect_modules.size == 1
45
+ messages << "#{import_item.file_path.basename} 第#{import_item.line_index}行 #{import_item.import_name} => #import <#{expect_modules.first}/#{import_item.import_file_name}>"
46
+ elsif expect_modules.size > 1
47
+ # 多个组件含同名头文件时无法确定归属,给出候选而不是猜测一个可能错误的组件
48
+ messages << "#{import_item.file_path.basename} 第#{import_item.line_index}行 #{import_item.import_name} 在本组件找不到该头文件,且组件#{expect_modules.join('、')}均含同名头文件,请手动确认正确写法"
49
+ end
50
+ end
51
+
52
+ if messages.size >0
53
+ CocoaPods::Yaml.log.warn("组件#{module_item.module_name}存在以下情况, 请检查:")
54
+ messages.each { |message| CocoaPods::Yaml.log.info(" #{message}") }
55
+ end
56
+
57
+ end
58
+
59
+ duration = ((Time.now.to_f * 1000).to_i - start)*0.001
60
+ # 本检查器只解析开发组件源码中的引号形式import(#import "X.h"),尖括号形式由podspec检查器负责
61
+ CocoaPods::Yaml.log.detail("import检查完毕! 共检查#{module_count}个组件(#{file_count}处引号import,去重后) 耗时:#{duration.round(2)}秒")
62
+ end
63
+
64
+ private
65
+ # 根据头文件名查找所有包含该头文件的组件(按头文件basename匹配)
66
+ # @return [Array<String>] 组件名列表;0个=无匹配,1个=可确定归属,>1个=重名无法确定
67
+ def find_modules_with_header_file(header_file_name)
68
+ project.module_items.each_with_object([]) do |(_module_name, module_item), result|
69
+ result << module_item.module_name if module_item.header_files[header_file_name]
70
+ end
71
+ end
72
+
73
+ end
74
+ end
@@ -0,0 +1,220 @@
1
+
2
+ module Xcodeproj
3
+ class ImportItem
4
+ attr_accessor :import_name
5
+ attr_accessor :module_name
6
+ attr_accessor :file_path
7
+ attr_accessor :import_file_name
8
+ attr_accessor :line_index
9
+ attr_accessor :belong_module
10
+ def initialize
11
+ @import_name = nil
12
+ @module_name = nil
13
+ @file_path = nil
14
+ @import_file_name = nil
15
+ @line_index = nil
16
+ @belong_module = nil
17
+ end
18
+
19
+ end
20
+
21
+ class ModuleItem
22
+
23
+ public
24
+ # 组名名
25
+ attr_accessor :module_name
26
+
27
+ # 组件路径
28
+ attr_accessor :module_path
29
+
30
+ # podspec路径
31
+ attr_accessor :podspec_path
32
+
33
+ # 组件目录的所有被引用的文件数组
34
+ attr_accessor :header_files
35
+
36
+ # 组件目录的所有被引用的文件数组
37
+ attr_accessor :source_files
38
+
39
+ # 是否是开发模块
40
+ attr_accessor :is_development_module
41
+
42
+ # [ImportItem]
43
+ attr_accessor :import_items
44
+
45
+ # [ImportItem]
46
+ attr_accessor :absolute_import_items
47
+
48
+ # [String]
49
+ attr_accessor :frameworks
50
+
51
+ def initialize(module_name)
52
+ @module_name = module_name
53
+ @module_path = nil
54
+ @podspec_path = nil
55
+ @header_files = {}
56
+ @source_files = {}
57
+ @import_items = []
58
+ @absolute_import_items = []
59
+ @is_development_module = false
60
+ @frameworks = []
61
+ end
62
+
63
+ # 生成 import <XX/YY.h>的信息
64
+ # 生成 include <XX/YY.h>的信息
65
+ def init_absolute_import_info
66
+ source_files.each_value do |source_file_path|
67
+ if File.directory?(source_file_path)
68
+ next
69
+ end
70
+
71
+ line_array = safe_readlines(source_file_path)
72
+ line_array.each_with_index { |line, line_index|
73
+ line = line.gsub(" ", "")
74
+
75
+ # 进入实现体后不再解析import(import按约定只出现在文件头部),
76
+ # 该判断必须在#import前缀过滤之前,否则@implementation行已被next而永远不可达
77
+ if line.start_with?("@implementation")
78
+ break
79
+ end
80
+
81
+ # 处理特殊情况
82
+ if line.start_with?("#import\"NBSAppAgent.h\"")
83
+ line = "#import<tingyunApp/NBSAppAgent.h>"
84
+ end
85
+
86
+ if not line.start_with?("#import<") and not line.start_with?("#include<")
87
+ next
88
+ end
89
+
90
+ # 前一行用于识别条件编译(#if/#else)内的import
91
+ pre_index = line_index-1 < 0 ? 0 : line_index-1
92
+ pre_line = line_array[pre_index]
93
+
94
+ import_item = other_module_import(line, source_file_path, line_index, @module_name, pre_line)
95
+ if import_item
96
+ absolute_import_items << import_item
97
+ end
98
+ }
99
+ end
100
+
101
+ absolute_import_items
102
+ end
103
+
104
+ # 生成 import "XXX.h"的信息
105
+ def init_import_info
106
+ source_files.each_value do |source_file_path|
107
+ if File.directory?(source_file_path)
108
+ next
109
+ end
110
+
111
+ line_array = safe_readlines(source_file_path)
112
+ line_array.each_with_index { |line, line_index|
113
+ line = line.gsub(" ", "")
114
+
115
+ # 进入实现体后不再解析import,判断必须在#import前缀过滤之前(否则该行已被next)
116
+ if line.start_with?("@implementation")
117
+ break
118
+ end
119
+
120
+ unless line.start_with?("#import")
121
+ next
122
+ end
123
+
124
+ # 前一行的内容
125
+ pre_index = line_index-1 < 0 ? 0 : line_index-1
126
+ pre_line = line_array[pre_index]
127
+
128
+ if line.start_with?("#import\"") #本组件
129
+ import_item = own_module_import(line, source_file_path, line_index, @module_name, pre_line)
130
+ if import_item
131
+ import_items << import_item
132
+ end
133
+ elsif line.start_with?("#import<") #其他组件
134
+
135
+ end
136
+
137
+ }
138
+ end
139
+
140
+ import_items
141
+ end
142
+
143
+ private
144
+ # 生成自己模块的import item
145
+ # @param line 以 import" 开头
146
+ def own_module_import(line, file_path, line_index, module_name, pre_line)
147
+ line_split_array = line.split("\"")
148
+ if line_split_array.size <= 1
149
+ return nil
150
+ end
151
+
152
+ import_content = "#{line_split_array[0]}\"#{line_split_array[1]}\"" # import"XXX.h" 或import"XXX.AAA.h" 防止后边有其他内容
153
+
154
+ # 特殊处理
155
+ if import_content == "#import\"NBSAppAgent.h\""
156
+ return nil
157
+ end
158
+
159
+ pre_line = pre_line.gsub(" ", "").gsub("\n", "")
160
+ if pre_line.start_with?("#if") or pre_line == "#else"
161
+ return nil
162
+ end
163
+
164
+ import_file_name = import_content
165
+ import_file_name = import_file_name.gsub("#import\"", "")
166
+ import_file_name = import_file_name.gsub("\"", "")
167
+ # 统一取basename与header_files的键(头文件名)对齐,兼容 #import "Sub/X.h" 带子目录的写法
168
+ import_file_name = File.basename(import_file_name) # 变成 XX.h
169
+
170
+
171
+ import_item = ImportItem.new # (import_name, module_name, file_name, line_index, podfile)
172
+ import_item.import_name = import_content.gsub("#import\"", "#import \"")
173
+ import_item.module_name = module_name
174
+ import_item.file_path = file_path
175
+ import_item.import_file_name = import_file_name
176
+ import_item.line_index = line_index + 1
177
+
178
+ import_item
179
+ end
180
+
181
+
182
+ def other_module_import(line, file_path, line_index, module_name, pre_line)
183
+ line = line.gsub("\n","")
184
+ line_split_array = line.split("/") #import<XX/YY.h> 或 #import<XX/YY/ZZ.h>
185
+ if line_split_array.size <= 1
186
+ return nil
187
+ end
188
+
189
+ pre_line = pre_line.gsub(" ", "").gsub("\n", "")
190
+ if pre_line.start_with?("#if") or pre_line == "#else"
191
+ return nil
192
+ end
193
+
194
+ import_file_name = line_split_array[line_split_array.size-1].gsub(">", "")
195
+ belong_module = line_split_array[0].gsub("#import<", "").gsub("#include<", "")
196
+ import_name = line.gsub("#import<", "#import <").gsub("#include<", "#include <")
197
+
198
+ import_item = ImportItem.new # (import_name, module_name, file_name, line_index, podfile)
199
+ import_item.import_name = import_name
200
+ import_item.module_name = module_name
201
+ import_item.file_path = file_path
202
+ import_item.import_file_name = import_file_name
203
+ import_item.line_index = line_index + 1
204
+ import_item.belong_module = belong_module
205
+
206
+ import_item
207
+
208
+ end
209
+
210
+ # 容错读取源码行:历史工程可能存在GBK等非UTF-8文件。
211
+ # 注意: 直接给IO.readlines传encoding: 'utf-8'在内外编码相同时不会转码,非法字节仍会透传,
212
+ # 这里先按二进制读取,再显式做一次同编码转换替换非法字节,
213
+ # 避免后续字符串处理抛invalid byte sequence中断整个post_install钩子
214
+ def safe_readlines(file_path)
215
+ content = File.read(file_path, mode: 'rb')
216
+ content.encode!('UTF-8', 'UTF-8', invalid: :replace, undef: :replace, replace: '?')
217
+ content.lines
218
+ end
219
+ end
220
+ end