podfileDep 1.1.4 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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