podfileDep 1.1.4 → 2.0.1

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.
@@ -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
+ }