podfileDep 1.1.4 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -1
- data/lib/cocoapods_plugin.rb +40 -0
- data/lib/old/podfileDep.rb +1408 -0
- data/lib/podfileDep/check/dep.rb +183 -0
- data/lib/podfileDep/check/import.rb +159 -0
- data/lib/podfileDep/check/item.rb +11 -0
- data/lib/podfileDep/check/module.rb +178 -0
- data/lib/podfileDep/check/xcodeproj.rb +310 -0
- data/lib/podfileDep/constants.rb +13 -0
- data/lib/podfileDep/modify/modify_code.rb +52 -0
- data/lib/podfileDep/podfilelock.rb +17 -0
- data/lib/podfileDep/reference/project.rb +72 -0
- data/lib/podfileDep/reference/unused.rb +141 -0
- data/lib/podfileDep/version.rb +1 -1
- data/lib/podfileDep/yaml/yaml_dep.rb +449 -0
- data/lib/podfileDep.rb +13 -816
- data/podfileDep.gemspec +3 -2
- metadata +45 -6
- data/Gemfile.lock +0 -19
- data/sig/podfileDep.rbs +0 -4
@@ -0,0 +1,183 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'yaml'
|
3
|
+
require_relative '../podfilelock'
|
4
|
+
require_relative '../constants'
|
5
|
+
require_relative 'module'
|
6
|
+
require_relative 'item'
|
7
|
+
require_relative 'xcodeproj'
|
8
|
+
|
9
|
+
module Dep
|
10
|
+
class DepChecker
|
11
|
+
|
12
|
+
# 检查缺少的或着多的依赖
|
13
|
+
def self.dep_check
|
14
|
+
puts "\n依赖库关系检查中..."
|
15
|
+
start = (Time.now.to_f * 1000).to_i
|
16
|
+
|
17
|
+
podfile_lock_manager = PodfileLock::PodfileLockManager
|
18
|
+
module_manager = ModuleModule::ModuleManager.share_instance
|
19
|
+
project_manager = XCProject::XcodeprojManager.share_manager
|
20
|
+
|
21
|
+
# podfile.lock的内容
|
22
|
+
lock_content = podfile_lock_manager.podfile_lock_content
|
23
|
+
|
24
|
+
# 获取需要检查的模块名和路径
|
25
|
+
modules = project_manager.get_development_module
|
26
|
+
|
27
|
+
# 获取所有已安装的依赖库
|
28
|
+
installed_deps = module_manager.get_installed_deps(lock_content)
|
29
|
+
|
30
|
+
# 计数
|
31
|
+
check_count = 0
|
32
|
+
|
33
|
+
# 遍历需要被检查的模块
|
34
|
+
modules.each { |module_name, module_item|
|
35
|
+
|
36
|
+
#组件的的真实依赖包括间接依赖 依据podfile.lock递归读取
|
37
|
+
real_dependencies = module_manager.real_dependencies(module_name, lock_content)
|
38
|
+
|
39
|
+
substart = (Time.now.to_f * 1000).to_i
|
40
|
+
real_frameworks = self.module_frameworks(real_dependencies)
|
41
|
+
puts "#{module_name}打点0:#{((Time.now.to_f * 1000).to_i - substart).to_s}毫秒\n"
|
42
|
+
substart = (Time.now.to_f * 1000).to_i
|
43
|
+
|
44
|
+
#podspec写的直接依赖
|
45
|
+
podspec_dependencies = module_manager.podspec_dependencies(module_item.podspec_path)
|
46
|
+
|
47
|
+
# 某个模块从代码文件里读取的真实需要的依赖库 #import <XX/XX.h>
|
48
|
+
import_item_map = {}
|
49
|
+
module_item.source_files.each { |source_file|
|
50
|
+
self.update_import_item_map(source_file, module_name, import_item_map)
|
51
|
+
check_count += 1
|
52
|
+
}
|
53
|
+
|
54
|
+
if import_item_map.size == 0 and real_dependencies.size == 0
|
55
|
+
next
|
56
|
+
end
|
57
|
+
|
58
|
+
# real_dependencies指的是根据组件podspec的写法 解析出来的依赖库
|
59
|
+
# import_item_map指的是根据代码文件解析出来的依赖库
|
60
|
+
loss = []
|
61
|
+
import_item_map.each_key {|key|
|
62
|
+
if key != module_name
|
63
|
+
did_loss = true
|
64
|
+
|
65
|
+
real_frameworks.each { |dep, frameworks|
|
66
|
+
frameworks.each {|framework|
|
67
|
+
if key == framework
|
68
|
+
did_loss = false
|
69
|
+
break
|
70
|
+
end
|
71
|
+
}
|
72
|
+
}
|
73
|
+
|
74
|
+
if did_loss
|
75
|
+
loss << "#{key}"
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
}
|
80
|
+
|
81
|
+
puts "#{module_name}打点1:#{((Time.now.to_f * 1000).to_i - substart).to_s}毫秒\n"
|
82
|
+
substart = (Time.now.to_f * 1000).to_i
|
83
|
+
|
84
|
+
unused = []
|
85
|
+
real_frameworks.each { |dep, frameworks|
|
86
|
+
use = false
|
87
|
+
frameworks.each{|framework|
|
88
|
+
if import_item_map.keys.include?(framework)
|
89
|
+
use = true
|
90
|
+
break
|
91
|
+
end
|
92
|
+
}
|
93
|
+
if not use and podspec_dependencies.include?(dep)
|
94
|
+
unused << "# s.dependency '#{dep}'"
|
95
|
+
end
|
96
|
+
}
|
97
|
+
|
98
|
+
if loss.size >0
|
99
|
+
puts "⚠️ [#{module_name}] 请到#{module_name}.podspec文件中添加缺少的依赖库, 如下:"
|
100
|
+
puts loss
|
101
|
+
puts "\n"
|
102
|
+
end
|
103
|
+
|
104
|
+
if unused.size >0
|
105
|
+
puts "⚠️ [#{module_name}] 请到#{module_name}.podspec文件中移除未被引用的依赖库, 如下:"
|
106
|
+
puts unused
|
107
|
+
puts "\n"
|
108
|
+
end
|
109
|
+
|
110
|
+
puts "#{module_name}打点2:#{((Time.now.to_f * 1000).to_i - substart).to_s}毫秒\n"
|
111
|
+
}
|
112
|
+
|
113
|
+
duration = ((Time.now.to_f * 1000).to_i - start)*0.001
|
114
|
+
puts "依赖库关系检查完毕! 共检查#{modules.size.to_s}个组件(#{check_count}个文件) 耗时:#{duration.round(2)}秒"
|
115
|
+
end
|
116
|
+
|
117
|
+
# 将某个库的所有依赖转化为其对应的frameworks
|
118
|
+
def self.module_frameworks(dependencies)
|
119
|
+
project_manager = XCProject::XcodeprojManager.share_manager
|
120
|
+
result_map = {}
|
121
|
+
dependencies.each do |dependency|
|
122
|
+
frameworks = project_manager.frameworks_with_module_name(dependency)
|
123
|
+
result_map[dependency] = frameworks
|
124
|
+
end
|
125
|
+
result_map
|
126
|
+
end
|
127
|
+
|
128
|
+
# 获取某个文件内对应类型的import <XX/XX>
|
129
|
+
def self.update_import_item_map(file_path, module_name, import_item_map)
|
130
|
+
if File.directory?(file_path)
|
131
|
+
return
|
132
|
+
end
|
133
|
+
|
134
|
+
line_array = IO.readlines(file_path)
|
135
|
+
line_array.each_with_index { |line, index|
|
136
|
+
line = line.gsub(" ", "")
|
137
|
+
if not line.start_with?("#import<") and not line.start_with?("#include<")
|
138
|
+
next
|
139
|
+
end
|
140
|
+
|
141
|
+
line_split_array = line.split(">")
|
142
|
+
if line_split_array.size == 0
|
143
|
+
next
|
144
|
+
end
|
145
|
+
|
146
|
+
import_name = line_split_array[0]+">" # #import <XX/XX.h> 或者 #import <XX.h> 防止后边有其他的内容
|
147
|
+
if not import_name.include?(".h") and not import_name.include?(".H")
|
148
|
+
next
|
149
|
+
end
|
150
|
+
import_array = import_name.split("/")
|
151
|
+
if line_split_array.size == 0
|
152
|
+
next
|
153
|
+
end
|
154
|
+
|
155
|
+
pod = import_array[0].gsub("#import<", "").gsub("#include<", "")
|
156
|
+
pod = pod.gsub(".h>", "")
|
157
|
+
pod = pod.gsub(".H>", "")
|
158
|
+
|
159
|
+
# 已经包含
|
160
|
+
if import_item_map[pod]
|
161
|
+
next
|
162
|
+
end
|
163
|
+
|
164
|
+
# 系统级依赖库
|
165
|
+
if self.system_deps.include?(pod)
|
166
|
+
next
|
167
|
+
end
|
168
|
+
|
169
|
+
file_name = File.basename(file_path)
|
170
|
+
line_index = index+1
|
171
|
+
|
172
|
+
import_item_map[pod] = ImportItem.new(import_name, module_name, file_name, line_index, pod)
|
173
|
+
|
174
|
+
}
|
175
|
+
end
|
176
|
+
|
177
|
+
# 系统级的依赖库
|
178
|
+
def self.system_deps
|
179
|
+
return Constants::LIBLIBARYS + Constants::FRAMEWORKS
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'yaml'
|
3
|
+
require_relative '../constants'
|
4
|
+
require_relative 'module'
|
5
|
+
require_relative 'item'
|
6
|
+
require_relative 'xcodeproj'
|
7
|
+
|
8
|
+
module Import
|
9
|
+
class ImportManager
|
10
|
+
|
11
|
+
# 检查import规范
|
12
|
+
def self.import_check
|
13
|
+
puts "\nimport规范检查中..."
|
14
|
+
start = (Time.now.to_f * 1000).to_i
|
15
|
+
|
16
|
+
project_manager = XCProject::XcodeprojManager.share_manager
|
17
|
+
|
18
|
+
# 获取需要检查的模块名和路径
|
19
|
+
modules = project_manager.get_development_module
|
20
|
+
|
21
|
+
# 获取Pods和本地依赖库的 组件名和包含的头文件列表 map类型
|
22
|
+
module_headers = project_manager.get_installed_headers_module
|
23
|
+
|
24
|
+
# 计数
|
25
|
+
check_count = 0
|
26
|
+
|
27
|
+
# 遍历需要被检查的模块
|
28
|
+
modules.each_value { |module_item|
|
29
|
+
|
30
|
+
# 某个模块从代码文件里读取的双引号依赖库 #import "XX/XX.h"
|
31
|
+
import_item_map = {}
|
32
|
+
|
33
|
+
#头文件的文件名
|
34
|
+
header_file_names = []
|
35
|
+
|
36
|
+
module_item.source_files.each { |source_file|
|
37
|
+
|
38
|
+
header_file_name = File.basename(source_file)
|
39
|
+
if header_file_name.end_with?(".h") or header_file_name.end_with?(".hpp") or header_file_name.end_with?(".pch")
|
40
|
+
header_file_names << header_file_name
|
41
|
+
end
|
42
|
+
|
43
|
+
self.update_import_dot_item_map(source_file, module_item, import_item_map)
|
44
|
+
|
45
|
+
check_count += 1
|
46
|
+
}
|
47
|
+
|
48
|
+
if import_item_map.size == 0 and header_file_names.size == 0
|
49
|
+
next
|
50
|
+
end
|
51
|
+
|
52
|
+
messages = []
|
53
|
+
import_item_map.each {|key, value| #key是 xxx.h value是{ImportItem:}
|
54
|
+
|
55
|
+
# 当前仓库的文件已包含
|
56
|
+
if header_file_names.include?(key)
|
57
|
+
next
|
58
|
+
end
|
59
|
+
|
60
|
+
# 是否是导入的系统lib
|
61
|
+
if self.check_sys_libary(key)
|
62
|
+
next
|
63
|
+
end
|
64
|
+
|
65
|
+
search_module_name = self.get_module_name(module_headers, key)
|
66
|
+
|
67
|
+
unless search_module_name
|
68
|
+
next
|
69
|
+
end
|
70
|
+
|
71
|
+
messages << "#{value.file_name} 第#{value.line_index}行 #{value.import_name} => #import <#{search_module_name}/#{value.pod}>"
|
72
|
+
|
73
|
+
}
|
74
|
+
|
75
|
+
if messages.size >0
|
76
|
+
puts "⚠️ 组件#{module_item.module_name}存在以下情况, 请检查:"
|
77
|
+
puts messages
|
78
|
+
puts "\n"
|
79
|
+
end
|
80
|
+
|
81
|
+
}
|
82
|
+
|
83
|
+
duration = ((Time.now.to_f * 1000).to_i - start)*0.001
|
84
|
+
puts "import规范检查完毕! 共检查#{modules.size.to_s}个组件(#{check_count}个文件) 耗时:#{duration.round(2)}秒"
|
85
|
+
end
|
86
|
+
|
87
|
+
# 获取某个文件内对应类型的import "XXX.h"
|
88
|
+
def self.update_import_dot_item_map(file_path, module_name, import_item_map)
|
89
|
+
if File.directory?(file_path)
|
90
|
+
return
|
91
|
+
end
|
92
|
+
|
93
|
+
line_array = IO.readlines(file_path)
|
94
|
+
line_array.each_with_index { |line, index|
|
95
|
+
line = line.gsub(" ", "")
|
96
|
+
unless line.start_with?("#import\"")
|
97
|
+
next
|
98
|
+
end
|
99
|
+
|
100
|
+
if line.start_with?("@implementation")
|
101
|
+
break
|
102
|
+
end
|
103
|
+
|
104
|
+
line_split_array = line.split("\"")
|
105
|
+
if line_split_array.size <= 1
|
106
|
+
next
|
107
|
+
end
|
108
|
+
|
109
|
+
import_name = "#{line_split_array[0]}\"#{line_split_array[1]}\"" # import"XXX.h" 或import"XXX.AAA.h" 防止后边有其他内容
|
110
|
+
|
111
|
+
# 特殊处理
|
112
|
+
if import_name == "#import\"NBSAppAgent.h\""
|
113
|
+
next
|
114
|
+
end
|
115
|
+
|
116
|
+
podfile = import_name
|
117
|
+
podfile = podfile.gsub("#import\"", "")
|
118
|
+
podfile = podfile.gsub("\"", "") # 变成 XX.h
|
119
|
+
|
120
|
+
# 已经包含
|
121
|
+
if import_item_map[podfile]
|
122
|
+
next
|
123
|
+
end
|
124
|
+
pre_index = index-1 < 0 ? 0 : index-1
|
125
|
+
pre_line = line_array[pre_index]
|
126
|
+
pre_line = pre_line.gsub(" ", "").gsub("\n", "")
|
127
|
+
if pre_line.start_with?("#if") or pre_line == "#else"
|
128
|
+
next
|
129
|
+
end
|
130
|
+
|
131
|
+
file_name = File.basename(file_path)
|
132
|
+
line_index = index+1
|
133
|
+
|
134
|
+
import_item_map[podfile] = ImportItem.new(import_name, module_name, file_name, line_index, podfile)
|
135
|
+
|
136
|
+
}
|
137
|
+
end
|
138
|
+
|
139
|
+
# 根据文件名反查出属于哪个组件
|
140
|
+
def self.get_module_name(module_headers, file_name)
|
141
|
+
module_headers.each do |module_name,module_item|
|
142
|
+
module_item.source_files.each {|source_file|
|
143
|
+
if source_file.basename.to_s == file_name
|
144
|
+
return module_name
|
145
|
+
end
|
146
|
+
}
|
147
|
+
end
|
148
|
+
return nil
|
149
|
+
end
|
150
|
+
|
151
|
+
# 获取某库是否是系统级的依赖库
|
152
|
+
def self.check_sys_libary(name)
|
153
|
+
name = name.gsub(".h", "")
|
154
|
+
libs = Constants::LIBLIBARYS
|
155
|
+
return libs.include?(name)
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
|
2
|
+
class ImportItem
|
3
|
+
attr_reader :import_name, :module_item, :file_name, :line_index, :pod
|
4
|
+
def initialize(import_name, module_item, file_name, line_index, pod)
|
5
|
+
@import_name = import_name
|
6
|
+
@module_item = module_item
|
7
|
+
@file_name = file_name
|
8
|
+
@line_index = line_index
|
9
|
+
@pod = pod
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'yaml'
|
3
|
+
require 'cocoapods'
|
4
|
+
require 'xcodeproj'
|
5
|
+
require_relative '../constants'
|
6
|
+
|
7
|
+
module ModuleModule
|
8
|
+
|
9
|
+
class ModuleDepObject
|
10
|
+
def initialize(module_name)
|
11
|
+
@module_name = module_name
|
12
|
+
@dependencies = []
|
13
|
+
@dependencies_expanded = false
|
14
|
+
end
|
15
|
+
|
16
|
+
# 组件名
|
17
|
+
attr_reader :module_name
|
18
|
+
|
19
|
+
# 组件所引用的依赖库
|
20
|
+
attr_reader :dependencies
|
21
|
+
|
22
|
+
# 组件的依赖是否被全部展开
|
23
|
+
attr_reader :dependencies_expanded
|
24
|
+
|
25
|
+
|
26
|
+
# 添加一个依赖库
|
27
|
+
def add_dependency(dependency)
|
28
|
+
self.dependencies << dependency
|
29
|
+
end
|
30
|
+
|
31
|
+
def did_expanded
|
32
|
+
@dependencies_expanded = true
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
class ModuleManager
|
38
|
+
|
39
|
+
# 缓存某个的依赖库
|
40
|
+
# {{"module_nameA":ModuleDepObject,{"module_nameB":ModuleDepObject}
|
41
|
+
attr_reader :all_module_dependencies
|
42
|
+
|
43
|
+
def initialize
|
44
|
+
@all_module_dependencies = {}
|
45
|
+
end
|
46
|
+
# 单例方法
|
47
|
+
@@instance = ModuleManager.new
|
48
|
+
|
49
|
+
# 获取单例
|
50
|
+
def self.share_instance
|
51
|
+
@@instance
|
52
|
+
end
|
53
|
+
|
54
|
+
private_class_method :new
|
55
|
+
|
56
|
+
def real_dependencies(module_name, lock_content)
|
57
|
+
|
58
|
+
lock_dependencies = lock_content["PODS"]
|
59
|
+
|
60
|
+
module_dep = ModuleDepObject.new(module_name)
|
61
|
+
|
62
|
+
# 可能是取的缓存 所以必须赋值
|
63
|
+
module_dep = self.get_module_dep(lock_dependencies, module_dep)
|
64
|
+
|
65
|
+
module_dep.dependencies
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
# 根据podfile.lock里内容递归获取某个库的依赖库
|
70
|
+
@private
|
71
|
+
def get_module_dep(lock_dependencies, module_dep)
|
72
|
+
|
73
|
+
module_dep_cache = self.all_module_dependencies[module_dep.module_name]
|
74
|
+
if module_dep_cache&.dependencies_expanded
|
75
|
+
return module_dep_cache
|
76
|
+
end
|
77
|
+
|
78
|
+
# 找出当前组件的依赖
|
79
|
+
lock_dependencies.each { |item|
|
80
|
+
|
81
|
+
item_name= item
|
82
|
+
if item.class == Hash
|
83
|
+
item_name = item.keys[0]
|
84
|
+
end
|
85
|
+
|
86
|
+
item_name = item_name.split(" (")[0]
|
87
|
+
item_name = item_name.split("/")[0] #解决子仓库的问题
|
88
|
+
|
89
|
+
# 非当前组件
|
90
|
+
if item_name != module_dep.module_name
|
91
|
+
next
|
92
|
+
end
|
93
|
+
|
94
|
+
# 当前组件无其他依赖
|
95
|
+
if item.class == String
|
96
|
+
break
|
97
|
+
end
|
98
|
+
|
99
|
+
# 当前组件有其他依赖
|
100
|
+
# item:{"A (99999)"=>["Masonry", "MJExtension"]
|
101
|
+
item.each {|name, child_names|
|
102
|
+
child_names.each { |child_name|
|
103
|
+
child_name = child_name.split(" (")[0]
|
104
|
+
child_name = child_name.split("/")[0] #解决子仓库的问题
|
105
|
+
|
106
|
+
if item_name == child_name #解决item_name = AFNetworking child_name=AFNetworking/NSURLSession
|
107
|
+
next
|
108
|
+
end
|
109
|
+
|
110
|
+
# 当前库的依赖加进去
|
111
|
+
unless module_dep.dependencies.include?(child_name)
|
112
|
+
module_dep.dependencies << child_name
|
113
|
+
end
|
114
|
+
|
115
|
+
# 当前库的依赖的子依赖
|
116
|
+
child = ModuleDepObject.new(child_name)
|
117
|
+
child_dep = self.get_module_dep(lock_dependencies, child)
|
118
|
+
|
119
|
+
child_dep.did_expanded
|
120
|
+
self.all_module_dependencies[child_dep.module_name] = child_dep
|
121
|
+
|
122
|
+
child_dep.dependencies.each { |sun_name|
|
123
|
+
# 孙子依赖加进去
|
124
|
+
unless module_dep.dependencies.include?(sun_name)
|
125
|
+
module_dep.dependencies << sun_name
|
126
|
+
end
|
127
|
+
}
|
128
|
+
}
|
129
|
+
}
|
130
|
+
}
|
131
|
+
|
132
|
+
module_dep.did_expanded
|
133
|
+
self.all_module_dependencies[module_dep.module_name] = module_dep
|
134
|
+
module_dep
|
135
|
+
end
|
136
|
+
|
137
|
+
# 获取某个库的podspec的直接依赖
|
138
|
+
def podspec_dependencies(podspec_path)
|
139
|
+
|
140
|
+
podspec_json_path = podspec_path.to_s.end_with?(".podspec.json") ? podspec_path : nil
|
141
|
+
|
142
|
+
podspec_content = nil
|
143
|
+
if podspec_json_path and File.exist?(podspec_json_path)
|
144
|
+
json = File.read(podspec_json_path)
|
145
|
+
podspec_content = JSON.parse(json)
|
146
|
+
elsif podspec_path and File.exist?(podspec_path)
|
147
|
+
json = self.get_podspec_content(podspec_path)
|
148
|
+
podspec_content = JSON.parse(json)
|
149
|
+
end
|
150
|
+
|
151
|
+
podspec_content["dependencies"] ? podspec_content["dependencies"].keys : []
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
# 获取已安装的依赖库
|
156
|
+
def get_installed_deps(lock_dependencies)
|
157
|
+
lock_dependencies["SPEC CHECKSUMS"] ? lock_dependencies["SPEC CHECKSUMS"].keys : {}
|
158
|
+
end
|
159
|
+
|
160
|
+
# 根据podspec路径把podspec转为json
|
161
|
+
@private
|
162
|
+
def get_podspec_content(podspec_path)
|
163
|
+
|
164
|
+
spec_contents = File.open(podspec_path, 'r:utf-8', &:read)
|
165
|
+
|
166
|
+
if spec_contents.respond_to?(:encoding) && spec_contents.encoding.name != 'UTF-8'
|
167
|
+
spec_contents.encode!('UTF-8')
|
168
|
+
end
|
169
|
+
|
170
|
+
path = Pathname.new(podspec_path)
|
171
|
+
|
172
|
+
spec = ::Pod._eval_podspec(spec_contents, path)
|
173
|
+
|
174
|
+
spec.to_json
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
end
|