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 +7 -0
- data/CHANGELOG.md +13 -0
- data/README.md +129 -0
- data/lib/cocoapods/yaml.rb +27 -0
- data/lib/cocoapods_plugin.rb +64 -0
- data/lib/core/check/import.rb +74 -0
- data/lib/core/check/item.rb +220 -0
- data/lib/core/check/podspec.rb +195 -0
- data/lib/core/check/project.rb +405 -0
- data/lib/core/check/simulator_arch.rb +145 -0
- data/lib/core/check/util.rb +37 -0
- data/lib/core/check/xcodeproj.rb +81 -0
- data/lib/core/command/trim_mode.rb +53 -0
- data/lib/core/command/yaml_init.rb +28 -0
- data/lib/core/command/yaml_relation.rb +43 -0
- data/lib/core/dep/find.rb +164 -0
- data/lib/core/indirect/indirect.rb +72 -0
- data/lib/core/logger.rb +85 -0
- data/lib/core/modify/source_patcher.rb +204 -0
- data/lib/core/my_constants.rb +16 -0
- data/lib/core/podfilelock.rb +17 -0
- data/lib/core/reference/project.rb +201 -0
- data/lib/core/reference/unused.rb +30 -0
- data/lib/core/setup/podfile_setup.rb +107 -0
- data/lib/core/thin/installer.rb +171 -0
- data/lib/core/thin/local.rb +40 -0
- data/lib/core/thin/pod_target.rb +32 -0
- data/lib/core/thin/shell.rb +180 -0
- data/lib/core/variable.rb +2 -0
- data/lib/core/yaml/yaml_dependencies.rb +774 -0
- data/lib/core/yaml/yaml_pod.rb +63 -0
- data/lib/core_helper.rb +10 -0
- data/lib/version.rb +7 -0
- metadata +133 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
require_relative 'project'
|
|
2
|
+
require_relative 'util'
|
|
3
|
+
require_relative '../my_constants'
|
|
4
|
+
|
|
5
|
+
module Xcodeproj
|
|
6
|
+
class PodspecChecker
|
|
7
|
+
|
|
8
|
+
# @return [XCodeProject]
|
|
9
|
+
attr_accessor :project
|
|
10
|
+
|
|
11
|
+
# @param project::[XCodeProject]
|
|
12
|
+
def initialize(project)
|
|
13
|
+
@project = project
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# 检查podspec文件
|
|
17
|
+
def check_podspec
|
|
18
|
+
|
|
19
|
+
yaml_dep = Pod::YamlDependencies.new
|
|
20
|
+
if yaml_dep.disable_check_podspec
|
|
21
|
+
return
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
CocoaPods::Yaml.log.step("podspec文件检查中...")
|
|
25
|
+
start = (Time.now.to_f * 1000).to_i
|
|
26
|
+
module_count = 0
|
|
27
|
+
|
|
28
|
+
project.module_items.each do |module_name, module_item|
|
|
29
|
+
unless module_item.is_development_module
|
|
30
|
+
next
|
|
31
|
+
end
|
|
32
|
+
unless module_item.podspec_path
|
|
33
|
+
next
|
|
34
|
+
end
|
|
35
|
+
module_count += 1
|
|
36
|
+
|
|
37
|
+
module_item.init_absolute_import_info
|
|
38
|
+
|
|
39
|
+
# 代码里需要的模块
|
|
40
|
+
need_modules = get_code_need_modules(module_item)
|
|
41
|
+
|
|
42
|
+
# podspec直接依赖
|
|
43
|
+
podspec_modules = get_podspec_modules(module_item)
|
|
44
|
+
|
|
45
|
+
loss = loss_modules(need_modules, podspec_modules)
|
|
46
|
+
unused = unused_modules(need_modules, podspec_modules, module_item)
|
|
47
|
+
|
|
48
|
+
if loss.size >0
|
|
49
|
+
CocoaPods::Yaml.log.warn("组件#{module_item.module_name}可能缺少以下依赖(#{loss.size}个), 请检查:")
|
|
50
|
+
loss.sort.each do |name|
|
|
51
|
+
CocoaPods::Yaml.log.info(" s.dependency '#{name}'")
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
if unused.size >0
|
|
56
|
+
CocoaPods::Yaml.log.warn("组件#{module_item.module_name}可能未使用以下依赖(#{unused.size}个), 请检查:")
|
|
57
|
+
unused.sort.each { |name| CocoaPods::Yaml.log.info(" #{name}") }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
duration = ((Time.now.to_f * 1000).to_i - start)*0.001
|
|
63
|
+
CocoaPods::Yaml.log.detail("podspec检查完毕! 共检查#{module_count}个组件 耗时:#{duration.round(2)}秒")
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
# podspec.json中平台级配置使用的键,s.ios.dependency等会序列化到这些键下
|
|
69
|
+
PLATFORM_KEYS = %w[ios osx tvos watchos visionos].freeze
|
|
70
|
+
|
|
71
|
+
# 获取缺失的模块
|
|
72
|
+
# @param need_modules::[String]
|
|
73
|
+
# @param podspec_modules::[Hash{String => [String]}]
|
|
74
|
+
def loss_modules(need_modules, podspec_modules)
|
|
75
|
+
# 一次性展开所有podspec声明的framework/组件名
|
|
76
|
+
declared_modules = podspec_modules.values.flatten.uniq
|
|
77
|
+
|
|
78
|
+
need_modules.select do |name|
|
|
79
|
+
!declared_modules.include?(name) && module_is_installed(name)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# 获取多余的模块
|
|
84
|
+
# @param need_modules::[String]
|
|
85
|
+
# @param podspec_modules::[Hash{String => [String]}]
|
|
86
|
+
def unused_modules(need_modules, podspec_modules, module_item)
|
|
87
|
+
res = []
|
|
88
|
+
podspec_modules.each do |dep_name, array|
|
|
89
|
+
contain = false
|
|
90
|
+
array.each do |vendor|
|
|
91
|
+
if need_modules.include?(vendor)
|
|
92
|
+
contain = true
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
if not contain and module_item.module_name != dep_name
|
|
97
|
+
res << dep_name
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
res
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# @note 获取代码里需要的模块
|
|
104
|
+
def get_code_need_modules(module_item)
|
|
105
|
+
import_items = module_item.absolute_import_items
|
|
106
|
+
result_dic = {}
|
|
107
|
+
import_items.each do |import_item|
|
|
108
|
+
# 过滤系统库
|
|
109
|
+
if system_modules.include?(import_item.belong_module)
|
|
110
|
+
next
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# 过滤当前库
|
|
114
|
+
if module_item.module_name == import_item.belong_module
|
|
115
|
+
next
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
result_dic[import_item.belong_module] = import_item
|
|
119
|
+
end
|
|
120
|
+
result_dic.keys
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# 获取podspec文件里写的模块 直接依赖
|
|
124
|
+
# @return [Hash{String => [String]}]
|
|
125
|
+
def get_podspec_modules(module_item)
|
|
126
|
+
|
|
127
|
+
podspec_content = Util.get_podspec_content(module_item.podspec_path)
|
|
128
|
+
|
|
129
|
+
# 递归收集当前层、所有嵌套subspec、以及平台级(ios/osx等)声明的依赖
|
|
130
|
+
podspec_dependencies = []
|
|
131
|
+
collect_podspec_dependencies(podspec_content, podspec_dependencies)
|
|
132
|
+
|
|
133
|
+
# 转成map
|
|
134
|
+
result = {}
|
|
135
|
+
podspec_dependencies.each do |podspec_dependency|
|
|
136
|
+
podspec_dependency = podspec_dependency.split("/")[0] #如果podspec里写的依赖是带/的,取/前第一个, 例如:s.dependency 'UMCShare/Social/ReducedQQ'
|
|
137
|
+
dependency_item = project.module_items[podspec_dependency]
|
|
138
|
+
if dependency_item
|
|
139
|
+
result[podspec_dependency] = dependency_item.frameworks
|
|
140
|
+
else #如果/都处理了,还是取不到,那就整个默认值,自身
|
|
141
|
+
frameworks = [podspec_dependency]
|
|
142
|
+
if podspec_dependency.include?("/") #处理依赖 s.dependency 'UMCShare/Social/ReducedQQ'
|
|
143
|
+
frameworks << podspec_dependency.split("/")[0]
|
|
144
|
+
end
|
|
145
|
+
result[podspec_dependency] = frameworks
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
result
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# 递归收集podspec(含嵌套subspec、平台级配置)中声明的全部依赖名
|
|
153
|
+
# @param spec_content::[Hash] podspec.json解析出的当前层内容
|
|
154
|
+
# @param dependencies::[Array<String>] 累加结果
|
|
155
|
+
def collect_podspec_dependencies(spec_content, dependencies)
|
|
156
|
+
return unless spec_content.is_a?(Hash)
|
|
157
|
+
|
|
158
|
+
if spec_content["dependencies"].is_a?(Hash)
|
|
159
|
+
dependencies.concat(spec_content["dependencies"].keys)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# s.ios.dependency 'X' 等平台级依赖会序列化到 ios/osx/... 键下
|
|
163
|
+
PLATFORM_KEYS.each do |platform_key|
|
|
164
|
+
platform_content = spec_content[platform_key]
|
|
165
|
+
collect_podspec_dependencies(platform_content, dependencies) if platform_content.is_a?(Hash)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# 嵌套subspec递归收集
|
|
169
|
+
subspecs = spec_content["subspecs"]
|
|
170
|
+
if subspecs.is_a?(Array)
|
|
171
|
+
subspecs.each do |subspec|
|
|
172
|
+
collect_podspec_dependencies(subspec, dependencies)
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# 判断某个库是否被安装
|
|
178
|
+
def module_is_installed(module_name)
|
|
179
|
+
all = project.lock_content["DEPENDENCIES"]
|
|
180
|
+
unless all
|
|
181
|
+
return false
|
|
182
|
+
end
|
|
183
|
+
# 使用非破坏式map生成归一化数组,禁止collect!改写lock原始数据
|
|
184
|
+
normalized = all.map do |dep|
|
|
185
|
+
dep.split(" (")[0].split("/")[0]
|
|
186
|
+
end
|
|
187
|
+
normalized.include?(module_name)
|
|
188
|
+
end
|
|
189
|
+
# 系统级的依赖库
|
|
190
|
+
# @return [String]
|
|
191
|
+
def system_modules
|
|
192
|
+
MyConstants::LIBLIBARYS + MyConstants::FRAMEWORKS
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
require 'pathname'
|
|
2
|
+
require 'colored2'
|
|
3
|
+
require 'cocoapods/downloader/request'
|
|
4
|
+
require_relative 'xcodeproj'
|
|
5
|
+
require_relative 'util'
|
|
6
|
+
require_relative 'item'
|
|
7
|
+
|
|
8
|
+
module Xcodeproj
|
|
9
|
+
|
|
10
|
+
class XCodeProject
|
|
11
|
+
|
|
12
|
+
# 参与import/podspec分析的源码后缀
|
|
13
|
+
CODE_FILE_EXTENSIONS = %w[.h .m .mm .pch .c .cc .cpp .hpp].freeze
|
|
14
|
+
HEADER_FILE_EXTENSIONS = %w[.h .hpp].freeze
|
|
15
|
+
|
|
16
|
+
# 壳工程xcodeproj对象
|
|
17
|
+
# @return Xcodeproj::Project
|
|
18
|
+
attr_accessor :shell_project
|
|
19
|
+
|
|
20
|
+
# Pods工程xcodeproj对象
|
|
21
|
+
# @return Xcodeproj::Project
|
|
22
|
+
attr_accessor :pod_project
|
|
23
|
+
|
|
24
|
+
# @return [Hash{String => ModuleItem}]
|
|
25
|
+
attr_accessor :module_items
|
|
26
|
+
|
|
27
|
+
# @return [Hash]
|
|
28
|
+
attr_accessor :lock_content
|
|
29
|
+
|
|
30
|
+
# @return [Hash]
|
|
31
|
+
attr_accessor :version_map
|
|
32
|
+
|
|
33
|
+
def initialize
|
|
34
|
+
@shell_project = nil
|
|
35
|
+
@pod_project = nil
|
|
36
|
+
@module_items = {}
|
|
37
|
+
@version_map = {}
|
|
38
|
+
@lock_content = {}
|
|
39
|
+
init_shell_project
|
|
40
|
+
init_pod_project
|
|
41
|
+
init_lockfile
|
|
42
|
+
init_version_map
|
|
43
|
+
expand_module
|
|
44
|
+
init_vendor_frameworks
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def init_shell_project
|
|
48
|
+
# 统一走XcodeprojManager的壳工程查找逻辑,避免多份实现选取结果不一致
|
|
49
|
+
@shell_project = XCProject::XcodeprojManager.share_manager.get_shell_project
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def init_pod_project
|
|
53
|
+
xcodeproj_path = "#{Dir.pwd}/Pods/Pods.xcodeproj"
|
|
54
|
+
unless File.exist?(xcodeproj_path)
|
|
55
|
+
return nil
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
@pod_project = Xcodeproj::Project.open(xcodeproj_path)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def init_lockfile
|
|
62
|
+
lockfile_path = MyConstants::PODFILE_LOCK
|
|
63
|
+
|
|
64
|
+
unless File.exist?(lockfile_path)
|
|
65
|
+
return
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
@lock_content = YAML.load_file(lockfile_path)
|
|
69
|
+
end
|
|
70
|
+
def init_version_map
|
|
71
|
+
lock_content = @lock_content
|
|
72
|
+
|
|
73
|
+
remote_dependencies = lock_content["PODS"]
|
|
74
|
+
unless remote_dependencies
|
|
75
|
+
return
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
remote_dependencies.each do |dependency|
|
|
79
|
+
if dependency.class == Hash
|
|
80
|
+
dependency = dependency.keys.first
|
|
81
|
+
end
|
|
82
|
+
# 外部源条目形如 "Xxx (from `path`)",没有版本号;
|
|
83
|
+
# 不跳过会把 "from `path`" 当版本存入,污染get_podspec_path的缓存路径推断
|
|
84
|
+
next if dependency.include?("(from")
|
|
85
|
+
|
|
86
|
+
temp = dependency.split("(")
|
|
87
|
+
# 无括号段(理论上不出现)时temp[1]为nil,直接跳过避免NoMethodError
|
|
88
|
+
version = temp[1]
|
|
89
|
+
next if version.nil?
|
|
90
|
+
|
|
91
|
+
name = temp[0].split("/")[0] # UMCShare/Social/ReducedWeChat
|
|
92
|
+
name = name.gsub(" ", "").gsub(")", "")
|
|
93
|
+
version = version.gsub("= ", "").gsub(")", "")
|
|
94
|
+
next if version.empty?
|
|
95
|
+
|
|
96
|
+
@version_map[name] = version
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# 展开模块
|
|
101
|
+
def expand_module
|
|
102
|
+
self.expand_with_module(pod_project.main_group, nil)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# 展开模块
|
|
106
|
+
def expand_with_module(group, module_name)
|
|
107
|
+
|
|
108
|
+
if group.class != Xcodeproj::Project::Object::PBXGroup
|
|
109
|
+
return
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
if not module_name and group.hierarchy_path
|
|
113
|
+
group_hierarchy_path_array = group.hierarchy_path.split("/")
|
|
114
|
+
module_name = group_hierarchy_path_array.size >= 3 ? group_hierarchy_path_array[2] : nil
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# 创建 source_file_item
|
|
118
|
+
source_file_item = @module_items[module_name]
|
|
119
|
+
if module_name and not source_file_item
|
|
120
|
+
source_file_item = ModuleItem.new(module_name)
|
|
121
|
+
source_file_item.podspec_path = get_podspec_path(module_name)
|
|
122
|
+
@module_items[module_name] = source_file_item
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# 更新module_path
|
|
126
|
+
if source_file_item and not source_file_item.module_path and group.hierarchy_path
|
|
127
|
+
group_hierarchy_path_array = group.hierarchy_path.split("/")
|
|
128
|
+
if group_hierarchy_path_array.size == 3
|
|
129
|
+
source_file_item.module_path = group.real_path
|
|
130
|
+
end
|
|
131
|
+
if group_hierarchy_path_array.include?("Development Pods")
|
|
132
|
+
source_file_item.is_development_module = true
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# 文件引用
|
|
137
|
+
file_reference_class = Xcodeproj::Project::Object::PBXFileReference
|
|
138
|
+
# PBXFileSystemSynchronizedRootGroup在xcodeproj 1.25(Xcode16)才引入,旧版本取nil走普通分组逻辑
|
|
139
|
+
synchronized_group_class =
|
|
140
|
+
if Xcodeproj::Project::Object.const_defined?(:PBXFileSystemSynchronizedRootGroup)
|
|
141
|
+
Xcodeproj::Project::Object.const_get(:PBXFileSystemSynchronizedRootGroup)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
not_child_names = ["Podfile","Frameworks", "Products","Targets Support Files","Support Files"]
|
|
145
|
+
source_file_extension = CODE_FILE_EXTENSIONS
|
|
146
|
+
header_file_extension = HEADER_FILE_EXTENSIONS
|
|
147
|
+
|
|
148
|
+
group.children.each {|child|
|
|
149
|
+
|
|
150
|
+
module_name = source_file_item ? source_file_item.module_name : nil
|
|
151
|
+
|
|
152
|
+
# Xcode16+的文件系统同步分组不是PBXGroup子类(PBXFileSystemSynchronizedRootGroup
|
|
153
|
+
# 直接继承AbstractObject),既没有name也没有real_path,走普通递归会在方法入口被整体跳过,
|
|
154
|
+
# 导致该组件不进module_items,podspec检查和import检查会静默失效。
|
|
155
|
+
# 该判断必须在child.name过滤之前(同步分组不响应name方法)。这里改为按磁盘目录扫描展开。
|
|
156
|
+
if synchronized_group_class && child.is_a?(synchronized_group_class)
|
|
157
|
+
expand_synchronized_group(group, child, module_name)
|
|
158
|
+
next
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
if not_child_names.include?(child.name)
|
|
162
|
+
# puts "不检查:#{child.name} #{child.real_path}"
|
|
163
|
+
next
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
if child.class != file_reference_class
|
|
167
|
+
# puts "不是文件引用:#{child.class} #{child.real_path}"
|
|
168
|
+
self.expand_with_module(child, module_name)
|
|
169
|
+
next
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
hierarchy_path_array = child.hierarchy_path.split("/")
|
|
173
|
+
if hierarchy_path_array.size < 3
|
|
174
|
+
self.expand_with_module(child, module_name)
|
|
175
|
+
next
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
if source_file_item
|
|
179
|
+
extend_name = child.real_path.basename.extname
|
|
180
|
+
basename = child.real_path.basename
|
|
181
|
+
if basename.to_s == "#{module_name}.podspec" or basename.to_s == "#{module_name}.podspec.json"
|
|
182
|
+
source_file_item.podspec_path = child.real_path
|
|
183
|
+
elsif source_file_extension.include?(extend_name)
|
|
184
|
+
source_file_item.source_files[child.real_path.basename.to_s] = child.real_path
|
|
185
|
+
if header_file_extension.include?(extend_name)
|
|
186
|
+
source_file_item.header_files[child.real_path.basename.to_s] = child.real_path
|
|
187
|
+
end
|
|
188
|
+
else
|
|
189
|
+
# puts child.real_path
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# 递归展开
|
|
194
|
+
self.expand_with_module(child, module_name)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# 展开Xcode16+文件系统同步分组(PBXFileSystemSynchronizedRootGroup)
|
|
200
|
+
# 同步分组不在工程文件里列举成员,磁盘上有什么文件Xcode就编译什么,
|
|
201
|
+
# 因此这里直接扫描磁盘目录收集源码与podspec,语义与普通分组遍历保持一致。
|
|
202
|
+
# 注意: 同步分组没有hierarchy_path/real_path/name,层级信息需从父分组拼接
|
|
203
|
+
# @param parent_group [PBXGroup] 承载该同步分组的普通分组(用于解析相对路径)
|
|
204
|
+
# @param sync_group [PBXFileSystemSynchronizedRootGroup]
|
|
205
|
+
# @param module_name [String, nil] 父分组已解析出的组件名
|
|
206
|
+
def expand_synchronized_group(parent_group, sync_group, module_name)
|
|
207
|
+
# 与普通分组一致,按层级路径解析组件名
|
|
208
|
+
hierarchy_path = build_synchronized_hierarchy_path(parent_group, sync_group)
|
|
209
|
+
if hierarchy_path
|
|
210
|
+
hierarchy_path_array = hierarchy_path.split("/")
|
|
211
|
+
module_name = hierarchy_path_array[2] if hierarchy_path_array.size >= 3
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
source_file_item = @module_items[module_name]
|
|
215
|
+
if module_name && source_file_item.nil?
|
|
216
|
+
source_file_item = ModuleItem.new(module_name)
|
|
217
|
+
source_file_item.podspec_path = get_podspec_path(module_name)
|
|
218
|
+
@module_items[module_name] = source_file_item
|
|
219
|
+
end
|
|
220
|
+
return unless source_file_item
|
|
221
|
+
|
|
222
|
+
disk_path = synchronized_disk_path(parent_group, sync_group)
|
|
223
|
+
return unless disk_path && File.directory?(disk_path)
|
|
224
|
+
|
|
225
|
+
# 与普通分组逻辑一致: 层级深度为3的分组对应组件根目录
|
|
226
|
+
if source_file_item.module_path.nil? && hierarchy_path && hierarchy_path.split("/").size == 3
|
|
227
|
+
source_file_item.module_path = Pathname.new(disk_path)
|
|
228
|
+
end
|
|
229
|
+
if hierarchy_path&.split("/")&.include?("Development Pods")
|
|
230
|
+
source_file_item.is_development_module = true
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
scan_disk_module_files(source_file_item, disk_path, module_name)
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# 拼接同步分组的层级路径(其自身不响应hierarchy_path)
|
|
237
|
+
# @return [String, nil] 形如 "/Development Pods/BSLogin"
|
|
238
|
+
def build_synchronized_hierarchy_path(parent_group, sync_group)
|
|
239
|
+
parent_path = parent_group.respond_to?(:hierarchy_path) ? parent_group.hierarchy_path : nil
|
|
240
|
+
return nil if parent_path.nil?
|
|
241
|
+
|
|
242
|
+
"#{parent_path}/#{sync_group.display_name}"
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# 递归收集磁盘目录下的源码文件和podspec(对应Xcode16+同步文件夹分组)
|
|
246
|
+
# @param module_item [ModuleItem]
|
|
247
|
+
# @param disk_path [String, Pathname] 同步分组在磁盘上的根目录
|
|
248
|
+
# @param module_name [String]
|
|
249
|
+
def scan_disk_module_files(module_item, disk_path, module_name)
|
|
250
|
+
Dir.glob(File.join(disk_path.to_s, '**', '*')).each do |path|
|
|
251
|
+
next unless File.file?(path)
|
|
252
|
+
|
|
253
|
+
basename = File.basename(path)
|
|
254
|
+
extend_name = File.extname(path)
|
|
255
|
+
pathname = Pathname.new(path)
|
|
256
|
+
if basename == "#{module_name}.podspec" || basename == "#{module_name}.podspec.json"
|
|
257
|
+
module_item.podspec_path = pathname
|
|
258
|
+
elsif CODE_FILE_EXTENSIONS.include?(extend_name)
|
|
259
|
+
module_item.source_files[basename] = pathname
|
|
260
|
+
if HEADER_FILE_EXTENSIONS.include?(extend_name)
|
|
261
|
+
module_item.header_files[basename] = pathname
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
# 计算同步分组在磁盘上的绝对路径
|
|
268
|
+
# @param parent_group [PBXGroup] 承载该同步分组的普通分组(有real_path)
|
|
269
|
+
# @param sync_group [PBXFileSystemSynchronizedRootGroup] 只有相对path,没有real_path
|
|
270
|
+
def synchronized_disk_path(parent_group, sync_group)
|
|
271
|
+
relative = sync_group.path
|
|
272
|
+
return nil if relative.nil? || relative.empty?
|
|
273
|
+
|
|
274
|
+
path = Pathname.new(relative)
|
|
275
|
+
return path if path.absolute?
|
|
276
|
+
|
|
277
|
+
case sync_group.source_tree
|
|
278
|
+
when 'SOURCE_ROOT'
|
|
279
|
+
# 相对Pods工程源根目录,不需要父分组回退
|
|
280
|
+
base = pod_project.root_object.main_group.real_path
|
|
281
|
+
return path unless base
|
|
282
|
+
|
|
283
|
+
base.join(relative)
|
|
284
|
+
else # '<group>'(默认): 相对所在父分组
|
|
285
|
+
base = parent_group.real_path
|
|
286
|
+
candidate = base ? base.join(relative) : path
|
|
287
|
+
# 部分工程父分组未配置显式path,real_path只解析到工程根目录,
|
|
288
|
+
# 此时按分组名再补一层做回退(Development Pods等分组名通常与磁盘目录同名)
|
|
289
|
+
if base && !candidate.directory? && parent_group.respond_to?(:display_name)
|
|
290
|
+
fallback = base.join(parent_group.display_name.to_s, relative)
|
|
291
|
+
candidate = fallback if fallback.directory?
|
|
292
|
+
end
|
|
293
|
+
candidate
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
# 读取某个模块的podspec路径
|
|
298
|
+
def get_podspec_path(module_name)
|
|
299
|
+
check_options = lock_content["CHECKOUT OPTIONS"]
|
|
300
|
+
if check_options and check_options[module_name]
|
|
301
|
+
check_option = check_options[module_name]
|
|
302
|
+
request = Pod::Downloader::Request.new(released:false, name:module_name, params:check_option)
|
|
303
|
+
slug_opts = {}
|
|
304
|
+
cache_path = request.slug(**slug_opts)
|
|
305
|
+
return "~/Library/Caches/CocoaPods/Pods/Specs/#{cache_path}.podspec.json"
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
# :path: localPods/XX
|
|
309
|
+
local_spec = lock_content["EXTERNAL SOURCES"]
|
|
310
|
+
if local_spec and local_spec[module_name]
|
|
311
|
+
path = local_spec[module_name][:path]
|
|
312
|
+
podspec_path = "#{path}/#{module_name}.podspec"
|
|
313
|
+
podspec_json_path = "#{path}/#{module_name}.podspec.json"
|
|
314
|
+
if File.exist?(podspec_path)
|
|
315
|
+
return Pathname(podspec_path).expand_path
|
|
316
|
+
elsif File.exist?(podspec_json_path)
|
|
317
|
+
return Pathname(podspec_json_path).expand_path
|
|
318
|
+
else
|
|
319
|
+
return nil
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# 4.0.1-24ee2.podspec.json
|
|
324
|
+
version_spec = @lock_content["SPEC CHECKSUMS"]
|
|
325
|
+
md5_to5 = ""
|
|
326
|
+
if version_spec and version_spec[module_name]
|
|
327
|
+
md5 = version_spec[module_name]
|
|
328
|
+
md5_to5 = md5.slice(0,5)
|
|
329
|
+
end
|
|
330
|
+
version = @version_map[module_name]
|
|
331
|
+
if version
|
|
332
|
+
podspec_json_name = "#{version}-#{md5_to5}.podspec.json"
|
|
333
|
+
path = "~/Library/Caches/CocoaPods/Pods/Specs/Release/#{module_name}/#{podspec_json_name}"
|
|
334
|
+
return Pathname(path).expand_path
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
nil
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
# 初始化模块的vendor_frameworks
|
|
342
|
+
def init_vendor_frameworks
|
|
343
|
+
@module_items.each_value do |module_item|
|
|
344
|
+
module_item.frameworks << module_item.module_name
|
|
345
|
+
|
|
346
|
+
unless module_item.podspec_path
|
|
347
|
+
CocoaPods::Yaml.log.warn("读取#{module_item.module_name}的podspec路径为空,请发送Podfile.lock文件联系开发者修复")
|
|
348
|
+
next
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
podspec_content = Util.get_podspec_content(module_item.podspec_path)
|
|
352
|
+
|
|
353
|
+
unless podspec_content
|
|
354
|
+
next
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
update_dep_frameworks(module_item.frameworks, podspec_content)
|
|
358
|
+
|
|
359
|
+
subspec_content = podspec_content["subspecs"]
|
|
360
|
+
if subspec_content and subspec_content.class == Array
|
|
361
|
+
subspec_content.each { |json_content|
|
|
362
|
+
update_dep_frameworks(module_item.frameworks, json_content)
|
|
363
|
+
}
|
|
364
|
+
end
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
def update_dep_frameworks(item_frameworks, json_content)
|
|
370
|
+
vendor_frameworks = json_content["vendored_frameworks"]
|
|
371
|
+
unless vendor_frameworks
|
|
372
|
+
ios = json_content["ios"]
|
|
373
|
+
if ios and ios.class == Hash
|
|
374
|
+
vendor_frameworks = ios["vendored_frameworks"]
|
|
375
|
+
end
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
if vendor_frameworks
|
|
379
|
+
if vendor_frameworks.class == Array
|
|
380
|
+
vendor_frameworks.each do |vendor_framework|
|
|
381
|
+
frameworks = deal_frameworks(vendor_framework)
|
|
382
|
+
if frameworks != "*"
|
|
383
|
+
item_frameworks << frameworks
|
|
384
|
+
end
|
|
385
|
+
end
|
|
386
|
+
elsif vendor_frameworks.class == String
|
|
387
|
+
frameworks = deal_frameworks(vendor_frameworks)
|
|
388
|
+
if frameworks != "*"
|
|
389
|
+
item_frameworks << frameworks
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
def deal_frameworks(frameworks)
|
|
396
|
+
# "frameworks/WCDB/WCDB.framework",
|
|
397
|
+
# "openssl.xcframework"
|
|
398
|
+
temp_array = frameworks.split("/")
|
|
399
|
+
result = temp_array.last
|
|
400
|
+
result = result.gsub(".xcframework","")
|
|
401
|
+
result.gsub(".framework", "")
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
end
|
|
405
|
+
end
|