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,171 @@
|
|
|
1
|
+
require 'cocoapods/installer'
|
|
2
|
+
require 'yaml'
|
|
3
|
+
require_relative 'shell'
|
|
4
|
+
require_relative 'local'
|
|
5
|
+
require_relative 'pod_target'
|
|
6
|
+
|
|
7
|
+
module Pod
|
|
8
|
+
# 裁剪开关的下载流程补丁。必须用prepend而不是重开class重定义:
|
|
9
|
+
# 重开class定义同名方法是覆盖,super找不到原方法;prepend后super即CocoaPods原始实现
|
|
10
|
+
module InstallerTrim
|
|
11
|
+
def download_dependencies
|
|
12
|
+
if Pod::YamlDependencies.new.trim_unused_pod?
|
|
13
|
+
UI.section '下载依赖...' do
|
|
14
|
+
|
|
15
|
+
thin_pod_unused #自定义
|
|
16
|
+
thin_delete_pods_unused #自定义
|
|
17
|
+
|
|
18
|
+
install_pod_sources
|
|
19
|
+
run_podfile_pre_install_hooks
|
|
20
|
+
clean_pod_sources
|
|
21
|
+
end
|
|
22
|
+
else
|
|
23
|
+
# 未开启裁剪时走CocoaPods原始下载流程(pod install/update --full或默认)
|
|
24
|
+
super
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class Installer
|
|
30
|
+
|
|
31
|
+
# @return [Hash{String, Pod:Target}] 所有的pod库被使用的次数
|
|
32
|
+
attr_accessor :thin_pods
|
|
33
|
+
|
|
34
|
+
# @return [String] 未被使用的pod库列表
|
|
35
|
+
attr_accessor :thin_unused_pods
|
|
36
|
+
|
|
37
|
+
prepend InstallerTrim
|
|
38
|
+
|
|
39
|
+
# 删除未被使用的依赖
|
|
40
|
+
# @return nil
|
|
41
|
+
def thin_pod_unused
|
|
42
|
+
@thin_pods = Hash.new
|
|
43
|
+
thin_all_deps_pods_targets
|
|
44
|
+
thin_all_deps_shell_targets
|
|
45
|
+
thin_all_deps_local_yaml
|
|
46
|
+
thin_unused_deps
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# 获取未被使用到的依赖库
|
|
50
|
+
# @return thin_unused_pods
|
|
51
|
+
def thin_unused_deps
|
|
52
|
+
thin_unused_pods = []
|
|
53
|
+
thin_pods.each do |name, pod_target|
|
|
54
|
+
pod_target.reduce_reference_count
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
thin_pods.each do |name, pod_target|
|
|
58
|
+
if pod_target.reference_count <= 0
|
|
59
|
+
thin_unused_pods << name
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
@thin_unused_pods = thin_unused_pods
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# 获取在pods_targets使用的依赖库
|
|
67
|
+
# @return thin_pods
|
|
68
|
+
def thin_all_deps_pods_targets
|
|
69
|
+
|
|
70
|
+
pod_targets = analysis_result.pod_targets
|
|
71
|
+
|
|
72
|
+
pod_targets.each do |pod_target|
|
|
73
|
+
thin_pods[pod_target.to_s] = pod_target
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
pod_targets.each do |pod_target|
|
|
77
|
+
pod_target.dependent_targets.each do|sub_pod_target|
|
|
78
|
+
thin_update_reference_count(sub_pod_target)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
thin_pods
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# 递归更新引用计数
|
|
86
|
+
# @return nil
|
|
87
|
+
def thin_update_reference_count(pod_target)
|
|
88
|
+
pod = thin_pods[pod_target.to_s]
|
|
89
|
+
pod.reference_count += 1
|
|
90
|
+
pod.dependent_targets.each do|sub_pod_target|
|
|
91
|
+
thin_update_reference_count(sub_pod_target)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# 获取在壳工程里使用的依赖库
|
|
96
|
+
# @return thin_pods
|
|
97
|
+
def thin_all_deps_shell_targets
|
|
98
|
+
shell = Shell.new
|
|
99
|
+
|
|
100
|
+
shell.deps_shell.each { |shell_pod|
|
|
101
|
+
if thin_pods.has_key?(shell_pod)
|
|
102
|
+
thin_pods[shell_pod].max_reference_count
|
|
103
|
+
end
|
|
104
|
+
}
|
|
105
|
+
thin_pods
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# 获取在local_yaml里使用的依赖库
|
|
109
|
+
# @return thin_pods
|
|
110
|
+
def thin_all_deps_local_yaml
|
|
111
|
+
local = Local.new
|
|
112
|
+
local.deps_yaml.each do |yaml_pod|
|
|
113
|
+
if thin_pods.has_key?(yaml_pod)
|
|
114
|
+
thin_pods[yaml_pod].max_reference_count
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
thin_pods
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# 删除某些依赖库
|
|
122
|
+
# @return nil
|
|
123
|
+
def thin_delete_pods_unused
|
|
124
|
+
if thin_unused_pods.empty?
|
|
125
|
+
return
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
thin_unused_pods.each do |unused_pod|
|
|
129
|
+
title_options = { :verbose_prefix => '-> '.red }
|
|
130
|
+
UI.titled_section("忽略安装 #{unused_pod}".red, title_options) do
|
|
131
|
+
thin_delete_pod(unused_pod)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# 删除某个依赖库
|
|
138
|
+
# @param unused_pod: String 依赖库名字
|
|
139
|
+
# @return nil
|
|
140
|
+
def thin_delete_pod(unused_pod)
|
|
141
|
+
sandbox_state.added.delete(unused_pod)
|
|
142
|
+
sandbox_state.changed.delete(unused_pod)
|
|
143
|
+
sandbox_state.unchanged.delete(unused_pod)
|
|
144
|
+
|
|
145
|
+
pod_targets.delete_if { |pod_target|
|
|
146
|
+
pod_target.to_s == unused_pod
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
aggregate_targets.each { |aggregate_target|
|
|
150
|
+
aggregate_target.pod_targets.delete_if { |pod_target|
|
|
151
|
+
pod_target.to_s == unused_pod
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
aggregate_targets.each do |aggregate_target|
|
|
156
|
+
aggregate_target.user_build_configurations.each_key do |config|
|
|
157
|
+
pod_targets = aggregate_target.pod_targets_for_build_configuration(config)
|
|
158
|
+
pod_targets.delete_if { |pod_target|
|
|
159
|
+
pod_target.to_s == unused_pod
|
|
160
|
+
}
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
analysis_result.specifications.delete_if { |specification|
|
|
165
|
+
real_specification = specification.to_s.split(" (")[0]
|
|
166
|
+
real_pod = real_specification.split("/")[0]
|
|
167
|
+
real_pod == unused_pod
|
|
168
|
+
}
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require 'pathname'
|
|
2
|
+
require_relative '../yaml/yaml_dependencies'
|
|
3
|
+
|
|
4
|
+
module Pod
|
|
5
|
+
class Local
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
super
|
|
9
|
+
@reference_pods = []
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# LocalDependencies.yaml指定的本地依赖库
|
|
13
|
+
# @return [String]
|
|
14
|
+
attr_accessor :reference_pods
|
|
15
|
+
|
|
16
|
+
def deps_yaml
|
|
17
|
+
|
|
18
|
+
yaml_dep = YamlDependencies.new
|
|
19
|
+
|
|
20
|
+
yaml_dep.generate_yaml
|
|
21
|
+
|
|
22
|
+
local_yaml = yaml_dep.local_yaml_name
|
|
23
|
+
yaml_dep.read_yaml_deps_file(local_yaml)
|
|
24
|
+
|
|
25
|
+
yaml_dependencies = yaml_dep.yaml_deps[local_yaml]
|
|
26
|
+
|
|
27
|
+
if yaml_dependencies
|
|
28
|
+
# 处理烦人的子模块
|
|
29
|
+
yaml_dependencies.keys.each do |pod|
|
|
30
|
+
real_pod = pod.split("/")[0]
|
|
31
|
+
unless reference_pods.include?(real_pod)
|
|
32
|
+
reference_pods << real_pod
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
reference_pods
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module Pod
|
|
2
|
+
class PodTarget < Target
|
|
3
|
+
|
|
4
|
+
# 被引用的次数
|
|
5
|
+
# @return Int
|
|
6
|
+
attr_accessor :reference_count
|
|
7
|
+
|
|
8
|
+
# 引用计数最大
|
|
9
|
+
def max_reference_count
|
|
10
|
+
self.reference_count = 99999
|
|
11
|
+
self.dependent_targets.each do |dep|
|
|
12
|
+
dep.max_reference_count
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# 引用计数减1
|
|
17
|
+
def reduce_reference_count
|
|
18
|
+
self.reference_count -= 1
|
|
19
|
+
|
|
20
|
+
self.dependent_targets.each do |dep|
|
|
21
|
+
dep.reduce_reference_count
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# 获取引用计数
|
|
26
|
+
# @return Int
|
|
27
|
+
def reference_count
|
|
28
|
+
@reference_count ? @reference_count : 0
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
require 'xcodeproj'
|
|
2
|
+
require 'pathname'
|
|
3
|
+
require_relative '../check/xcodeproj'
|
|
4
|
+
|
|
5
|
+
module Pod
|
|
6
|
+
class Shell
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
super
|
|
10
|
+
@reference_files = []
|
|
11
|
+
@reference_pods = []
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# 壳工程project对象
|
|
15
|
+
# @return Xcodeproj::Project
|
|
16
|
+
attr_accessor :project_shell
|
|
17
|
+
|
|
18
|
+
# 壳工程引用的文件列表
|
|
19
|
+
# @return [Pathname]
|
|
20
|
+
attr_accessor :reference_files
|
|
21
|
+
|
|
22
|
+
# 壳工程引用的依赖库
|
|
23
|
+
# @return [String]
|
|
24
|
+
attr_accessor :reference_pods
|
|
25
|
+
|
|
26
|
+
# 获取壳工程所引用的依赖库
|
|
27
|
+
# @return [String]
|
|
28
|
+
def deps_shell
|
|
29
|
+
init_project_shell
|
|
30
|
+
|
|
31
|
+
reference_code_file(project_shell.root_object.main_group)
|
|
32
|
+
|
|
33
|
+
reference_files.each do |reference_file|
|
|
34
|
+
pod_in_file(reference_file)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
reference_pods
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# 初始化project对象
|
|
41
|
+
# @return project_shell
|
|
42
|
+
def init_project_shell
|
|
43
|
+
# 统一走XcodeprojManager的壳工程查找逻辑,避免多份实现选取结果不一致
|
|
44
|
+
@project_shell = XCProject::XcodeprojManager.share_manager.get_shell_project
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# 参与import分析的源码后缀(.swift参与后Swift的import Module才能进引用图,
|
|
48
|
+
# 否则开启裁剪时Swift依赖会被误删)
|
|
49
|
+
CODE_FILE_EXTS = %w[.h .m .mm .pch .c .cc .hpp .cpp .swift].freeze
|
|
50
|
+
|
|
51
|
+
# 获取其对应的引用代码文件和路径
|
|
52
|
+
# @return reference_files
|
|
53
|
+
def reference_code_file(group)
|
|
54
|
+
# 文件引用
|
|
55
|
+
file_reference = Xcodeproj::Project::Object::PBXFileReference
|
|
56
|
+
# PBXFileSystemSynchronizedRootGroup在xcodeproj 1.25(Xcode16)才引入,
|
|
57
|
+
# gemspec允许xcodeproj>=1.23,旧版本必须走const_defined?兜底,避免直接NameError
|
|
58
|
+
synchronized_group_class =
|
|
59
|
+
if Xcodeproj::Project::Object.const_defined?(:PBXFileSystemSynchronizedRootGroup)
|
|
60
|
+
Xcodeproj::Project::Object::PBXFileSystemSynchronizedRootGroup
|
|
61
|
+
end
|
|
62
|
+
group.children.each {|child|
|
|
63
|
+
|
|
64
|
+
# Xcode16+的文件系统同步分组: 它在父group的children里,但自身没有parent/real_path,
|
|
65
|
+
# 用所在父group的磁盘路径拼接其相对path后直接扫描磁盘目录
|
|
66
|
+
if synchronized_group_class && child.is_a?(synchronized_group_class)
|
|
67
|
+
scan_disk_code_files(synchronized_disk_path(group, child))
|
|
68
|
+
next
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
if child.class != file_reference
|
|
72
|
+
self.reference_code_file(child)
|
|
73
|
+
next
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
file_name = child.real_path.basename
|
|
77
|
+
extend_name = file_name.extname
|
|
78
|
+
next unless CODE_FILE_EXTS.include?(extend_name)
|
|
79
|
+
|
|
80
|
+
# 引用关系路径
|
|
81
|
+
hierarchy_path_array = child.hierarchy_path.split("/")
|
|
82
|
+
if hierarchy_path_array.size < 2
|
|
83
|
+
next
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
reference_files << child.real_path
|
|
87
|
+
}
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# 递归收集磁盘目录下的源码文件(对应Xcode16+同步文件夹分组)
|
|
91
|
+
def scan_disk_code_files(dir_path)
|
|
92
|
+
return unless dir_path && File.directory?(dir_path)
|
|
93
|
+
Dir.glob(File.join(dir_path, '**', '*')).each do |path|
|
|
94
|
+
next unless File.file?(path)
|
|
95
|
+
next unless CODE_FILE_EXTS.include?(File.extname(path))
|
|
96
|
+
reference_files << Pathname.new(path)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# 计算同步分组在磁盘上的绝对路径
|
|
101
|
+
# @param parent_group 承载该同步分组的普通PBXGroup(有real_path)
|
|
102
|
+
# @param sync_group PBXFileSystemSynchronizedRootGroup(只有相对path)
|
|
103
|
+
def synchronized_disk_path(parent_group, sync_group)
|
|
104
|
+
relative = sync_group.path
|
|
105
|
+
return nil if relative.nil? || relative.empty?
|
|
106
|
+
path = Pathname.new(relative)
|
|
107
|
+
return path if path.absolute?
|
|
108
|
+
|
|
109
|
+
case sync_group.source_tree
|
|
110
|
+
when 'SOURCE_ROOT'
|
|
111
|
+
base = sync_group.project.root_object.main_group.real_path
|
|
112
|
+
else # '<group>'(默认): 相对所在父分组
|
|
113
|
+
base = parent_group.real_path
|
|
114
|
+
end
|
|
115
|
+
base ? base.join(relative) : path
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# 获取某个文件里引用的依赖库名字
|
|
119
|
+
# @param file_path 文件路径
|
|
120
|
+
# @return reference_pods
|
|
121
|
+
def pod_in_file(file_path)
|
|
122
|
+
if File.directory?(file_path)
|
|
123
|
+
return reference_pods
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Swift与OC的import语法完全不同,按文件后缀分流解析
|
|
127
|
+
if File.extname(file_path) == '.swift'
|
|
128
|
+
parse_swift_imports(file_path)
|
|
129
|
+
else
|
|
130
|
+
parse_oc_imports(file_path)
|
|
131
|
+
end
|
|
132
|
+
reference_pods
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
private
|
|
136
|
+
|
|
137
|
+
# 解析OC源码的依赖引用: 兼容 #import <A/B.h> 与 #import<A/B.h>(无空格)两种写法,
|
|
138
|
+
# 无空格写法漏识别会导致组件不进引用图,开启裁剪时被误删
|
|
139
|
+
def parse_oc_imports(file_path)
|
|
140
|
+
safe_readlines(file_path).each { |line|
|
|
141
|
+
match = line.match(/\A#\s*import\s*<([^>]+)>/)
|
|
142
|
+
next unless match
|
|
143
|
+
|
|
144
|
+
import_path = match[1]
|
|
145
|
+
next unless import_path.include?("/")
|
|
146
|
+
|
|
147
|
+
# 取第一段作为组件名,处理 #import <A/B/C.h> 多级子模块的情况
|
|
148
|
+
add_reference_pod(import_path.split("/").first)
|
|
149
|
+
}
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# 解析Swift源码的依赖引用: import Module / @testable import Module /
|
|
153
|
+
# import class Module.Sub(子符号取第一段)等
|
|
154
|
+
def parse_swift_imports(file_path)
|
|
155
|
+
safe_readlines(file_path).each { |line|
|
|
156
|
+
match = line.match(/\A(?:@\w+\s+)?import\s+(?:(?:class|struct|enum|protocol|func|var|let|typealias)\s+)?([A-Za-z_]\w*)/)
|
|
157
|
+
next unless match
|
|
158
|
+
|
|
159
|
+
add_reference_pod(match[1])
|
|
160
|
+
}
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def add_reference_pod(module_name)
|
|
164
|
+
unless reference_pods.include?(module_name)
|
|
165
|
+
reference_pods << module_name
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# 容错读取源码行:壳工程历史源码可能存在GBK等非UTF-8文件。
|
|
170
|
+
# 直接IO.readlines按默认编码读,后续start_with?/gsub遇到非法字节会抛
|
|
171
|
+
# invalid byte sequence中断整个裁剪流程;先按二进制读取再显式做一次
|
|
172
|
+
# 同编码转换替换非法字节
|
|
173
|
+
def safe_readlines(file_path)
|
|
174
|
+
content = File.read(file_path, mode: 'rb')
|
|
175
|
+
content.encode!('UTF-8', 'UTF-8', invalid: :replace, undef: :replace, replace: '?')
|
|
176
|
+
content.lines
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
end
|
|
180
|
+
end
|