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,1408 @@
1
+ require 'pathname'
2
+ require 'yaml'
3
+ require 'cocoapods'
4
+ require 'xcodeproj'
5
+ autoload :CheckDep, "check_dep"
6
+
7
+ module PodfileDep
8
+
9
+ $libLibarys = ["time","removefile","AppleTextureEncoder","standards","AvailabilityMacros","semaphore","SystemHealthClient","dns_sd","ftw","checkint","pwd","bitstring","utime","err","CommonCrypto","inttypes","resolv","c++","_ctermid","stdlib","sysdir","unwind","fts","vis","AvailabilityVersions","membership","libxml2","ulimit","_stdio","ntsid","net","notify_keys","CMakeLists.txt","AssertMacros","float","aio","libxml","ConditionalMacros","locale","fmtmsg","unicode","_wctype","cpio","langinfo","xlocale","limits","Endian","unwind_itanium","_types","db","bank","_ctype","unistd","stddef","dispatch","wctype","fcntl","mach_debug","Availability","stringlist","tar","readpassphrase","_locale","ttyent","bzlib","cache","signal","dirent","mpool","libDER","unwind_arm_ehabi","spawn","crt_externs","simd","regex","AvailabilityInternal","arpa","syslog","bsm","netinet","libunwind","libgen","corpses","rpcsvc","monetary","compression","expat_external","setjmp","tgmath","notify","getopt","_regex","dlfcn","strings","TargetConditionals","fnmatch","pthread","xlocale","execinfo","networkext","printf","architecture","alloca","sys","iconv","fenv","expat","grp","secure","ctype","fstab","pthread_spis","machine","SystemHealthManager","voucher","wordexp","zlib","nl_types","paths","AppleEXR","wchar","sqlite3","util","gethostuuid","sched","sqlite3ext","iso646","Block","netdb","pthread","math","memory","asl","__wctype","mach","errno","device","termios","copyfile","os","xattr_flags","poll","_xlocale","stdio","mach-o","malloc","string_x86","arm","Spatial","__libunwind_config","nameser","dns","_types","rpc","netinet6","pthread_impl","objc","MacTypes","libkern","uuid","search","assert","AppleArchive","cache_callbacks","dns_util","glob","ifaddrs","utmpx","sandbox","arm64","stdint","complex","runetype","zconf","ucontext","sysexits","string","ndbm"]
10
+ $frameworks = ["MetricKit","Network","GameKit","AddressBookUI","CreateML","Speech","SafariServices","ProximityReader","Metal","AppClip","QuartzCore","CoreGraphics","StoreKit","GSS","CoreML","WatchConnectivity","UserNotificationsUI","ARKit","CoreLocationUI","MetalPerformanceShaders","AutomaticAssessmentConfiguration","ExternalAccessory","MediaPlayer","LinkPresentation","IdentityLookupUI","MediaToolbox","UIKit","MessageUI","MetalFX","CoreNFC","HomeKit","CryptoKit","ScreenTime","NewsstandKit","MLCompute","NaturalLanguage","CoreVideo","MetalKit","OSLog","PDFKit","CoreText","UniformTypeIdentifiers","IOKit","ManagedSettings","AVFoundation","Accelerate","DeviceCheck","ImageIO","BackgroundTasks","FamilyControls","ClockKit","CoreBluetooth","ThreadNetwork","SensorKit","Security","Contacts","CarPlay","ExtensionFoundation","RealityKit","PhotosUI","RoomPlan","ReplayKit","IOSurface","SpriteKit","MobileCoreServices","IntentsUI","QuickLookThumbnailing","CoreMedia","BusinessChat","Intents","ColorSync","VideoSubscriberAccount","PencilKit","ManagedSettingsUI","CoreServices","HealthKit","MultipeerConnectivity","BackgroundAssets","WebKit","NotificationCenter","SystemConfiguration","GameController","CoreTelephony","ActivityKit","AVFAudio","AssetsLibrary","AudioUnit","FileProvider","DeviceActivity","Social","IdentityLookup","Matter","CoreImage","CoreAudio","MusicKit","DeviceDiscoveryExtension","MediaSetup","AutomatedDeviceEnrollment","CoreAudioKit","ExposureNotification","ClassKit","VisionKit","SwiftUI","Combine","ModelIO","PHASE","SharedWithYou","OpenGLES","MetalPerformanceShadersGraph","CreateMLComponents","Accounts","FileProviderUI","ShazamKit","CarKey","RealityFoundation","QuickLook","AppIntents","AuthenticationServices","WeatherKit","DataDetection","AudioToolbox","NearbyInteraction","ContactsUI","CoreSpotlight","LocalAuthentication","MatterSupport","Foundation","AdSupport","LocalAuthenticationEmbeddedUI","Accessibility","HealthKitUI","PushKit","Vision","CoreAudioTypes","NetworkExtension","ExtensionKit","OpenAL","EventKit","MediaAccessibility","Charts","iAd","MapKit","AppTrackingTransparency","CoreHaptics","CallKit","CoreFoundation","TabularData","CoreMotion","AVRouting","WidgetKit","AVKit","AddressBook","SoundAnalysis","GroupActivities","CoreLocation","CloudKit","SharedWithYouCore","CFNetwork","Photos","JavaScriptCore","GameplayKit","PushToTalk","CoreMIDI","Twitter","PassKit","ImageCaptureCore","CoreData","GLKit","AdServices","Messages","CryptoTokenKit","VideoToolbox","SafetyKit","UserNotifications","DeveloperToolsSupport","CoreTransferable","EventKitUI","SceneKit"]
11
+
12
+ $podfile_thirdParty = 'PodfileThirdParty.yaml'
13
+ $podfile_module = 'PodfileModule.yaml'
14
+ $podfile_local = 'PodfileLocal.yaml'
15
+ $podfile_lock = 'Podfile.lock'
16
+
17
+
18
+ class PodDependency
19
+ attr_reader :pod, :version, :git, :tag, :branch, :podspec, :configurations, :path, :inhibit_warnings, :source, :binary
20
+ def initialize(module_name, pod, version, git, tag, branch, podspec, configurations, path, inhibit_warnings, source, binary)
21
+ @module_name = module_name
22
+ @pod = pod
23
+ @version = version
24
+ @git = git
25
+ @tag = tag
26
+ @branch = branch
27
+ @podspec = podspec
28
+ @configurations = configurations
29
+ @path = path
30
+ @inhibit_warnings = inhibit_warnings
31
+ @source = source
32
+ @binary = binary
33
+ end
34
+ end
35
+
36
+ def PodfileDep.setup(targets)
37
+
38
+ PodfileDep.checkFile()
39
+ PodfileDep.checkParam(targets)
40
+
41
+ PodfileDep.generateLocal()
42
+ PodfileDep.generateModule($podfile_module, "企业内部组件", "TestPodName")
43
+ PodfileDep.generateModule($podfile_thirdParty, "第三方组件", "Masonry")
44
+
45
+ all_dependencies = {}
46
+ PodfileDep.readYamlDep(all_dependencies, $podfile_thirdParty, false)
47
+ PodfileDep.readYamlDep(all_dependencies, $podfile_module, false)
48
+ PodfileDep.readYamlDep(all_dependencies, $podfile_local, true)
49
+
50
+ PodfileDep.logDeps(all_dependencies)
51
+
52
+ result = PodfileDep.generateTargetsDep(targets, all_dependencies)
53
+
54
+ # 读取依赖
55
+ lock_dependencies = PodfileDep.lockDeps()
56
+ PodfileDep.logChangedDep(all_dependencies, lock_dependencies)
57
+
58
+ return result
59
+ end
60
+
61
+ def PodfileDep.checkFile()
62
+ unless File.exist?("Podfile") then
63
+ puts "本脚本文件必须和Podfile文件同级目录"
64
+ exit!
65
+ end
66
+ end
67
+
68
+ def PodfileDep.checkParam(targets)
69
+ msg = "❌ setup里参数需要填写target名字, 参数类型是字符串格式的数组。例如[\"test1\", \"test2\"]"
70
+ unless targets.class == Array
71
+ puts msg
72
+ exit!
73
+ end
74
+
75
+ targets.each { |target|
76
+ unless target.class == String
77
+ puts msg
78
+ exit!
79
+ end
80
+ }
81
+ end
82
+
83
+ def PodfileDep.lockDeps()
84
+ result = {}
85
+
86
+ unless File.exist?($podfile_lock)
87
+ return result
88
+ end
89
+
90
+ dependencies_yaml = YAML.load_file($podfile_lock)
91
+ if dependencies_yaml.key?('DEPENDENCIES')
92
+ temp_dependencies = dependencies_yaml['DEPENDENCIES']
93
+ temp_dependencies.each { |item|
94
+ old_dependency = item.split(' (')
95
+ result[old_dependency[0]] = old_dependency[1]
96
+ }
97
+ end
98
+ return result
99
+ end
100
+
101
+ def PodfileDep.generateLocal()
102
+ if File.exist?($podfile_local) then
103
+ return
104
+ end
105
+ content = "
106
+ # 1、本依赖配置文件优先级最高, 用来覆盖其他两个依赖的yaml文件中的组件。 尝试打开以下注释, 修改对应字段, 然后执行pod install
107
+ # 2、PODS下边的配置是数组形式, 如果有多个就写多个
108
+ # 3、请将本文件加入到忽略文件中
109
+
110
+ PODS:
111
+
112
+ # - module: Masonry
113
+ # pod: Masonry
114
+ # path: ~/CodePath
115
+
116
+ "
117
+ File.open($podfile_local, 'w') { |file|
118
+ file.write(content)
119
+ }
120
+ end
121
+
122
+ def PodfileDep.generateModule(yaml_module, prefix, pod_name)
123
+ if File.exist?(yaml_module) then
124
+ return
125
+ end
126
+
127
+ content = "
128
+ # #{prefix}
129
+ # 1、优先级:path > podspec > version > tag > branch (git字段是tag字段或branch字段不为空时用到, source字段是version不为空时用到, path字段就是直接从对应的路径读取依赖)
130
+ # 2、为方便查看, 建议将本文件拖入壳工程引用
131
+
132
+ # 如果 source = null && binary = false 则source默认使用这个
133
+ SOURCE: 这里替换成源码的source
134
+
135
+ # 如果 source = null && binary = true 则source默认使用这个
136
+ BINARY_SOURCE: 这里替换成二进制的source
137
+
138
+ PODS:
139
+
140
+ # - module: #{pod_name}
141
+ # pod: #{pod_name}
142
+ # podspec: null
143
+ # version: 1.1.0
144
+ # git: null
145
+ # tag: null
146
+ # branch: null
147
+ # configurations: null
148
+ # inhibit_warnings: true
149
+ # source: null
150
+ # binary: true
151
+ "
152
+ File.open(yaml_module, 'w') { |file|
153
+ file.write(content)
154
+ }
155
+ end
156
+
157
+ def PodfileDep.readYamlDep(all_dependencies, yamlName, canCover)
158
+ unless File.exist?(yamlName)
159
+ puts "文件不存在:"+yamlName
160
+ return
161
+ end
162
+
163
+ begin
164
+ dependencies_yaml = YAML.load_file(yamlName)
165
+ rescue Exception => e
166
+ puts e
167
+ puts yamlName+'文件解析异常, 请检查 ⬆️'
168
+ exit!
169
+ end
170
+
171
+ prefix_message = "解析:"+yamlName + "=> "
172
+ unless dependencies_yaml.class == Hash
173
+ puts "#{prefix_message}共0个依赖(文件不是keyValue形式)"
174
+ return
175
+ end
176
+
177
+ unless dependencies_yaml.key?('PODS')
178
+ puts "#{prefix_message}共0个依赖(文件内的key[PODS]不存在)"
179
+ return
180
+ end
181
+
182
+ unless dependencies_yaml['PODS'].class == Array
183
+ puts "#{prefix_message}共0个依赖(文件配置的依赖PODS下边不是数组或数组为空)"
184
+ return
185
+ end
186
+
187
+ # 读取默认源码source
188
+ default_source = nil
189
+ if dependencies_yaml.key?('SOURCE') and dependencies_yaml['SOURCE'].class == String
190
+ default_source = dependencies_yaml['SOURCE']
191
+ end
192
+
193
+ # 读取默认二进制source
194
+ default_binary_source = nil
195
+ if dependencies_yaml.key?('BINARY_SOURCE') and dependencies_yaml['BINARY_SOURCE'].class == String
196
+ default_binary_source = dependencies_yaml['BINARY_SOURCE']
197
+ end
198
+
199
+ dependencies = dependencies_yaml['PODS']
200
+ puts "#{prefix_message}共"+dependencies.size.to_s + "个依赖"
201
+
202
+ covers = []
203
+ repeats = []
204
+ # 遍历依赖库列表
205
+ dependencies.each { |dependency|
206
+
207
+ # 不能覆盖,有重复依赖
208
+ if all_dependencies.key?(dependency['pod'])
209
+ # 能被覆盖
210
+ if canCover
211
+ covers << dependency['pod']
212
+ else
213
+ repeats << dependency['pod']
214
+ end
215
+ end
216
+
217
+ # 如果source不存在, 则使用默认的
218
+ unless dependency['source']
219
+ dependency['source'] = dependency['binary'] ? default_binary_source : default_source
220
+ end
221
+
222
+ # 校验
223
+ if not dependency['path'] and not dependency['podspec'] and not dependency['source'] and dependency['version']
224
+ puts '❌ 请为组件'+dependency['pod']+'配置source字段或在'+ yamlName+'顶层设置默认source(字段:SOURCE和BINARY_SOURCE)'
225
+ exit!
226
+ end
227
+
228
+ # 警告
229
+ if dependency['path'] and not canCover
230
+ puts '⚠️ 组件'+dependency['pod']+'使用path依赖方式应该写在'+ $podfile_local
231
+ end
232
+
233
+ if dependency['podspec'] and not canCover
234
+ puts '⚠️ 组件'+dependency['pod']+'使用podspec依赖方式应该写在'+ $podfile_local
235
+ end
236
+
237
+ # 生成依赖对象
238
+ module_name = dependency['module']
239
+ pod = dependency['pod']
240
+ version = dependency['version']
241
+ git = dependency['git']
242
+ tag = dependency['tag']
243
+ branch = dependency['branch']
244
+ podspec = dependency['podspec']
245
+ configurations = dependency['configurations']
246
+ path = dependency['path']
247
+ inhibit_warnings = dependency['inhibit_warnings'] ? dependency['inhibit_warnings'] : false
248
+ source = dependency['source']
249
+ binary = dependency['binary']
250
+
251
+ unless pod then
252
+ puts yamlName + ': pod 字段(pod组件名字)必须存在,请检查'
253
+ exit!
254
+ end
255
+
256
+ unless path or podspec or version or git or tag or branch then
257
+ puts yamlName + ': path/podspec/version/git/tag/branch 字段需至少有一个存在,请检查'
258
+ exit!
259
+ end
260
+
261
+ pod_dependency = PodDependency.new(module_name, pod, version, git, tag, branch, podspec, configurations, path, inhibit_warnings, source, binary);
262
+
263
+ # 加入全部的依赖map中
264
+ all_dependencies[dependency['pod']] = pod_dependency
265
+ }
266
+
267
+ if covers.size > 0
268
+ puts "✅ 被#{yamlName}中同名组件覆盖的组件如下:"
269
+ puts covers
270
+ end
271
+
272
+ if repeats.size > 0
273
+ puts "❌ 发现重复组件如下,请检查并删除重复依赖:"
274
+ puts repeats
275
+ exit!
276
+ end
277
+ end
278
+
279
+ def PodfileDep.logDeps(all_dependencies)
280
+ puts "\n➡️ 解析依赖总数共计:#{all_dependencies.size}个"
281
+ puts '⬇️ 按照 Podfile 格式打印一遍'
282
+ all_dependencies.each_value { |dependency|
283
+
284
+ # 执行打印
285
+ if dependency.path #路径引用
286
+ puts_result = 'pod \'' + dependency.pod + '\''
287
+ puts_result += ', :path => \'' + dependency.path + '\''
288
+ if dependency.configurations
289
+ puts_result += ', :configurations => [\'' + dependency.configurations.join('\', \'') + '\']'
290
+ end
291
+ if dependency.inhibit_warnings
292
+ puts_result += ', :inhibit_warnings => true'
293
+ end
294
+ puts puts_result
295
+ elsif dependency.podspec #podspec引用
296
+ puts_result = 'pod \'' + dependency.pod + '\''
297
+ if dependency.podspec
298
+ puts_result += ', :podspec => \'' + dependency.podspec + '\''
299
+ end
300
+ if dependency.configurations
301
+ puts_result += ', :configurations => [\'' + dependency.configurations.join('\', \'') + '\']'
302
+ end
303
+ if dependency.inhibit_warnings
304
+ puts_result += ', :inhibit_warnings => true'
305
+ end
306
+ puts puts_result
307
+ elsif dependency.version #版本号引用
308
+ puts_result = 'pod \'' + dependency.pod + '\', \'' + dependency.version.to_s + '\''
309
+ if dependency.configurations
310
+ puts_result += ', :configurations => [\'' + dependency.configurations.join('\', \'') + '\']'
311
+ end
312
+ if dependency.inhibit_warnings
313
+ puts_result += ', :inhibit_warnings => true'
314
+ end
315
+ if dependency.source
316
+ puts_result += ', :source => \'' + dependency.source + '\''
317
+ end
318
+ puts puts_result
319
+ elsif dependency.git #git仓库引用
320
+ puts_result = 'pod \'' + dependency.pod + '\''
321
+ if dependency.git
322
+ puts_result += ', :git => \'' + dependency.git + '\''
323
+ end
324
+ if dependency.tag
325
+ puts_result += ', :tag => \'' + dependency.tag + '\''
326
+ elsif dependency.branch
327
+ puts_result += ', :branch => \'' + dependency.branch + '\''
328
+ end
329
+ if dependency.configurations
330
+ puts_result += ', :configurations => [\'' + dependency.configurations.join('\', \'') + '\']'
331
+ end
332
+ if dependency.inhibit_warnings
333
+ puts_result += ', :inhibit_warnings => true'
334
+ end
335
+ puts puts_result
336
+ else # 路径 版本号 git地址 podspec 均未指定 去指定的source中找(source不会为空)
337
+ puts_result = 'pod \'' + dependency.pod + '\''
338
+ if dependency.configurations
339
+ puts_result += ', :configurations => [\'' + dependency.configurations.join('\', \'') + '\']'
340
+ end
341
+ if dependency.inhibit_warnings
342
+ puts_result += ', :inhibit_warnings => true'
343
+ end
344
+ if dependency.source
345
+ puts_result += ', :source => \'' + dependency.source + '\''
346
+ end
347
+ puts puts_result
348
+ end
349
+ }
350
+ puts '⬆️ 打印完毕'
351
+ end
352
+
353
+ def PodfileDep.generateTargetsDep(targets, all_dependencies)
354
+
355
+ tempArray = []
356
+ all_dependencies.each_value { |value|
357
+ tempArray << value
358
+ }
359
+
360
+ result = {}
361
+ targets.each { |target|
362
+ result[target] = tempArray
363
+ }
364
+ return result
365
+ end
366
+
367
+ def PodfileDep.logNewDependency(lock_dependencies, new_dependency)
368
+ new_add = true
369
+ lock_dependencies.each_key { |old_dependency|
370
+ if old_dependency == new_dependency
371
+ new_add = false
372
+ break
373
+ end
374
+ }
375
+
376
+ if new_add == true
377
+ puts '🈶 新增依赖: ' + new_dependency
378
+ end
379
+ end
380
+
381
+ def PodfileDep.logRemoveDependency(new_dependencies, old_dependency)
382
+
383
+ old_remove = true
384
+ new_dependencies.each { |new_dependency|
385
+ if old_dependency == new_dependency
386
+ old_remove = false
387
+ break
388
+ end
389
+ }
390
+
391
+ if old_remove == true
392
+ puts '🈚️ 删除依赖: ' + old_dependency
393
+ end
394
+ end
395
+
396
+ def PodfileDep.podfilePod()
397
+ work_dir = FileUtils.pwd()
398
+ podfile_path = work_dir + '/Podfile'
399
+ res = Array.new()
400
+ unless File.exist?(podfile_path) then
401
+ return res
402
+ end
403
+ lineArr = IO.readlines(podfile_path)
404
+ lineArr.each { |line|
405
+ line = line.gsub(" ", "")
406
+ if line.start_with?("pod\'")
407
+ arr = line.split("\'")
408
+ if arr.size>1 then
409
+ res << arr[1]
410
+ end
411
+ end
412
+ }
413
+ return res
414
+ end
415
+
416
+ def PodfileDep.logChangedDep(all_dependencies, lock_dependencies)
417
+ new_dependencies = PodfileDep.podfilePod()
418
+
419
+ all_dependencies.each_key { |key|
420
+ new_dependencies << key
421
+ }
422
+
423
+ new_dependencies.each { |item|
424
+ PodfileDep.logNewDependency(lock_dependencies, item)
425
+ }
426
+
427
+ lock_dependencies.each_key { |old_dependency|
428
+ PodfileDep.logRemoveDependency(new_dependencies, old_dependency)
429
+ }
430
+ end
431
+
432
+ def PodfileDep.logIndirectDependencies()
433
+
434
+ # 判断yaml文件是否存在
435
+ unless File.exist?($podfile_lock)
436
+ return
437
+ end
438
+
439
+ # 内容读取
440
+ dependencies_lock = YAML.load_file($podfile_lock)
441
+
442
+ dependencies = dependencies_lock['DEPENDENCIES']
443
+ unless dependencies
444
+ return
445
+ end
446
+
447
+ check_sums = dependencies_lock['SPEC CHECKSUMS']
448
+ unless check_sums
449
+ return
450
+ end
451
+
452
+ # 数组处理
453
+ checkValues = dependencies.collect! {|item|
454
+ item.split(' (')[0]
455
+ }
456
+ checkValues = dependencies.collect! {|item|
457
+ item.split('/')[0]
458
+ }
459
+
460
+ puts ''
461
+ check_sums.each{ |array|
462
+ beCheckValue = array[0]
463
+ unless checkValues.include?(beCheckValue)
464
+ puts '↪️ 间接依赖: '+ beCheckValue
465
+ end
466
+ }
467
+ end
468
+
469
+ def PodfileDep.logUnusedDependencies()
470
+
471
+ # 判断yaml文件是否存在
472
+ unless File.exist?($podfile_lock)
473
+ return
474
+ end
475
+
476
+ # 内容读取
477
+ dependencies_lock = YAML.load_file($podfile_lock)
478
+
479
+ dependencies = dependencies_lock['PODS']
480
+ unless dependencies
481
+ return
482
+ end
483
+
484
+ # 数组处理
485
+ checkValues = Array.new
486
+ allValues = Array.new
487
+ dependencies.each {|item|
488
+ if item.class == Hash then
489
+ item.each { |key, value|
490
+ item = key
491
+ allValues.concat(value)
492
+ }
493
+ end
494
+ item.split(' (')[0]
495
+ checkValues << item
496
+ }
497
+
498
+ # 数组处理
499
+ checkValues.collect! { |value|
500
+ value.split(' (')[0]
501
+ }
502
+ allValues.collect! { |value|
503
+ value.split(' (')[0]
504
+ }
505
+
506
+ # 处理有的只依赖了子仓库
507
+ allValues1 = allValues + []
508
+ allValues1.collect! { |value|
509
+ value.split('/')[0]
510
+ }
511
+
512
+
513
+ # 壳工程中的文件 只查找main.m 因为有的文件可能没有使用到
514
+ main_path = %x(find . -name "main.m")
515
+ main_path = PathUtil.expandPath(main_path)
516
+
517
+ checkValues.each { |item|
518
+ if not allValues.include?(item) and not allValues1.include?(item)
519
+ res = PodfileDep.checkUnuseInMain(item, main_path)
520
+ if res
521
+ puts "可能未使用的组件: " + item
522
+ end
523
+ end
524
+ }
525
+ end
526
+
527
+ def PodfileDep.checkUnuseInMain(item, main_path)
528
+ unless File.exist?(main_path)then
529
+ return true
530
+ end
531
+ lineArr = IO.readlines(main_path)
532
+ lineArr.each_with_index { |line, index|
533
+ if line.start_with?("#import <#{item}")
534
+ return false
535
+ end
536
+ }
537
+
538
+ return true
539
+ end
540
+
541
+ end
542
+
543
+ module ModuleCheck
544
+ class ModuleItem
545
+ attr_reader :moduleName, :modulePath
546
+ def initialize(moduleName, modulePath)
547
+ @moduleName = moduleName
548
+ @modulePath = modulePath
549
+ end
550
+ end
551
+
552
+ class ImportItem
553
+ attr_reader :importName, :moduleItem, :fileName, :lineIndex, :pod
554
+
555
+ def initialize(importName, moduleItem, fileName, lineIndex, pod)
556
+ @importName = importName
557
+ @moduleItem = moduleItem
558
+ @fileName = fileName
559
+ @lineIndex = lineIndex
560
+ @pod = pod
561
+ end
562
+
563
+ end
564
+
565
+ # 检查import规范
566
+ def ModuleCheck.checkImport()
567
+ puts "\nimport规范检查中..."
568
+ start = (Time.now.to_f * 1000).to_i
569
+
570
+ # podfile.lock的内容
571
+ lockContent = YAML.load_file($podfile_lock)
572
+
573
+ # 获取需要检查的模块名和路径
574
+ modules = ModuleCheck.getCheckModules(lockContent)
575
+
576
+ # podfile.lock里的所有组件依赖
577
+ lock_dependencies = lockContent["PODS"]
578
+
579
+ # 获取Pods和本地依赖库的 组件名和包含的头文件列表
580
+ module_headers = ModuleCheck.getModuleHeaders(modules)
581
+
582
+ # 白名单
583
+ importWhiteList = ModuleCheck.getImportWhiteList()
584
+
585
+ # 计数
586
+ checkCount = 0
587
+
588
+ # 遍历需要被检查的模块
589
+ modules.each { |moduleItem|
590
+
591
+ # 获取某个模块的podspec里写的依赖库和源码文件路径
592
+ moduleInfo = ModuleCheck.getDepdencyInModule(moduleItem, lockContent, shouldGetDependencies = false)
593
+ source_files_array = moduleInfo["source_files"]
594
+
595
+
596
+ # 某个模块从代码文件里读取的双引号依赖库 #import "XX/XX.h"
597
+ checkDirectory = false
598
+ importItemMap = {}
599
+ headerFileNames = [] #头文件的文件名
600
+ source_files_array.each {|source_files|
601
+ source_files_path = "#{moduleItem.modulePath}/#{source_files}"
602
+ Dir[source_files_path].select{|filePath|
603
+ headerFileName = File.basename(filePath)
604
+ if headerFileName.end_with?(".h") or headerFileName.end_with?(".hpp") or headerFileName.end_with?(".pch")
605
+ headerFileNames << headerFileName
606
+ end
607
+ ModuleCheck.updateImportDotItemMap(filePath, moduleItem, importItemMap)
608
+ checkCount += 1
609
+ if File.directory?(filePath)
610
+ checkDirectory = true
611
+ end
612
+ }
613
+ }
614
+
615
+ # 检测到文件夹
616
+ if checkDirectory
617
+ # puts "⚠️ #{moduleItem.moduleName}解析到文件夹, 请检查s.source_files的写法(注意加文件后缀,例如*.{h,m})"
618
+ end
619
+
620
+ if importItemMap.size == 0 and headerFileNames.size == 0
621
+ next
622
+ end
623
+
624
+ messages = []
625
+ importItemMap.each {|key, value|
626
+
627
+ # 当前仓库的文件已包含
628
+ if headerFileNames.include?(key)
629
+ next
630
+ end
631
+
632
+ # 白名单
633
+ if importWhiteList.include?(value.importName)
634
+ next
635
+ end
636
+
637
+ # 是否是导入的系统lib
638
+ if ModuleCheck.checkNameInLig(key)
639
+ next
640
+ end
641
+
642
+ moduleName = module_headers[value.pod]
643
+ result = moduleName ? "#import <#{moduleName}/#{value.pod}>" : "<>形式导入"
644
+ message = "#{value.fileName} 第#{value.lineIndex}行 #{value.importName} => #{result}"
645
+ messages << message
646
+
647
+ }
648
+
649
+ if messages.size >0
650
+ puts "⚠️ 组件#{moduleItem.moduleName}import存在以下情况, 请检查:"
651
+ puts messages
652
+ puts "\n"
653
+ end
654
+
655
+ }
656
+
657
+ duration = ((Time.now.to_f * 1000).to_i - start)*0.001
658
+ puts "import规范检查完毕! 共检查#{modules.size.to_s}个组件 #{checkCount}个文件 耗时:#{duration.round(2)}秒"
659
+ end
660
+
661
+
662
+ # 检查缺少的或着多的依赖
663
+ def ModuleCheck.checkModuleDep()
664
+ puts "\n依赖库检查中..."
665
+ start = (Time.now.to_f * 1000).to_i
666
+
667
+ # podfile.lock的内容
668
+ lockContent = YAML.load_file($podfile_lock)
669
+
670
+ # 获取需要检查的模块名和路径
671
+ modules = ModuleCheck.getCheckModules(lockContent)
672
+
673
+ # podfile.lock里的所有组件依赖
674
+ lock_dependencies = lockContent["PODS"]
675
+
676
+ # 获取所有已安装的依赖库
677
+ installedDeps = ModuleCheck.getInstalledDeps(lockContent)
678
+
679
+ # 计数
680
+ checkCount = 0
681
+
682
+ # 遍历需要被检查的模块
683
+ modules.each { |moduleItem|
684
+
685
+ # 获取某个模块的podspec里写的依赖库和源码文件路径
686
+ moduleInfo = ModuleCheck.getDepdencyInModule(moduleItem, lockContent, shouldGetDependencies = true)
687
+ dependencies = moduleInfo["dependencies"]
688
+ source_files_array = moduleInfo["source_files"]
689
+ podspec_dependencies = moduleInfo["podspec_dependencies"]
690
+
691
+ # 某个模块从代码文件里读取的真实需要的依赖库 #import <XX/XX.h>
692
+ checkDirectory = false
693
+ importItemMap = {}
694
+ source_files_array.each {|source_files|
695
+ source_files_path = "#{moduleItem.modulePath}/#{source_files}"
696
+ Dir[source_files_path].select{|filePath|
697
+ ModuleCheck.updateImportItemMap(filePath, moduleItem, importItemMap)
698
+ checkCount += 1
699
+ if File.directory?(filePath)
700
+ checkDirectory = true
701
+ end
702
+ }
703
+ }
704
+
705
+ # 检测到文件夹
706
+ if checkDirectory
707
+ puts "⚠️ #{moduleItem.moduleName}解析到文件夹, 请检查s.source_files的写法(注意加文件后缀,例如*.{h,m})"
708
+ end
709
+
710
+ if importItemMap.size == 0 and dependencies.size == 0
711
+ next
712
+ end
713
+
714
+ loss = []
715
+
716
+ # dependencies指的是根据组件podspec的写法 解析出来的依赖库
717
+ # importItemMap指的是根据代码文件解析出来的依赖库
718
+ importItemMap.each_key {|key|
719
+ if installedDeps.include?(key) and key != moduleItem.moduleName
720
+ didLoss = true
721
+ dependencies.each { |dep|
722
+ if dep.include?("/") #处理子仓库的形式
723
+ didLoss = dep.start_with?(key) ? false : true
724
+ else
725
+ didLoss = dep == key ? false : true
726
+ end
727
+ if !didLoss #没有丢失 就结束
728
+ break
729
+ end
730
+ }
731
+
732
+ if didLoss
733
+ loss << "s.dependency '#{key}'"
734
+ end
735
+
736
+ end
737
+ }
738
+
739
+ if loss.size >0
740
+ puts "⚠️ [#{moduleItem.moduleName}] 请到#{moduleItem.moduleName}.podspec文件中添加缺少的依赖库, 如下:"
741
+ puts loss
742
+ puts "\n"
743
+ end
744
+
745
+ unuse = []
746
+ dependencies.each {|dep|
747
+ if not importItemMap.keys.include?(dep) and podspec_dependencies.include?(dep)
748
+ unuse << "# s.dependency '#{dep}'"
749
+ end
750
+ }
751
+ if unuse.size >0
752
+ puts "⚠️ [#{moduleItem.moduleName}] 请到#{moduleItem.moduleName}.podspec文件中移除未使用的依赖库, 如下:"
753
+ puts unuse
754
+ puts "\n"
755
+ end
756
+ }
757
+
758
+ duration = ((Time.now.to_f * 1000).to_i - start)*0.001
759
+ puts "依赖库检查完毕! 共检查#{modules.size.to_s}个组件 #{checkCount}个文件 耗时:#{duration.round(2)}秒"
760
+ end
761
+
762
+ # 获取某个文件内对应类型的import "XXX.h"
763
+ def ModuleCheck.updateImportDotItemMap(filePath, moduleItem, importItemMap)
764
+ if File.directory?(filePath)
765
+ return
766
+ end
767
+
768
+ resut = []
769
+ lineArr = IO.readlines(filePath)
770
+ lineArr.each_with_index { |line, index|
771
+ unless line.start_with?("#import \"")
772
+ next
773
+ end
774
+
775
+ lineSplitArr = line.split("\"")
776
+ if lineSplitArr.size <= 1
777
+ next
778
+ end
779
+
780
+ importName = "#{lineSplitArr[0]}\"#{lineSplitArr[1]}\"" # import "XXX.h" 或import "XXX.AAA.h" 防止后边有其他内容
781
+
782
+ podFile = importName
783
+ podFile = podFile.gsub("#import \"", "")
784
+ podFile = podFile.gsub("\"", "") # 变成 XX.h
785
+
786
+ # 已经包含
787
+ if importItemMap[podFile]
788
+ next
789
+ end
790
+
791
+ podFile_temp = podFile
792
+ podFile_temp = podFile_temp.gsub(".h", "")
793
+ podFile_temp = podFile_temp.gsub(".h", "") # 变成 XX
794
+
795
+ fileName = File.basename(filePath)
796
+ lineIndex = index+1
797
+
798
+ importItemMap[podFile] = ImportItem.new(importName, moduleItem, fileName, lineIndex, podFile)
799
+
800
+ }
801
+ end
802
+
803
+ # 获取某个文件内对应类型的import <XX/XX>
804
+ def ModuleCheck.updateImportItemMap(filePath, moduleItem, importItemMap)
805
+ if File.directory?(filePath)
806
+ return
807
+ end
808
+
809
+ resut = []
810
+ lineArr = IO.readlines(filePath)
811
+ lineArr.each_with_index { |line, index|
812
+ unless line.start_with?("#import <")
813
+ next
814
+ end
815
+
816
+ lineSplitArr = line.split(">")
817
+ if lineSplitArr.size == 0
818
+ next
819
+ end
820
+
821
+ importName = lineSplitArr[0]+">" # #import <XX/XX.h> 或者 #import <XX.h> 防止后边有其他的内容
822
+ importArr = importName.split("/")
823
+ if lineSplitArr.size == 0
824
+ next
825
+ end
826
+
827
+ pod = importArr[0].gsub("#import <", "")
828
+ pod = pod.gsub(".h>", "")
829
+ pod = pod.gsub(".H>", "")
830
+
831
+ # 已经包含
832
+ if importItemMap[pod]
833
+ next
834
+ end
835
+
836
+ # 系统级依赖库
837
+ systemDeps = ModuleCheck.systemDeps()
838
+ if systemDeps.include?(pod)
839
+ next
840
+ end
841
+
842
+ fileName = File.basename(filePath)
843
+ lineIndex = index+1
844
+
845
+ importItemMap[pod] = ImportItem.new(importName, moduleItem, fileName, lineIndex, pod)
846
+
847
+ }
848
+ end
849
+
850
+ # 获取Pods和本地依赖库的 组件名和包含的头文件列表
851
+ def ModuleCheck.getModuleHeaders(modules)
852
+ resMap = {}
853
+
854
+ # Pods文件夹下的
855
+ pods_dir = FileUtils.pwd() + "/Pods"
856
+ Dir.foreach(pods_dir) { |dirName|
857
+ if dirName.include?(".") or dirName == "Target Support Files" or dirName == "Local Podspecs" or dirName == "Headers"
858
+ next
859
+ end
860
+
861
+ moduleName = File.basename(dirName)
862
+ modulePath = pods_dir + "/" + moduleName
863
+
864
+ res = ModuleCheck.getModuleOnHeader(modulePath, moduleName)
865
+ res.each {|key, value|
866
+ resMap[key] = value
867
+ }
868
+ }
869
+
870
+ # 本地路径下的
871
+ modules.each { |moduleItem|
872
+ res = ModuleCheck.getModuleOnHeader(moduleItem.modulePath, moduleItem.moduleName)
873
+ res.each {|key, value|
874
+ resMap[key] = value
875
+ }
876
+ }
877
+
878
+ return resMap
879
+ end
880
+
881
+ # 找出某个路径下所有的.h .hpp文件
882
+ def ModuleCheck.getModuleOnHeader(path, moduleName)
883
+ res = {}
884
+ path = path + +"/**/*.{h,hpp}"
885
+ fineNameArray = []
886
+ Dir[path].select{|filePath|
887
+ res[File.basename(filePath)] = moduleName
888
+ }
889
+
890
+ return res
891
+ end
892
+
893
+ # 系统级的依赖库
894
+ def ModuleCheck.checkNameInLig(name)
895
+ name = name.gsub(".h", "")
896
+ return $libLibarys.include?(name)
897
+ end
898
+
899
+ # 系统级的依赖库
900
+ def ModuleCheck.systemDeps()
901
+ return $libLibarys + $frameworks
902
+ end
903
+
904
+ # 获取已安装的依赖库
905
+ def ModuleCheck.getInstalledDeps(lock_dependencies)
906
+ installedDeps = lock_dependencies["SPEC CHECKSUMS"] ? lock_dependencies["SPEC CHECKSUMS"].keys : {}
907
+ return installedDeps
908
+ end
909
+
910
+ # 获取某个库的所有依赖和源码文件路径
911
+ def ModuleCheck.getDepdencyInModule(moduleItem, lockContent, shouldGetDependencies)
912
+
913
+ podspecName = "#{moduleItem.moduleName}.podspec"
914
+ podspecPath = File.expand_path("#{moduleItem.modulePath}/#{podspecName}")
915
+
916
+ podspecJsonName = "#{podspecName}.json"
917
+ podspecJsonPath = File.expand_path("#{moduleItem.modulePath}/#{podspecJsonName}")
918
+
919
+ podspec_content = nil
920
+
921
+ if File.exist?(podspecJsonPath)
922
+ json = File.read(podspecJsonPath)
923
+ podspec_content = JSON.parse(json)
924
+ elsif File.exist?(podspecPath)
925
+ json = ModuleCheck.getPodspecContent(podspecPath)
926
+ podspec_content = JSON.parse(json)
927
+ end
928
+
929
+ source_files = []
930
+ if podspec_content["subspecs"]
931
+ subspecs = podspec_content["subspecs"]
932
+ subspecs.each {|subspec|
933
+ subModuleName = "#{moduleItem.moduleName}/#{subspec["name"]}"
934
+ subModuleIsUse = ModuleCheck.getSubModuleIsUse(subModuleName, lockContent)
935
+ # puts "#{subModuleName}是否被依赖#{subModuleIsUse.to_s}"
936
+ subspecs_source_files = subspec["source_files"]
937
+ if subModuleIsUse and subspecs_source_files
938
+ source_files << subspecs_source_files
939
+ end
940
+ }
941
+ end
942
+ if podspec_content["source_files"]
943
+ source_files << podspec_content["source_files"]
944
+ end
945
+
946
+ # puts "source_files: #{source_files}"
947
+
948
+ podspec_dependencies = podspec_content["dependencies"] ? podspec_content["dependencies"].keys : {}
949
+ dependencies = Array.new
950
+
951
+ if shouldGetDependencies #耗时操作 依赖库越多越耗时
952
+ lock_dependencies = lockContent["PODS"]
953
+ ModuleCheck.getDep(lock_dependencies, moduleItem.moduleName, dependencies) #此处依赖库越多 越是耗时操作
954
+ end
955
+
956
+ return {"dependencies" => dependencies, "source_files" => source_files, "podspec_dependencies" => podspec_dependencies}
957
+ end
958
+
959
+ # 根据podspec路径把podspec转为json
960
+ def ModuleCheck.getPodspecContent(podspecPath)
961
+
962
+ spec_contents = File.open(podspecPath, 'r:utf-8', &:read)
963
+ if spec_contents.respond_to?(:encoding) && spec_contents.encoding.name != 'UTF-8'
964
+ spec_contents.encode!('UTF-8')
965
+ end
966
+
967
+ path = Pathname.new(podspecPath)
968
+
969
+ spec = nil
970
+ Dir.chdir(path.parent.directory? ? path.parent : Dir.pwd) do
971
+ spec = ::Pod._eval_podspec(spec_contents, path)
972
+ end
973
+ return spec.to_json
974
+ end
975
+
976
+ # 根据podflie.lock里内容递归获取某个库的依赖库
977
+ def ModuleCheck.getDep(dependencies, pod_name, allDeps)
978
+
979
+ dependencies.each { |item|
980
+ unless item.class == Hash then
981
+ next
982
+ end
983
+
984
+ item.each {|key, value|
985
+ item = key.split(" (")[0]
986
+ item = item.split("/")[0] #解决子仓库的问题
987
+ if item == pod_name then
988
+ value.each { |dep|
989
+ dep = dep.split(" (")[0]
990
+
991
+ if pod_name != dep and not allDeps.include? (dep) then
992
+ allDeps << dep
993
+ end
994
+ ModuleCheck.getDep(dependencies, dep, allDeps)
995
+ }
996
+ end
997
+ }
998
+ }
999
+ return allDeps
1000
+ end
1001
+
1002
+ # 获取子模块是否被依赖, 子模块格式 XXX/AAA
1003
+ def ModuleCheck.getSubModuleIsUse(subModuleName, lockContent)
1004
+ use = false
1005
+ dependencies = lockContent['PODS']
1006
+
1007
+ dependencies.each { |item|
1008
+ dependency = item
1009
+ if item.class == Hash and item.keys.size > 0
1010
+ dependency = item.keys[0]
1011
+ end
1012
+
1013
+ if not dependency.include?("/")
1014
+ next
1015
+ end
1016
+ if dependency.start_with?(subModuleName)
1017
+ use = true
1018
+ end
1019
+ }
1020
+
1021
+ return use
1022
+ end
1023
+
1024
+ # 获取podfile.lock里所有的本地依赖
1025
+ def ModuleCheck.getCheckModules(lockContent)
1026
+ result = []
1027
+ local_dependencies = lockContent['EXTERNAL SOURCES']
1028
+ unless local_dependencies then
1029
+ return result
1030
+ end
1031
+
1032
+ local_dependencies.each { |item|
1033
+ if item.size >=2
1034
+ moduleName = item[0]
1035
+ modulePath = item[1]
1036
+ item[1].each_value {|obj|
1037
+ modulePath = obj
1038
+ break
1039
+ }
1040
+ modulePath = File.expand_path(modulePath)
1041
+ result << ModuleItem.new(moduleName, modulePath)
1042
+ end
1043
+ }
1044
+ return result
1045
+ end
1046
+
1047
+ # 获取 白名单检查的import
1048
+ def ModuleCheck.getImportWhiteList()
1049
+ whiteListName = "importCheckWhite.txt"
1050
+
1051
+ unless File.exist?(whiteListName) then
1052
+ ModuleCheck.generateWhiteImportList(whiteListName)
1053
+ puts "\n⛔️⛔️⛔️ import规范检查白名单文件: #{whiteListName}"
1054
+ return []
1055
+ end
1056
+
1057
+ array = []
1058
+ lineArr = IO.readlines(whiteListName)
1059
+ lineArr.each { |line|
1060
+ unless line.start_with?("#import \"")
1061
+ next
1062
+ end
1063
+ array << line.gsub("\n","")
1064
+ }
1065
+ return array
1066
+ end
1067
+
1068
+ def ModuleCheck.generateWhiteImportList(whiteListName)
1069
+
1070
+ content = "import不规范检查, 这里配置白名单, 每个一行(加入白名单的不会被检查是否规范)。 示例
1071
+ #import \"libxx.h\"
1072
+ #import \"libxxx.h\"
1073
+ -----------请在下边按示例加白名单---------
1074
+ "
1075
+ File.open(whiteListName, 'w') { |file|
1076
+ file.write(content)
1077
+ }
1078
+ end
1079
+
1080
+ # 读取podfile.lock内功
1081
+ def ModuleCheck.getPodfileLock()
1082
+ YAML.load_file($podfile_lock)
1083
+ end
1084
+ end
1085
+
1086
+ module ImportCheck
1087
+ class Module
1088
+ attr_reader :moduleName, :modulePath, :fileNameArray, :filePathArray
1089
+ def initialize(moduleName, modulePath, fileNameArray, filePathArray)
1090
+ @moduleName = moduleName
1091
+ @modulePath = modulePath
1092
+ @fileNameArray = fileNameArray
1093
+ @filePathArray = filePathArray
1094
+ end
1095
+ end
1096
+
1097
+ class Import
1098
+ attr_reader :content, :importName, :moduleItem, :fileName, :lineIndex, :pod
1099
+ def initialize(content, importName, moduleItem, fileName, lineIndex, pod)
1100
+ @content = content
1101
+ @importName = importName
1102
+ @moduleItem = moduleItem
1103
+ @fileName = fileName
1104
+ @lineIndex = lineIndex
1105
+ @pod = pod
1106
+ end
1107
+ def setPod=(pod)
1108
+ @pod = pod
1109
+ end
1110
+ end
1111
+
1112
+
1113
+
1114
+ def ImportCheck.check()
1115
+ puts "import规范检查中..."
1116
+ start = (Time.now.to_f * 1000).to_i
1117
+
1118
+ # 需要检查的模块
1119
+ checkModules = []
1120
+ ImportCheck.findCheckModules(checkModules, $podfile_module)
1121
+ ImportCheck.findCheckModules(checkModules, $podfile_local)
1122
+
1123
+ # 白名单
1124
+ whiteListName = "importCheckWhite.txt"
1125
+ whiteList = ImportCheck.WhiteList(whiteListName)
1126
+
1127
+ # 有问题的import
1128
+ badImportArray = []
1129
+
1130
+ # 计数
1131
+ checkCount = 0
1132
+ checkModules.each { |moduleItem|
1133
+ modulePath = moduleItem.modulePath
1134
+
1135
+ # 需要检查的文件
1136
+ filePathArray = []
1137
+ fileNameArray = []
1138
+ ImportCheck.findCodeFile(modulePath, filePathArray, fileNameArray, false)
1139
+
1140
+ filePathArray.each { |filePath|
1141
+ ImportCheck.findBadQuoteImport(filePath, fileNameArray, badImportArray, moduleItem, whiteList)
1142
+ }
1143
+ checkCount += fileNameArray.size
1144
+ }
1145
+
1146
+ allModuleArray = ImportCheck.findModules()
1147
+ allModuleArray = allModuleArray + checkModules
1148
+
1149
+ tiplog = false
1150
+ badImportArray.collect { |import|
1151
+ moduleName = ImportCheck.findPodModule(allModuleArray, import.importName)
1152
+ import.setPod = moduleName
1153
+ result = moduleName ? "#import <#{moduleName}/#{import.importName}>" : "<>形式导入"
1154
+ puts "⚠️ <#{import.moduleItem.moduleName}> #{import.fileName} #{import.lineIndex}行 #{import.content} => #{result}"
1155
+ tiplog = true
1156
+ }
1157
+
1158
+ # 生成白名单文件
1159
+ ImportCheck.generateWhite(whiteListName)
1160
+
1161
+ if tiplog then
1162
+ puts "\nimport规范检查白名单文件: #{whiteListName}"
1163
+ end
1164
+
1165
+ duration = ((Time.now.to_f * 1000).to_i - start)*0.001
1166
+ puts "import规范检查完毕! 共检查#{checkModules.size.to_s}个组件 #{checkCount}个文件 耗时:#{duration.round(2)}秒"
1167
+ end
1168
+
1169
+ def ImportCheck.WhiteList(whiteListName)
1170
+ unless File.exist?(whiteListName) then
1171
+ return []
1172
+ end
1173
+
1174
+ array = []
1175
+ lineArr = IO.readlines(whiteListName)
1176
+ lineArr.each { |line|
1177
+ unless line.start_with?("#import \"")
1178
+ next
1179
+ end
1180
+ array << line
1181
+ }
1182
+ return array
1183
+ end
1184
+
1185
+ def ImportCheck.generateWhite(whiteListName)
1186
+ if File.exist?(whiteListName) then
1187
+ return
1188
+ end
1189
+
1190
+ content = "import不规范检查, 这里配置白名单, 每个一行(加入白名单的不会被检查是否规范)。 示例
1191
+ #import \"libxx.h\"
1192
+ #import \"libxxx.h\"
1193
+ -----------请在下边按示例加白名单---------
1194
+ "
1195
+ File.open(whiteListName, 'w') { |file|
1196
+ file.write(content)
1197
+ }
1198
+ end
1199
+
1200
+ def ImportCheck.findPodModule(moduleArray, importName)
1201
+ moduleArray.each { |item|
1202
+ item.fileNameArray.each { |fileName|
1203
+ if fileName == importName
1204
+ return item.moduleName
1205
+ end
1206
+ }
1207
+ }
1208
+ return nil
1209
+ end
1210
+
1211
+ def ImportCheck.findModules()
1212
+ pods_dir = FileUtils.pwd() + "/Pods"
1213
+
1214
+ moduleArray = []
1215
+
1216
+ Dir.foreach(pods_dir) { |moduleName|
1217
+ if moduleName.include?(".") or moduleName == "Target Support Files" or moduleName == "Local Podspecs" or moduleName == "Headers"
1218
+ next
1219
+ end
1220
+ modulePath = pods_dir + "/" + moduleName
1221
+ filePathArray = []
1222
+ fileNameArray = []
1223
+ ImportCheck.findCodeFile(modulePath, filePathArray, fileNameArray, true)
1224
+ moduleArray << Module.new(moduleName, modulePath, fileNameArray, filePathArray)
1225
+ }
1226
+
1227
+ return moduleArray
1228
+ end
1229
+
1230
+ # 根据路径获取所有的代码文件
1231
+ def ImportCheck.findCodeFile(des_path, filePathArray, fileNameArray, onlyDotH)
1232
+
1233
+ if not File.directory?(des_path)
1234
+ puts "非文件夹, 请检查: #{des_path}"
1235
+ return
1236
+ end
1237
+
1238
+ Dir.foreach(des_path) { |fileName|
1239
+ sub_path = des_path + "/" + fileName
1240
+ if fileName.start_with?(".")
1241
+ next
1242
+ end
1243
+ if File.directory?(sub_path)
1244
+ ImportCheck.findCodeFile(sub_path, filePathArray, fileNameArray, onlyDotH)
1245
+ else
1246
+
1247
+ onlyDotHCheck = (fileName.end_with?(".h") or fileName.end_with?(".pch"))
1248
+ notOnlyDotHCheck = (fileName.end_with?(".h") or
1249
+ fileName.end_with?(".m") or
1250
+ fileName.end_with?(".pch") or
1251
+ fileName.end_with?(".mm") or
1252
+ fileName.end_with?(".hpp") or
1253
+ fileName.end_with?(".cpp") or
1254
+ fileName.end_with?(".c") or
1255
+ fileName.end_with?(".cc"))
1256
+ isCodeFile = onlyDotH ? onlyDotHCheck : notOnlyDotHCheck
1257
+ if isCodeFile
1258
+ filePath = des_path + "/" + fileName
1259
+ filePathArray << filePath
1260
+ fileNameArray << fileName
1261
+ end
1262
+ end
1263
+ }
1264
+ end
1265
+
1266
+ # 查找不在当前模块内的 #import "xx"
1267
+ def ImportCheck.findBadQuoteImport(filePath, fileNameArray, badImportArray, moduleItem, whiteList)
1268
+ lineArr = IO.readlines(filePath)
1269
+ lineArr.each_with_index { |line, index|
1270
+ unless line.start_with?("#import \"")
1271
+ next
1272
+ end
1273
+
1274
+ lineSplitArr = line.split("\"")
1275
+ if lineSplitArr.size < 2
1276
+ next
1277
+ end
1278
+
1279
+ content = line.gsub("\n", "")
1280
+ importName = lineSplitArr.size >=2 ? lineSplitArr[1].gsub("\n", "") : content
1281
+
1282
+ if ImportCheck.importInWihte(importName, whiteList)
1283
+ next
1284
+ end
1285
+
1286
+ if fileNameArray.include?(importName)
1287
+ next
1288
+ end
1289
+
1290
+ fileName = File.basename(filePath)
1291
+ lineIndex = index+1
1292
+ pod = nil
1293
+
1294
+ badImportArray << Import.new(content, importName, moduleItem, fileName, lineIndex, pod)
1295
+ }
1296
+ end
1297
+
1298
+ def ImportCheck.importInWihte(importName, whiteList)
1299
+ whiteList.each {|whiteItem|
1300
+ if whiteItem.include?(importName)
1301
+ return true
1302
+ end
1303
+ }
1304
+ return false
1305
+ end
1306
+
1307
+ def ImportCheck.findCheckModules(checkModules, yaml_module)
1308
+
1309
+ unless File.exist?(yaml_module)
1310
+ return
1311
+ end
1312
+
1313
+ # 内容读取
1314
+ dependencies_local = YAML.load_file(yaml_module)
1315
+
1316
+ unless dependencies_local.class == Hash
1317
+ return
1318
+ end
1319
+
1320
+ dependencies = dependencies_local['PODS']
1321
+ unless dependencies
1322
+ return
1323
+ end
1324
+
1325
+ dependencies.each { |dependency|
1326
+ dependencyPath = dependency["path"]
1327
+ if dependencyPath and dependencyPath != "null" and dependencyPath.gsub(" ", "").length>0 then
1328
+ moduleName = dependency["pod"]
1329
+ modulePath = PathUtil.expandPath(dependencyPath)
1330
+ fileNameArray = []
1331
+ filePathArray = []
1332
+ ImportCheck.findCodeFile(modulePath, filePathArray, fileNameArray, true)
1333
+ checkModules << Module.new(moduleName, modulePath, fileNameArray, filePathArray)
1334
+ end
1335
+ }
1336
+
1337
+ end
1338
+
1339
+ end
1340
+
1341
+ class PathUtil
1342
+
1343
+ def PathUtil.expandPath(des_path)
1344
+ return File.expand_path(des_path).gsub("\n", "")
1345
+ end
1346
+
1347
+ def PathUtil.replaceStr(file_path, findstr, replacestr)
1348
+ unless File.exist?(file_path)
1349
+ return
1350
+ end
1351
+
1352
+ FileUtils.chmod("+w", file_path)
1353
+ text = File.read(file_path)
1354
+ replace = text.gsub(findstr, replacestr)
1355
+ if text != replace
1356
+ File.open(file_path, "w") { |file| file.puts replace }
1357
+ STDOUT.flush
1358
+ file_name = File.basename(file_path)
1359
+ puts "代码替换: #{file_name} (#{findstr} => #{replacestr})"
1360
+ end
1361
+ end
1362
+
1363
+ end
1364
+
1365
+ module SpecialDeal
1366
+ def SpecialDeal.FBRetainCycleDetector()
1367
+ PathUtil.replaceStr("Pods/FBRetainCycleDetector/FBRetainCycleDetector/Layout/Classes/FBClassStrongLayout.mm", "layoutCache[currentClass] = ivars;", "layoutCache[(id<NSCopying>)currentClass] = ivars;")
1368
+ PathUtil.replaceStr("Pods/FBRetainCycleDetector/fishhook/fishhook.c", "indirect_symbol_bindings[i] = cur->rebindings[j].replacement;", "if(i < (sizeof(indirect_symbol_bindings) /sizeof(indirect_symbol_bindings[0]))) {indirect_symbol_bindings[i]=cur->rebindings[j].replacement;}")
1369
+
1370
+ newStr = "#import <objc/runtime.h>
1371
+ #import <malloc/malloc.h>"
1372
+ PathUtil.replaceStr("Pods/FBRetainCycleDetector/FBRetainCycleDetector/Graph/FBObjectiveCGraphElement.mm", "#import <objc/runtime.h>", newStr)
1373
+
1374
+ newStr = "malloc_zone_t *zone = malloc_zone_from_ptr((__bridge void *)object); if (zone) { Class aCls=object_getClass(object);"
1375
+ PathUtil.replaceStr("Pods/FBRetainCycleDetector/FBRetainCycleDetector/Graph/FBObjectiveCGraphElement.mm", "Class aCls = object_getClass(object);", newStr)
1376
+
1377
+ newStr = "}
1378
+ # endif"
1379
+ PathUtil.replaceStr("Pods/FBRetainCycleDetector/FBRetainCycleDetector/Graph/FBObjectiveCGraphElement.mm", "#endif", newStr)
1380
+
1381
+ end
1382
+
1383
+ def SpecialDeal.test
1384
+ res = CheckDep::Dep.check
1385
+ puts res
1386
+
1387
+ path = "/Users/wangshuaipeng/BSCode/podfileDepDemo/Pods/Pods.xcodeproj"
1388
+ project = Xcodeproj::Project.open(path)
1389
+ project.groups.each { |group|
1390
+ if group.name != "Development Pods"
1391
+ next
1392
+ end
1393
+ group.children.each {|child|
1394
+ puts child.children
1395
+ }
1396
+ }
1397
+ end
1398
+
1399
+ end
1400
+
1401
+ END {
1402
+ # SpecialDeal.test
1403
+ # SpecialDeal.FBRetainCycleDetector()
1404
+ # ModuleCheck.checkModuleDep()
1405
+ ModuleCheck.checkImport()
1406
+ # PodfileDep.logIndirectDependencies()
1407
+ # PodfileDep.logUnusedDependencies()
1408
+ }