cocoapods-tt 0.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.
Files changed (124) hide show
  1. checksums.yaml +7 -0
  2. data/lib/cocoapods-tt/command/native/install.rb +56 -0
  3. data/lib/cocoapods-tt/command/native/update.rb +157 -0
  4. data/lib/cocoapods-tt/command/tt/make.rb +92 -0
  5. data/lib/cocoapods-tt/command/tt.rb +115 -0
  6. data/lib/cocoapods-tt/command.rb +1 -0
  7. data/lib/cocoapods-tt/gem_version.rb +3 -0
  8. data/lib/cocoapods-tt/native/command.rb +185 -0
  9. data/lib/cocoapods-tt/native/config.rb +366 -0
  10. data/lib/cocoapods-tt/native/core_overrides.rb +1 -0
  11. data/lib/cocoapods-tt/native/downloader/cache.rb +322 -0
  12. data/lib/cocoapods-tt/native/downloader/request.rb +86 -0
  13. data/lib/cocoapods-tt/native/downloader/response.rb +16 -0
  14. data/lib/cocoapods-tt/native/downloader.rb +192 -0
  15. data/lib/cocoapods-tt/native/executable.rb +247 -0
  16. data/lib/cocoapods-tt/native/external_sources/abstract_external_source.rb +205 -0
  17. data/lib/cocoapods-tt/native/external_sources/downloader_source.rb +30 -0
  18. data/lib/cocoapods-tt/native/external_sources/path_source.rb +55 -0
  19. data/lib/cocoapods-tt/native/external_sources/podspec_source.rb +54 -0
  20. data/lib/cocoapods-tt/native/external_sources.rb +57 -0
  21. data/lib/cocoapods-tt/native/gem_version.rb +5 -0
  22. data/lib/cocoapods-tt/native/generator/acknowledgements/markdown.rb +44 -0
  23. data/lib/cocoapods-tt/native/generator/acknowledgements/plist.rb +94 -0
  24. data/lib/cocoapods-tt/native/generator/acknowledgements.rb +107 -0
  25. data/lib/cocoapods-tt/native/generator/app_target_helper.rb +363 -0
  26. data/lib/cocoapods-tt/native/generator/bridge_support.rb +22 -0
  27. data/lib/cocoapods-tt/native/generator/constant.rb +19 -0
  28. data/lib/cocoapods-tt/native/generator/copy_dsyms_script.rb +56 -0
  29. data/lib/cocoapods-tt/native/generator/copy_resources_script.rb +223 -0
  30. data/lib/cocoapods-tt/native/generator/copy_xcframework_script.rb +227 -0
  31. data/lib/cocoapods-tt/native/generator/dummy_source.rb +31 -0
  32. data/lib/cocoapods-tt/native/generator/embed_frameworks_script.rb +196 -0
  33. data/lib/cocoapods-tt/native/generator/file_list.rb +39 -0
  34. data/lib/cocoapods-tt/native/generator/header.rb +103 -0
  35. data/lib/cocoapods-tt/native/generator/info_plist_file.rb +128 -0
  36. data/lib/cocoapods-tt/native/generator/module_map.rb +99 -0
  37. data/lib/cocoapods-tt/native/generator/prefix_header.rb +60 -0
  38. data/lib/cocoapods-tt/native/generator/script_phase_constants.rb +100 -0
  39. data/lib/cocoapods-tt/native/generator/umbrella_header.rb +46 -0
  40. data/lib/cocoapods-tt/native/hooks_manager.rb +132 -0
  41. data/lib/cocoapods-tt/native/installer/analyzer/analysis_result.rb +87 -0
  42. data/lib/cocoapods-tt/native/installer/analyzer/locking_dependency_analyzer.rb +103 -0
  43. data/lib/cocoapods-tt/native/installer/analyzer/pod_variant.rb +87 -0
  44. data/lib/cocoapods-tt/native/installer/analyzer/pod_variant_set.rb +175 -0
  45. data/lib/cocoapods-tt/native/installer/analyzer/podfile_dependency_cache.rb +55 -0
  46. data/lib/cocoapods-tt/native/installer/analyzer/sandbox_analyzer.rb +268 -0
  47. data/lib/cocoapods-tt/native/installer/analyzer/specs_state.rb +108 -0
  48. data/lib/cocoapods-tt/native/installer/analyzer/target_inspection_result.rb +58 -0
  49. data/lib/cocoapods-tt/native/installer/analyzer/target_inspector.rb +258 -0
  50. data/lib/cocoapods-tt/native/installer/analyzer.rb +1204 -0
  51. data/lib/cocoapods-tt/native/installer/base_install_hooks_context.rb +135 -0
  52. data/lib/cocoapods-tt/native/installer/installation_options.rb +195 -0
  53. data/lib/cocoapods-tt/native/installer/pod_source_installer.rb +224 -0
  54. data/lib/cocoapods-tt/native/installer/pod_source_preparer.rb +77 -0
  55. data/lib/cocoapods-tt/native/installer/podfile_validator.rb +168 -0
  56. data/lib/cocoapods-tt/native/installer/post_install_hooks_context.rb +9 -0
  57. data/lib/cocoapods-tt/native/installer/post_integrate_hooks_context.rb +9 -0
  58. data/lib/cocoapods-tt/native/installer/pre_install_hooks_context.rb +51 -0
  59. data/lib/cocoapods-tt/native/installer/pre_integrate_hooks_context.rb +9 -0
  60. data/lib/cocoapods-tt/native/installer/project_cache/project_cache.rb +11 -0
  61. data/lib/cocoapods-tt/native/installer/project_cache/project_cache_analysis_result.rb +53 -0
  62. data/lib/cocoapods-tt/native/installer/project_cache/project_cache_analyzer.rb +200 -0
  63. data/lib/cocoapods-tt/native/installer/project_cache/project_cache_version.rb +43 -0
  64. data/lib/cocoapods-tt/native/installer/project_cache/project_installation_cache.rb +103 -0
  65. data/lib/cocoapods-tt/native/installer/project_cache/project_metadata_cache.rb +73 -0
  66. data/lib/cocoapods-tt/native/installer/project_cache/target_cache_key.rb +176 -0
  67. data/lib/cocoapods-tt/native/installer/project_cache/target_metadata.rb +74 -0
  68. data/lib/cocoapods-tt/native/installer/sandbox_dir_cleaner.rb +105 -0
  69. data/lib/cocoapods-tt/native/installer/sandbox_header_paths_installer.rb +45 -0
  70. data/lib/cocoapods-tt/native/installer/source_provider_hooks_context.rb +34 -0
  71. data/lib/cocoapods-tt/native/installer/target_uuid_generator.rb +34 -0
  72. data/lib/cocoapods-tt/native/installer/user_project_integrator/target_integrator/xcconfig_integrator.rb +179 -0
  73. data/lib/cocoapods-tt/native/installer/user_project_integrator/target_integrator.rb +815 -0
  74. data/lib/cocoapods-tt/native/installer/user_project_integrator.rb +280 -0
  75. data/lib/cocoapods-tt/native/installer/xcode/multi_pods_project_generator.rb +82 -0
  76. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/aggregate_target_dependency_installer.rb +66 -0
  77. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/aggregate_target_installer.rb +192 -0
  78. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/app_host_installer.rb +154 -0
  79. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/file_references_installer.rb +329 -0
  80. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/pod_target_dependency_installer.rb +195 -0
  81. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/pod_target_installer.rb +1239 -0
  82. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/pod_target_integrator.rb +312 -0
  83. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/pods_project_writer.rb +90 -0
  84. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/project_generator.rb +120 -0
  85. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/target_installation_result.rb +140 -0
  86. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/target_installer.rb +257 -0
  87. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/target_installer_helper.rb +110 -0
  88. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator.rb +291 -0
  89. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator_result.rb +54 -0
  90. data/lib/cocoapods-tt/native/installer/xcode/single_pods_project_generator.rb +38 -0
  91. data/lib/cocoapods-tt/native/installer/xcode/target_validator.rb +170 -0
  92. data/lib/cocoapods-tt/native/installer/xcode.rb +11 -0
  93. data/lib/cocoapods-tt/native/installer.rb +1044 -0
  94. data/lib/cocoapods-tt/native/native_target_extension.rb +60 -0
  95. data/lib/cocoapods-tt/native/open-uri.rb +33 -0
  96. data/lib/cocoapods-tt/native/podfile.rb +13 -0
  97. data/lib/cocoapods-tt/native/project.rb +544 -0
  98. data/lib/cocoapods-tt/native/resolver/lazy_specification.rb +88 -0
  99. data/lib/cocoapods-tt/native/resolver/resolver_specification.rb +41 -0
  100. data/lib/cocoapods-tt/native/resolver.rb +600 -0
  101. data/lib/cocoapods-tt/native/sandbox/file_accessor.rb +532 -0
  102. data/lib/cocoapods-tt/native/sandbox/headers_store.rb +163 -0
  103. data/lib/cocoapods-tt/native/sandbox/path_list.rb +242 -0
  104. data/lib/cocoapods-tt/native/sandbox/pod_dir_cleaner.rb +71 -0
  105. data/lib/cocoapods-tt/native/sandbox/podspec_finder.rb +23 -0
  106. data/lib/cocoapods-tt/native/sandbox.rb +470 -0
  107. data/lib/cocoapods-tt/native/sources_manager.rb +221 -0
  108. data/lib/cocoapods-tt/native/target/aggregate_target.rb +558 -0
  109. data/lib/cocoapods-tt/native/target/build_settings.rb +1385 -0
  110. data/lib/cocoapods-tt/native/target/pod_target.rb +1168 -0
  111. data/lib/cocoapods-tt/native/target.rb +378 -0
  112. data/lib/cocoapods-tt/native/user_interface/error_report.rb +204 -0
  113. data/lib/cocoapods-tt/native/user_interface/inspector_reporter.rb +102 -0
  114. data/lib/cocoapods-tt/native/user_interface.rb +463 -0
  115. data/lib/cocoapods-tt/native/validator.rb +1170 -0
  116. data/lib/cocoapods-tt/native/version_metadata.rb +26 -0
  117. data/lib/cocoapods-tt/native/xcode/framework_paths.rb +54 -0
  118. data/lib/cocoapods-tt/native/xcode/linkage_analyzer.rb +22 -0
  119. data/lib/cocoapods-tt/native/xcode/xcframework/xcframework_slice.rb +138 -0
  120. data/lib/cocoapods-tt/native/xcode/xcframework.rb +99 -0
  121. data/lib/cocoapods-tt/native/xcode.rb +7 -0
  122. data/lib/cocoapods-tt.rb +1 -0
  123. data/lib/cocoapods_plugin.rb +17 -0
  124. metadata +193 -0
@@ -0,0 +1,366 @@
1
+ require 'active_support/multibyte/unicode'
2
+
3
+ module Pod
4
+ # Stores the global configuration of CocoaPods.
5
+ #
6
+ class Config
7
+ # The default settings for the configuration.
8
+ #
9
+ # Users can specify custom settings in `~/.cocoapods/config.yaml`.
10
+ # An example of the contents of this file might look like:
11
+ #
12
+ # ---
13
+ # skip_repo_update: true
14
+ # new_version_message: false
15
+ #
16
+ DEFAULTS = {
17
+ :verbose => false,
18
+ :silent => false,
19
+ :skip_download_cache => !ENV['COCOAPODS_SKIP_CACHE'].nil?,
20
+
21
+ :new_version_message => ENV['COCOAPODS_SKIP_UPDATE_MESSAGE'].nil?,
22
+
23
+ :cache_root => Pathname.new(Dir.home) + 'Library/Caches/CocoaPods',
24
+ }
25
+
26
+ # Applies the given changes to the config for the duration of the given
27
+ # block.
28
+ #
29
+ # @param [Hash<#to_sym,Object>] changes
30
+ # the changes to merge temporarily with the current config
31
+ #
32
+ # @yield [] is called while the changes are applied
33
+ #
34
+ def with_changes(changes)
35
+ old = {}
36
+ changes.keys.each do |key|
37
+ key = key.to_sym
38
+ old[key] = send(key) if respond_to?(key)
39
+ end
40
+ configure_with(changes)
41
+ yield if block_given?
42
+ ensure
43
+ configure_with(old)
44
+ end
45
+
46
+ public
47
+
48
+ #-------------------------------------------------------------------------#
49
+
50
+ # @!group UI
51
+
52
+ # @return [Bool] Whether CocoaPods should provide detailed output about the
53
+ # performed actions.
54
+ #
55
+ attr_accessor :verbose
56
+ alias_method :verbose?, :verbose
57
+
58
+ # @return [Bool] Whether CocoaPods should produce not output.
59
+ #
60
+ attr_accessor :silent
61
+ alias_method :silent?, :silent
62
+
63
+ # @return [Bool] Whether CocoaPods is allowed to run as root.
64
+ #
65
+ attr_accessor :allow_root
66
+ alias_method :allow_root?, :allow_root
67
+
68
+ # @return [Bool] Whether a message should be printed when a new version of
69
+ # CocoaPods is available.
70
+ #
71
+ attr_accessor :new_version_message
72
+ alias_method :new_version_message?, :new_version_message
73
+
74
+ #-------------------------------------------------------------------------#
75
+
76
+ # @!group Installation
77
+
78
+ # @return [Bool] Whether the installer should skip the download cache.
79
+ #
80
+ attr_accessor :skip_download_cache
81
+ alias_method :skip_download_cache?, :skip_download_cache
82
+
83
+ public
84
+
85
+ #-------------------------------------------------------------------------#
86
+
87
+ # @!group Cache
88
+
89
+ # @return [Pathname] The directory where CocoaPods should cache remote data
90
+ # and other expensive to compute information.
91
+ #
92
+ attr_accessor :cache_root
93
+
94
+ def cache_root
95
+ @cache_root.mkpath unless @cache_root.exist?
96
+ @cache_root
97
+ end
98
+
99
+ public
100
+
101
+ #-------------------------------------------------------------------------#
102
+
103
+ # @!group Initialization
104
+
105
+ def initialize(use_user_settings = true)
106
+ configure_with(DEFAULTS)
107
+
108
+ unless ENV['CP_HOME_DIR'].nil?
109
+ @cache_root = home_dir + 'cache'
110
+ end
111
+
112
+ if use_user_settings && user_settings_file.exist?
113
+ require 'yaml'
114
+ user_settings = YAML.load_file(user_settings_file)
115
+ configure_with(user_settings)
116
+ end
117
+
118
+ unless ENV['CP_CACHE_DIR'].nil?
119
+ @cache_root = Pathname.new(ENV['CP_CACHE_DIR']).expand_path
120
+ end
121
+ end
122
+
123
+ def verbose
124
+ @verbose && !silent
125
+ end
126
+
127
+ public
128
+
129
+ #-------------------------------------------------------------------------#
130
+
131
+ # @!group Paths
132
+
133
+ # @return [Pathname] the directory where repos, templates and configuration
134
+ # files are stored.
135
+ #
136
+ def home_dir
137
+ @home_dir ||= Pathname.new(ENV['CP_HOME_DIR'] || '~/.cocoapods').expand_path
138
+ end
139
+
140
+ # @return [Pathname] the directory where the CocoaPods sources are stored.
141
+ #
142
+ def repos_dir
143
+ @repos_dir ||= Pathname.new(ENV['CP_REPOS_DIR'] || (home_dir + 'repos')).expand_path
144
+ end
145
+
146
+ attr_writer :repos_dir
147
+
148
+ # @return [Source::Manager] the source manager for the spec repos in `repos_dir`
149
+ #
150
+ def sources_manager
151
+ return @sources_manager if @sources_manager && @sources_manager.repos_dir == repos_dir
152
+ @sources_manager = Source::Manager.new(repos_dir)
153
+ end
154
+
155
+ # @return [Pathname] the directory where the CocoaPods templates are stored.
156
+ #
157
+ def templates_dir
158
+ @templates_dir ||= Pathname.new(ENV['CP_TEMPLATES_DIR'] || (home_dir + 'templates')).expand_path
159
+ end
160
+
161
+ # @return [Pathname] the root of the CocoaPods installation where the
162
+ # Podfile is located.
163
+ #
164
+ def installation_root
165
+ @installation_root ||= begin
166
+ current_dir = Pathname.new(Dir.pwd.unicode_normalize(:nfkc))
167
+ current_path = current_dir
168
+ until current_path.root?
169
+ if podfile_path_in_dir(current_path)
170
+ installation_root = current_path
171
+ unless current_path == current_dir
172
+ UI.puts("[in #{current_path}]")
173
+ end
174
+ break
175
+ else
176
+ current_path = current_path.parent
177
+ end
178
+ end
179
+ installation_root || current_dir
180
+ end
181
+ end
182
+
183
+ attr_writer :installation_root
184
+ alias_method :project_root, :installation_root
185
+
186
+ # @return [Pathname] The root of the sandbox.
187
+ #
188
+ def sandbox_root
189
+ @sandbox_root ||= installation_root + 'Pods'
190
+ end
191
+
192
+ attr_writer :sandbox_root
193
+ alias_method :project_pods_root, :sandbox_root
194
+
195
+ # @return [Sandbox] The sandbox of the current project.
196
+ #
197
+ def sandbox
198
+ @sandbox ||= Sandbox.new(sandbox_root)
199
+ end
200
+
201
+ # @return [Podfile] The Podfile to use for the current execution.
202
+ # @return [Nil] If no Podfile is available.
203
+ #
204
+ def podfile
205
+ @podfile ||= Podfile.from_file(podfile_path) if podfile_path
206
+ end
207
+ attr_writer :podfile
208
+
209
+ # @return [Lockfile] The Lockfile to use for the current execution.
210
+ # @return [Nil] If no Lockfile is available.
211
+ #
212
+ def lockfile
213
+ @lockfile ||= Lockfile.from_file(lockfile_path) if lockfile_path
214
+ end
215
+
216
+ # Returns the path of the Podfile.
217
+ #
218
+ # @note The Podfile can be named either `CocoaPods.podfile.yaml`,
219
+ # `CocoaPods.podfile` or `Podfile`. The first two are preferred as
220
+ # they allow to specify an OS X UTI.
221
+ #
222
+ # @return [Pathname]
223
+ # @return [Nil]
224
+ #
225
+ def podfile_path
226
+ @podfile_path ||= podfile_path_in_dir(installation_root)
227
+ end
228
+
229
+ # Returns the path of the Lockfile.
230
+ #
231
+ # @note The Lockfile is named `Podfile.lock`.
232
+ #
233
+ def lockfile_path
234
+ @lockfile_path ||= installation_root + 'Podfile.lock'
235
+ end
236
+
237
+ # Returns the path of the default Podfile pods.
238
+ #
239
+ # @note The file is expected to be named Podfile.default
240
+ #
241
+ # @return [Pathname]
242
+ #
243
+ def default_podfile_path
244
+ @default_podfile_path ||= templates_dir + 'Podfile.default'
245
+ end
246
+
247
+ # Returns the path of the default Podfile test pods.
248
+ #
249
+ # @note The file is expected to be named Podfile.test
250
+ #
251
+ # @return [Pathname]
252
+ #
253
+ def default_test_podfile_path
254
+ @default_test_podfile_path ||= templates_dir + 'Podfile.test'
255
+ end
256
+
257
+ # @return [Pathname] The file to use to cache the search data.
258
+ #
259
+ def search_index_file
260
+ cache_root + 'search_index.json'
261
+ end
262
+
263
+ private
264
+
265
+ #-------------------------------------------------------------------------#
266
+
267
+ # @!group Private helpers
268
+
269
+ # @return [Pathname] The path of the file which contains the user settings.
270
+ #
271
+ def user_settings_file
272
+ home_dir + 'config.yaml'
273
+ end
274
+
275
+ # Sets the values of the attributes with the given hash.
276
+ #
277
+ # @param [Hash{String,Symbol => Object}] values_by_key
278
+ # The values of the attributes grouped by key.
279
+ #
280
+ # @return [void]
281
+ #
282
+ def configure_with(values_by_key)
283
+ return unless values_by_key
284
+ values_by_key.each do |key, value|
285
+ if key.to_sym == :cache_root
286
+ value = Pathname.new(value).expand_path
287
+ end
288
+ instance_variable_set("@#{key}", value)
289
+ end
290
+ end
291
+
292
+ # @return [Array<String>] The filenames that the Podfile can have ordered
293
+ # by priority.
294
+ #
295
+ PODFILE_NAMES = [
296
+ 'CocoaPods.podfile.yaml',
297
+ 'CocoaPods.podfile',
298
+ 'Podfile',
299
+ 'Podfile.rb',
300
+ ].freeze
301
+
302
+ public
303
+
304
+ # Returns the path of the Podfile in the given dir if any exists.
305
+ #
306
+ # @param [Pathname] dir
307
+ # The directory where to look for the Podfile.
308
+ #
309
+ # @return [Pathname] The path of the Podfile.
310
+ # @return [Nil] If not Podfile was found in the given dir
311
+ #
312
+ def podfile_path_in_dir(dir)
313
+ PODFILE_NAMES.each do |filename|
314
+ candidate = dir + filename
315
+ if candidate.file?
316
+ return candidate
317
+ end
318
+ end
319
+ nil
320
+ end
321
+
322
+ # Excludes the given dir from Time Machine backups.
323
+ #
324
+ # @param [Pathname] dir
325
+ # The directory to exclude from Time Machine backups.
326
+ #
327
+ # @return [void]
328
+ #
329
+ def exclude_from_backup(dir)
330
+ return if Gem.win_platform?
331
+ system('tmutil', 'addexclusion', dir.to_s, %i(out err) => File::NULL)
332
+ end
333
+
334
+ public
335
+
336
+ #-------------------------------------------------------------------------#
337
+
338
+ # @!group Singleton
339
+
340
+ # @return [Config] the current config instance creating one if needed.
341
+ #
342
+ def self.instance
343
+ @instance ||= new
344
+ end
345
+
346
+ # Sets the current config instance. If set to nil the config will be
347
+ # recreated when needed.
348
+ #
349
+ # @param [Config, Nil] the instance.
350
+ #
351
+ # @return [void]
352
+ #
353
+ class << self
354
+ attr_writer :instance
355
+ end
356
+
357
+ # Provides support for accessing the configuration instance in other
358
+ # scopes.
359
+ #
360
+ module Mixin
361
+ def config
362
+ Config.instance
363
+ end
364
+ end
365
+ end
366
+ end
@@ -0,0 +1 @@
1
+ require 'cocoapods/sources_manager'
@@ -0,0 +1,322 @@
1
+ require 'fileutils'
2
+ require 'tmpdir'
3
+
4
+ module Pod
5
+ module Downloader
6
+ # The class responsible for managing Pod downloads, transparently caching
7
+ # them in a cache directory.
8
+ #
9
+ class Cache
10
+ # @return [Pathname] The root directory where this cache store its
11
+ # downloads.
12
+ #
13
+ attr_reader :root
14
+
15
+ # Initialize a new instance
16
+ #
17
+ # @param [Pathname,String] root
18
+ # see {#root}
19
+ #
20
+ def initialize(root)
21
+ @root = Pathname(root)
22
+ ensure_matching_version
23
+ end
24
+
25
+ # Downloads the Pod from the given `request`
26
+ #
27
+ # @param [Request] request
28
+ # the request to be downloaded.
29
+ #
30
+ # @return [Response] the response from downloading `request`
31
+ #
32
+ def download_pod(request)
33
+ cached_pod(request) || uncached_pod(request)
34
+ rescue Informative
35
+ raise
36
+ rescue
37
+ UI.puts("\n[!] Error installing #{request.name}".red)
38
+ raise
39
+ end
40
+
41
+ # @return [Hash<String, Hash<Symbol, String>>]
42
+ # A hash whose keys are the pod name
43
+ # And values are a hash with the following keys:
44
+ # :spec_file : path to the spec file
45
+ # :name : name of the pod
46
+ # :version : pod version
47
+ # :release : boolean to tell if that's a release pod
48
+ # :slug : the slug path where the pod cache is located
49
+ #
50
+ def cache_descriptors_per_pod
51
+ specs_dir = root + 'Specs'
52
+ release_specs_dir = specs_dir + 'Release'
53
+ return {} unless specs_dir.exist?
54
+
55
+ spec_paths = specs_dir.find.select { |f| f.fnmatch('*.podspec.json') }
56
+ spec_paths.reduce({}) do |hash, spec_path|
57
+ spec = Specification.from_file(spec_path)
58
+ hash[spec.name] ||= []
59
+ is_release = spec_path.to_s.start_with?(release_specs_dir.to_s)
60
+ request = Downloader::Request.new(:spec => spec, :released => is_release)
61
+ hash[spec.name] << {
62
+ :spec_file => spec_path,
63
+ :name => spec.name,
64
+ :version => spec.version,
65
+ :release => is_release,
66
+ :slug => root + request.slug,
67
+ }
68
+ hash
69
+ end
70
+ end
71
+
72
+ # Convenience method for acquiring a shared lock to safely read from the
73
+ # cache. See `Cache.lock` for more details.
74
+ #
75
+ # @param [Pathname] location
76
+ # the path to require a lock for.
77
+ #
78
+ # @param [block] &block
79
+ # the block to execute inside the lock.
80
+ #
81
+ # @return [void]
82
+ #
83
+ def self.read_lock(location, &block)
84
+ Cache.lock(location, File::LOCK_SH, &block)
85
+ end
86
+
87
+ # Convenience method for acquiring an exclusive lock to safely write to
88
+ # the cache. See `Cache.lock` for more details.
89
+ #
90
+ # @param [Pathname] location
91
+ # the path to require a lock for.
92
+ #
93
+ # @param [block] &block
94
+ # the block to execute inside the lock.
95
+ #
96
+ # @return [void]
97
+ #
98
+ def self.write_lock(location, &block)
99
+ Cache.lock(location, File::LOCK_EX, &block)
100
+ end
101
+
102
+ # Creates a .lock file at `location`, aquires a lock of type
103
+ # `lock_type`, checks that it is valid, and executes passed block while
104
+ # holding on to that lock. Afterwards, the .lock file is deleted, which is
105
+ # why validation of the lock is necessary, as you might have a lock on a
106
+ # file that doesn't exist on the filesystem anymore.
107
+ #
108
+ # @param [Pathname] location
109
+ # the path to require a lock for.
110
+ #
111
+ # @param [locking_constant] lock_type
112
+ # the type of lock, either exclusive (File::LOCK_EX) or shared
113
+ # (File::LOCK_SH).
114
+ #
115
+ # @return [void]
116
+ #
117
+ def self.lock(location, lock_type)
118
+ raise ArgumentError, 'no block given' unless block_given?
119
+ lockfile = "#{location}.lock"
120
+ f = nil
121
+ loop do
122
+ f.close if f
123
+ f = File.open(lockfile, File::CREAT, 0o644)
124
+ f.flock(lock_type)
125
+ break if Cache.valid_lock?(f, lockfile)
126
+ end
127
+ begin
128
+ yield location
129
+ ensure
130
+ if lock_type == File::LOCK_SH
131
+ f.flock(File::LOCK_EX)
132
+ File.delete(lockfile) if Cache.valid_lock?(f, lockfile)
133
+ else
134
+ File.delete(lockfile)
135
+ end
136
+ f.close
137
+ end
138
+ end
139
+
140
+ # Checks that the lock is on a file that still exists on the filesystem.
141
+ #
142
+ # @param [File] file
143
+ # the actual file that we have a lock for.
144
+ #
145
+ # @param [String] filename
146
+ # the filename of the file that we have a lock for.
147
+ #
148
+ # @return [Boolean]
149
+ # true if `filename` still exists and is the same file as `file`
150
+ #
151
+ def self.valid_lock?(file, filename)
152
+ file.stat.ino == File.stat(filename).ino
153
+ rescue Errno::ENOENT
154
+ false
155
+ end
156
+
157
+ private
158
+
159
+ # Ensures the cache on disk was created with the same CocoaPods version as
160
+ # is currently running.
161
+ #
162
+ # @return [Void]
163
+ #
164
+ def ensure_matching_version
165
+ version_file = root + 'VERSION'
166
+ version = version_file.read.strip if version_file.file?
167
+
168
+ root.rmtree if version != Pod::VERSION && root.exist?
169
+ root.mkpath
170
+
171
+ version_file.open('w') { |f| f << Pod::VERSION }
172
+ end
173
+
174
+ # @param [Request] request
175
+ # the request to be downloaded.
176
+ #
177
+ # @param [Hash<Symbol,String>] slug_opts
178
+ # the download options that should be used in constructing the
179
+ # cache slug for this request.
180
+ #
181
+ # @return [Pathname] The path for the Pod downloaded from the given
182
+ # `request`.
183
+ #
184
+ def path_for_pod(request, slug_opts = {})
185
+ root + request.slug(**slug_opts)
186
+ end
187
+
188
+ # @param [Request] request
189
+ # the request to be downloaded.
190
+ #
191
+ # @param [Hash<Symbol,String>] slug_opts
192
+ # the download options that should be used in constructing the
193
+ # cache slug for this request.
194
+ #
195
+ # @return [Pathname] The path for the podspec downloaded from the given
196
+ # `request`.
197
+ #
198
+ def path_for_spec(request, slug_opts = {})
199
+ path = root + 'Specs' + request.slug(**slug_opts)
200
+ Pathname.new(path.to_path + '.podspec.json')
201
+ end
202
+
203
+ # @param [Request] request
204
+ # the request to be downloaded.
205
+ #
206
+ # @return [Response] The download response for the given `request` that
207
+ # was found in the download cache.
208
+ #
209
+ def cached_pod(request)
210
+ cached_spec = cached_spec(request)
211
+ path = path_for_pod(request)
212
+
213
+ return unless cached_spec && path.directory?
214
+ spec = request.spec || cached_spec
215
+ Response.new(path, spec, request.params)
216
+ end
217
+
218
+ # @param [Request] request
219
+ # the request to be downloaded.
220
+ #
221
+ # @return [Specification] The cached specification for the given
222
+ # `request`.
223
+ #
224
+ def cached_spec(request)
225
+ path = path_for_spec(request)
226
+ path.file? && Specification.from_file(path)
227
+ rescue JSON::ParserError
228
+ nil
229
+ end
230
+
231
+ # @param [Request] request
232
+ # the request to be downloaded.
233
+ #
234
+ # @return [Response] The download response for the given `request` that
235
+ # was not found in the download cache.
236
+ #
237
+ def uncached_pod(request)
238
+ in_tmpdir do |target|
239
+ result, podspecs = download(request, target)
240
+ result.location = nil
241
+
242
+ podspecs.each do |name, spec|
243
+ destination = path_for_pod(request, :name => name, :params => result.checkout_options)
244
+ copy_and_clean(target, destination, spec)
245
+ write_spec(spec, path_for_spec(request, :name => name, :params => result.checkout_options))
246
+ if request.name == name
247
+ result.location = destination
248
+ end
249
+ end
250
+
251
+ result
252
+ end
253
+ end
254
+
255
+ def download(request, target)
256
+ Downloader.download_request(request, target)
257
+ end
258
+
259
+ # Performs the given block inside a temporary directory,
260
+ # which is removed at the end of the block's scope.
261
+ #
262
+ # @return [Object] The return value of the given block
263
+ #
264
+ def in_tmpdir(&blk)
265
+ tmpdir = Pathname(Dir.mktmpdir)
266
+ blk.call(tmpdir)
267
+ ensure
268
+ FileUtils.remove_entry(tmpdir, :force => true) if tmpdir && tmpdir.exist?
269
+ end
270
+
271
+ # Copies the `source` directory to `destination`, cleaning the directory
272
+ # of any files unused by `spec`.
273
+ #
274
+ # @param [Pathname] source
275
+ #
276
+ # @param [Pathname] destination
277
+ #
278
+ # @param [Specification] spec
279
+ #
280
+ # @return [Void]
281
+ #
282
+ def copy_and_clean(source, destination, spec)
283
+ specs_by_platform = group_subspecs_by_platform(spec)
284
+ destination.parent.mkpath
285
+ Cache.write_lock(destination) do
286
+ FileUtils.rm_rf(destination)
287
+ FileUtils.cp_r(source, destination)
288
+ Pod::Installer::PodSourcePreparer.new(spec, destination).prepare!
289
+ Sandbox::PodDirCleaner.new(destination, specs_by_platform).clean!
290
+ end
291
+ end
292
+
293
+ def group_subspecs_by_platform(spec)
294
+ specs_by_platform = {}
295
+ [spec, *spec.recursive_subspecs].each do |ss|
296
+ ss.available_platforms.each do |platform|
297
+ specs_by_platform[platform] ||= []
298
+ specs_by_platform[platform] << ss
299
+ end
300
+ end
301
+ specs_by_platform
302
+ end
303
+
304
+ # Writes the given `spec` to the given `path`.
305
+ #
306
+ # @param [Specification] spec
307
+ # the specification to be written.
308
+ #
309
+ # @param [Pathname] path
310
+ # the path the specification is to be written to.
311
+ #
312
+ # @return [Void]
313
+ #
314
+ def write_spec(spec, path)
315
+ path.dirname.mkpath
316
+ Cache.write_lock(path) do
317
+ path.open('w') { |f| f.write spec.to_pretty_json }
318
+ end
319
+ end
320
+ end
321
+ end
322
+ end