cocoapods-pack 1.0.0

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 (53) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rubocop.yml +43 -0
  4. data/Gemfile +8 -0
  5. data/Gemfile.lock +149 -0
  6. data/LICENSE +202 -0
  7. data/README.md +116 -0
  8. data/Rakefile +8 -0
  9. data/cocoapods-pack.gemspec +34 -0
  10. data/endToEndPackMySample.sh +13 -0
  11. data/lib/cocoapods-pack/command/pack.rb +497 -0
  12. data/lib/cocoapods-pack/command.rb +19 -0
  13. data/lib/cocoapods-pack/env_parser.rb +23 -0
  14. data/lib/cocoapods-pack/find_follow.rb +106 -0
  15. data/lib/cocoapods-pack/gem_version.rb +21 -0
  16. data/lib/cocoapods-pack/spec_generator.rb +154 -0
  17. data/lib/cocoapods-pack/xcode_builder.rb +193 -0
  18. data/lib/cocoapods-pack/zip_file_generator.rb +56 -0
  19. data/lib/cocoapods_pack.rb +19 -0
  20. data/lib/cocoapods_plugin.rb +19 -0
  21. data/local_pod.rb +7 -0
  22. data/packMySample.sh +8 -0
  23. data/samples/CLIConsumer/CLIConsumer/dummy.swift +1 -0
  24. data/samples/CLIConsumer/CLIConsumer/main.m +10 -0
  25. data/samples/CLIConsumer/CLIConsumer.xcodeproj/project.pbxproj +329 -0
  26. data/samples/CLIConsumer/CLIConsumer.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  27. data/samples/CLIConsumer/CLIConsumer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  28. data/samples/CLIConsumer/CLIConsumer.xcodeproj/xcshareddata/xcschemes/CLIConsumer.xcscheme +88 -0
  29. data/samples/CLIConsumer/CLIConsumer.xcworkspace/contents.xcworkspacedata +10 -0
  30. data/samples/CLIConsumer/CLIConsumer.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  31. data/samples/CLIConsumer/Podfile +9 -0
  32. data/samples/CLIConsumer/build.sh +13 -0
  33. data/samples/MySample/MIT +1 -0
  34. data/samples/MySample/MySample/Internal/MySample_Internal.h +1 -0
  35. data/samples/MySample/MySample/MyObjCInSwiftSample.swift +7 -0
  36. data/samples/MySample/MySample/MySample.h +7 -0
  37. data/samples/MySample/MySample/MySample.m +10 -0
  38. data/samples/MySample/MySample/MySwiftSample.swift +7 -0
  39. data/samples/MySample/MySample.podspec +45 -0
  40. data/samples/MySample/MySample.xcodeproj/project.pbxproj +379 -0
  41. data/samples/MySample/MySample.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  42. data/samples/MySample/MySampleTests/Info.plist +24 -0
  43. data/samples/MySample/python/a.py +1 -0
  44. data/samples/MySample/python/subpython/b.py +1 -0
  45. data/samples/MySample/resources/a.png +1 -0
  46. data/samples/MySample/resources/lets/go/nuts/b.png +1 -0
  47. data/samples/MySample/resources/lets/go/runit.png +0 -0
  48. data/spec/acceptance_spec.rb +22 -0
  49. data/spec/env_parser_spec.rb +18 -0
  50. data/spec/spec_generator_spec.rb +247 -0
  51. data/spec/spec_helper.rb +47 -0
  52. data/spec/xcode_builder_spec.rb +113 -0
  53. metadata +207 -0
@@ -0,0 +1,497 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright 2021 Square, Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'cocoapods-pack/xcode_builder'
20
+ require 'cocoapods-pack/spec_generator.rb'
21
+ require 'cocoapods-pack/find_follow'
22
+ require 'cocoapods-pack/zip_file_generator'
23
+ require 'English'
24
+ require 'set'
25
+
26
+ module Pod
27
+ class Command
28
+ class Pack < Command
29
+ include Pod::Config::Mixin
30
+ include FindFollow
31
+
32
+ LICENSE_GLOB_PATTERNS = Pod::Sandbox::FileAccessor::GLOB_PATTERNS[:license]
33
+ CONCRETE_TARGET_NAME = 'Bin'
34
+
35
+ XCArchive = Struct.new(:path, :podspec) do
36
+ def dsym_paths
37
+ Dir.glob(File.join(path, 'dSYMs', '*.dSYM'))
38
+ end
39
+
40
+ def bcsymbolmap_paths
41
+ Dir.glob(File.join(path, 'BCSymbolMaps', '*.bcsymbolmap'))
42
+ end
43
+
44
+ def modules_path
45
+ "#{framework_path}/Modules"
46
+ end
47
+
48
+ def framework_path
49
+ "#{path}/Products/Library/Frameworks/#{podspec.module_name}.framework"
50
+ end
51
+ end
52
+
53
+ self.summary = 'An xcframework and podspec generator.'
54
+ self.description = <<-DESC
55
+ Converts the provided `SOURCE` into a binary version with each platform packed as an `xcframework`.
56
+ The process includes installing a CocoaPods sandbox, building it for device and simulator using the 'Release'
57
+ configuration, zipping the output and generating a new podspec that uses the `ARTIFACT_REPO_URL` provided as the
58
+ source. The generated podspec is also validated.
59
+ DESC
60
+
61
+ self.arguments = [
62
+ CLAide::Argument.new('SOURCE', true),
63
+ CLAide::Argument.new('ARTIFACT_REPO_URL', true)
64
+ ]
65
+
66
+ def self.options
67
+ [
68
+ ['--use-static-frameworks', 'Produce a framework that wraps a static library from the source files. ' \
69
+ 'By default dynamic frameworks are used.'],
70
+ ['--generate-module-map', 'If specified, instead of using the default generated umbrella module map one ' \
71
+ 'will be generated based on the frameworks header dirs.'],
72
+ ['--allow-warnings', 'Lint validates even if warnings are present.'],
73
+ ['--repo-update', 'Force running `pod repo update` before install.'],
74
+ ['--out-dir', 'Optional directory to use to output results into. Defaults to current working directory.'],
75
+ ['--skip-validation', 'Skips linting the generated binary podspec.'],
76
+ ['--skip-platforms', 'Comma-delimited platforms to skip when creating a binary.'],
77
+ ['--xcodebuild-opts', 'Options to be passed through to xcodebuild.'],
78
+ ['--use-json', 'Use JSON for the generated binary podspec.'],
79
+ ['--sources=https://github.com/artsy/Specs,master', 'The sources from which to pull dependant pods ' \
80
+ '(defaults to all available repos). Multiple sources must be comma-delimited.']
81
+ ].concat(super)
82
+ end
83
+
84
+ def initialize(argv)
85
+ @podspec_path = argv.shift_argument
86
+ @artifact_repo_url = argv.shift_argument
87
+ @allow_warnings = argv.flag?('allow-warnings', false)
88
+ @repo_update = argv.flag?('repo-update', false)
89
+ @generate_module_map = argv.flag?('generate-module-map', false)
90
+ @use_static_frameworks = argv.flag?('use-static-frameworks', false)
91
+ @skip_validation = argv.flag?('skip-validation', false)
92
+ @xcodebuild_opts = argv.option('xcodebuild-opts')
93
+ @out_dir = argv.option('out-dir', Dir.getwd)
94
+ @skipped_platforms = argv.option('skip-platforms', '').split(',')
95
+ @source_urls = argv.option('sources', Config.instance.sources_manager.all.map(&:url).join(',')).split(',')
96
+ @use_json = argv.flag?('use-json', false)
97
+ @build_settings_memoized = {}
98
+ @sandbox_map = {}
99
+ @project_files_dir = nil
100
+ @project_zips_dir = nil
101
+ super
102
+ end
103
+
104
+ def validate!
105
+ super
106
+ help! 'A podspec file is required.' unless @podspec_path
107
+ help! 'Must supply an output directory.' unless @out_dir
108
+ end
109
+
110
+ def run
111
+ podspec = Specification.from_file(podspec_to_pack)
112
+ @project_files_dir = File.expand_path(File.join(@out_dir, 'files', podspec.name, podspec.version.to_s))
113
+ @project_zips_dir = File.expand_path(File.join(@out_dir, 'zips', podspec.name, podspec.version.to_s))
114
+ @artifact_repo_url ||= podspec.attributes_hash['artifact_repo_url']
115
+ FileUtils.mkdir_p(@project_files_dir)
116
+ FileUtils.mkdir_p(@project_zips_dir)
117
+ help! 'Must supply an artifact repo url.' unless @artifact_repo_url
118
+ stage_dir = File.join(@project_files_dir, 'staged')
119
+ FileUtils.rm_rf(stage_dir)
120
+ FileUtils.mkdir_p(stage_dir)
121
+ source_urls = @source_urls.map { |url| Config.instance.sources_manager.source_with_name_or_url(url) }.map(&:url)
122
+ available_platforms(podspec).each do |platform|
123
+ linkage = @use_static_frameworks ? :static : :dynamic
124
+ podfile = podfile_from_spec(platform, podspec, source_urls, linkage, @is_local)
125
+ sandbox = install(podfile, platform, podspec)
126
+ @sandbox_map[platform.name] = sandbox
127
+ xcodebuild_out_dir = File.join(sandbox.root.to_s, 'xcodebuild')
128
+ build(podspec, platform, sandbox, xcodebuild_out_dir)
129
+ xcarchives = Dir.glob(File.join(xcodebuild_out_dir, '**', '*.xcarchive')).map do |path|
130
+ XCArchive.new(path, podspec)
131
+ end
132
+ stage_platform_xcframework(platform, sandbox, podspec, xcarchives, xcodebuild_out_dir, stage_dir)
133
+ end
134
+ stage_additional_artifacts(podspec, stage_dir)
135
+ zip_output_path = pack(stage_dir, @project_zips_dir, podspec.name)
136
+ binary_podspec = generate_binary_podspec(podspec, stage_dir, zip_output_path)
137
+ validate_binary_podspec(binary_podspec)
138
+ UI.message "Binary pod for #{podspec.name} created successfully!".green
139
+ rescue XcodeBuilder::BuildError => e
140
+ raise Informative, e
141
+ end
142
+
143
+ private
144
+
145
+ def install(podfile, platform, podspec)
146
+ UI.puts "\nInstalling #{podspec.name} for #{platform.name}...\n\n".yellow
147
+ original_config = config.clone
148
+ config.installation_root = Pathname.new(File.join(@project_files_dir, 'sandbox', platform.name.to_s))
149
+
150
+ sandbox = Sandbox.new(config.sandbox_root)
151
+
152
+ installer = Installer.new(sandbox, podfile)
153
+ installer.repo_update = @repo_update
154
+ installer.use_default_plugins = false
155
+ # noinspection RubyResolve
156
+ installer.podfile.installation_options.integrate_targets = false
157
+ # noinspection RubyResolve
158
+ installer.podfile.installation_options.deterministic_uuids = false
159
+ # noinspection RubyResolve
160
+ installer.podfile.installation_options.warn_for_multiple_pod_sources = false
161
+ installer.install!
162
+
163
+ Config.instance = original_config
164
+ sandbox
165
+ end
166
+
167
+ def build(podspec, platform, sandbox, xcodebuild_out_dir)
168
+ xcode_builder(sandbox, xcodebuild_out_dir).build(platform.name, podspec.name)
169
+ end
170
+
171
+ def stage_platform_xcframework(platform, sandbox, podspec, xcarchives, xcodebuild_out_dir, stage_dir)
172
+ target = podspec.name
173
+ staged_platform_path = File.join(stage_dir, platform.name.to_s)
174
+ UI.puts "Staging #{platform.name}-#{target} into #{staged_platform_path}...".yellow
175
+ FileUtils.mkdir_p(staged_platform_path)
176
+
177
+ if @generate_module_map
178
+ type = type_from_platform(platform)
179
+ settings = build_settings(sandbox, xcodebuild_out_dir, platform, target, type)
180
+ module_name = settings['PRODUCT_MODULE_NAME']
181
+ file_accessor = Pod::Sandbox::FileAccessor.new(sandbox.pod_dir(podspec.name), podspec.consumer(platform))
182
+ module_map_contents = module_map_contents_for_framework_header_dir(module_name, file_accessor)
183
+
184
+ # Replace the generate module map by Xcode with the one we generated.
185
+ xcarchives.each do |xcarchive|
186
+ module_map = File.join(xcarchive.modules_path, 'module.modulemap')
187
+ File.write(module_map, module_map_contents)
188
+ end
189
+ end
190
+
191
+ xcframework_staged_output_path = File.join(staged_platform_path, "#{podspec.module_name}.xcframework")
192
+ args = %w[xcodebuild -create-xcframework]
193
+ xcarchives.each do |xcarchive|
194
+ args << "-framework #{xcarchive.framework_path}"
195
+ xcarchive.dsym_paths.each { |dsym_path| args << "-debug-symbols #{dsym_path}" }
196
+ xcarchive.bcsymbolmap_paths.each { |bcsymbolmap_path| args << "-debug-symbols #{bcsymbolmap_path}" }
197
+ end
198
+ args << "-output #{xcframework_staged_output_path}"
199
+ create_xcframework_cmd = args.join(' ')
200
+ output, process_status = shellout(create_xcframework_cmd)
201
+ return output if process_status.success?
202
+
203
+ warn output
204
+ raise Informative, "Failed to invoke create-xcframework command! Exit status: #{process_status.exitstatus}"
205
+ end
206
+
207
+ def stage_additional_artifacts(podspec, stage_dir)
208
+ copy_vendored_frameworks(podspec, stage_dir)
209
+ copy_vendored_libraries(podspec, stage_dir)
210
+ copy_resource_bundles(podspec, stage_dir)
211
+ copy_resources(podspec, stage_dir)
212
+ copy_license(podspec, stage_dir)
213
+ end
214
+
215
+ def generate_binary_podspec(source_podspec, stage_dir, zip_output_path)
216
+ name = source_podspec.name
217
+ spec_generator = SpecGenerator.new(source_podspec, @artifact_repo_url, zip_output_path)
218
+ available_platforms(source_podspec).each do |platform|
219
+ type = type_from_platform(platform)
220
+ sandbox = @sandbox_map[platform.name]
221
+ xcodebuild_out_dir = File.join(sandbox.root.to_s, 'xcodebuild')
222
+ settings = build_settings(sandbox, xcodebuild_out_dir, platform, name, type)
223
+ spec_generator.add_platform(platform, "#{settings['PRODUCT_NAME']}.xcframework")
224
+ end
225
+ spec = spec_generator.generate
226
+ if @use_json
227
+ binary_spec_path = File.join(stage_dir, name + '.podspec.json')
228
+ File.open(binary_spec_path, 'w') { |file| file.write(spec.to_pretty_json) }
229
+ else
230
+ binary_spec_path = File.join(stage_dir, name + '.podspec')
231
+ File.open(binary_spec_path, 'w') { |file| file.write(spec_generator.generate_ruby_string) }
232
+ end
233
+ spec.instance_variable_set(:@defined_in_file, Pathname.new(binary_spec_path))
234
+ spec
235
+ end
236
+
237
+ def validate_binary_podspec(podspec)
238
+ if @skip_validation
239
+ UI.puts 'Skipping validation phase...'.yellow
240
+ return
241
+ end
242
+
243
+ UI.puts "\nValidating generated binary podspec...\n\n".yellow
244
+ validator = Pod::Validator.new(podspec, @source_urls)
245
+ validator.quick = false
246
+ validator.local = true
247
+ validator.no_clean = false
248
+ validator.fail_fast = true
249
+ validator.allow_warnings = @allow_warnings
250
+ validator.no_subspecs = true
251
+ validator.only_subspec = false
252
+ validator.use_frameworks = true
253
+ validator.use_static_frameworks = @use_static_frameworks
254
+ validator.validate
255
+ raise Informative, "The binary spec did not pass validation, due to #{validator.failure_reason}." if validator.failure_reason
256
+ end
257
+
258
+ def pack(input_dir, output_dir, name)
259
+ output_path = File.join(output_dir, "#{name}.zip")
260
+ UI.puts "\nPacking #{input_dir} into #{output_path}...\n".green
261
+ ZipFileGenerator.new(input_dir, output_path).write { |file| File.extname(file) == '.podspec' || File.symlink?(file) }
262
+ output_path
263
+ end
264
+
265
+ def module_map_contents_for_framework_header_dir(framework_name, file_accessor)
266
+ if header_mappings_dir = file_accessor.spec_consumer.header_mappings_dir
267
+ header_mappings_dir = file_accessor.root + header_mappings_dir
268
+ end
269
+
270
+ headers = {}
271
+ file_accessor.public_headers.each do |path|
272
+ header = if header_mappings_dir
273
+ path.relative_path_from(header_mappings_dir)
274
+ else
275
+ path.basename
276
+ end
277
+ header = header.to_s
278
+ .sub(%r{\A\.?/?}, '') # remove preceding '.' or './'
279
+ next if header.empty?
280
+
281
+ parts = header.split('/')
282
+ file_name = parts.pop unless path.directory?
283
+
284
+ current_module = headers # start at the root
285
+ while part = parts.shift
286
+ # drill down to find / create the intermediary "submodules"
287
+ current_module = (current_module[part] ||= {})
288
+ end
289
+ current_module[file_name] = file_name if file_name
290
+ end
291
+
292
+ modules_str = ''
293
+ handle = lambda { |m, prefix = '', i = 2|
294
+ m.each do |k, v|
295
+ k = v.split('.', 2).first if v.is_a? String
296
+ modules_str << (' ' * i) << 'module ' << k.tr('-', '_') << ' {' << "\n"
297
+ if v.is_a? String
298
+ header = prefix.empty? ? v : "#{prefix}/#{v}"
299
+ modules_str << (' ' * (i + 2)) << %(header "#{header}") << "\n"
300
+ modules_str << (' ' * (i + 2)) << 'export *' << "\n"
301
+ else
302
+ nested_prefix = prefix.empty? ? k : "#{prefix}/#{k}"
303
+ handle[v, nested_prefix, i + 2]
304
+ end
305
+ modules_str << (' ' * i) << '}' << "\n"
306
+ end
307
+ }
308
+ handle[headers]
309
+
310
+ "framework module #{framework_name} {\n#{modules_str.chomp}\n}\n"
311
+ end
312
+
313
+ def build_settings(sandbox, xcodebuild_out_dir, platform, target, type = nil)
314
+ args = [sandbox, xcodebuild_out_dir, platform, target, type]
315
+ value = @build_settings_memoized[args]
316
+ if value.nil?
317
+ value = xcode_builder(sandbox, xcodebuild_out_dir).build_settings(platform, target, type)
318
+ @build_settings_memoized[args] = value
319
+ end
320
+ value
321
+ end
322
+
323
+ def xcode_builder(sandbox, xcodebuild_out_dir)
324
+ XcodeBuilder.new(sandbox.project_path, @xcodebuild_opts, xcodebuild_out_dir, UI, config.verbose)
325
+ end
326
+
327
+ def copy_vendored_frameworks(podspec, stage_dir)
328
+ copy_vendored_artifacts(podspec, 'vendored_frameworks', stage_dir)
329
+ end
330
+
331
+ def copy_vendored_libraries(podspec, stage_dir)
332
+ copy_vendored_artifacts(podspec, 'vendored_libraries', stage_dir)
333
+ end
334
+
335
+ def copy_vendored_artifacts(podspec, attribute, stage_dir)
336
+ platforms = Pod::Specification::DSL::PLATFORMS
337
+ hash = podspec.attributes_hash
338
+ globs = [Array(hash[attribute])] + platforms.map { |p| Array((hash[p.to_s] || {})[attribute]) }
339
+ globs.flatten.to_set.each { |glob| stage_glob(glob, stage_dir) }
340
+ end
341
+
342
+ def copy_resource_bundles(podspec, stage_dir)
343
+ resource_bundles = podspec.attributes_hash['resource_bundles']
344
+ return if resource_bundles.nil?
345
+
346
+ resource_paths = []
347
+ resource_bundles.values.map { |globspec| Array(globspec) }.flatten.each do |glob|
348
+ podspec_dir_relative_glob(glob, include_dirs: true).each do |file_path|
349
+ if File.file?(file_path)
350
+ resource_paths << file_path
351
+ else
352
+ resource_paths += Pod::Sandbox::PathList.new(Pathname(file_path)).glob('**/*')
353
+ end
354
+ end
355
+ end
356
+ resource_paths.uniq.each { |resource_path| stage_file(resource_path, stage_dir) }
357
+ end
358
+
359
+ def copy_resources(podspec, stage_dir)
360
+ %w[preserve_paths resources].each { |attribute| transplant_tree_with_attribute(podspec, attribute, stage_dir) }
361
+ end
362
+
363
+ def transplant_tree_with_attribute(podspec, attribute, stage_dir)
364
+ globs = Array(podspec.attributes_hash[attribute])
365
+ globs.to_set.each { |glob| stage_glob(glob, stage_dir) }
366
+ end
367
+
368
+ def copy_license(podspec, stage_dir)
369
+ return if podspec.license[:text]
370
+
371
+ license_spec = podspec.license[:file]
372
+ license_file = license_spec ? File.join(podspec_dir, license_spec) : lookup_default_license_file
373
+ return unless license_file
374
+
375
+ stage_file(license_file, stage_dir)
376
+ end
377
+
378
+ def lookup_default_license_file
379
+ podspec_dir_relative_glob(LICENSE_GLOB_PATTERNS).first
380
+ end
381
+
382
+ def stage_glob(glob, stage_dir)
383
+ glob = File.join(glob, '**', '*') if File.directory?(File.join(podspec_dir, glob))
384
+ podspec_dir_relative_glob(glob).each { |file_path| stage_file(file_path, stage_dir) }
385
+ end
386
+
387
+ def podspec_dir_relative_glob(glob, options = {})
388
+ Pod::Sandbox::PathList.new(Pathname(podspec_dir)).glob(glob, options)
389
+ end
390
+
391
+ def stage_file(file_path, stage_dir)
392
+ pathname = Pathname(file_path)
393
+
394
+ relative_path_file = pathname.relative_path_from(Pathname(podspec_dir)).dirname.to_path
395
+ raise Informative, "Bad Relative path #{relative_path_file}" if relative_path_file.start_with?('..')
396
+
397
+ staged_folder = File.join(stage_dir, relative_path_file)
398
+ FileUtils.mkdir_p(staged_folder)
399
+ staged_file_path = File.join(staged_folder, pathname.basename)
400
+ raise Informative, "File #{staged_file_path} already exists." if File.exist?(staged_file_path)
401
+
402
+ FileUtils.copy_file(pathname.to_path, staged_file_path)
403
+ end
404
+
405
+ def podspec_dir
406
+ File.expand_path(File.dirname(podspec_to_pack))
407
+ end
408
+
409
+ def copy_headers(sandbox, target, stage_dir)
410
+ header_path = File.join(sandbox.public_headers.root, target)
411
+ cp_r_dereference(header_path, stage_dir)
412
+ end
413
+
414
+ def cp_r_dereference(src, dst)
415
+ src_pn = Pathname.new(src)
416
+ find_follow(src) do |path|
417
+ relpath = Pathname.new(path).relative_path_from(src_pn).to_s
418
+ dstpath = File.join(dst, relpath)
419
+
420
+ if File.directory?(path) || (File.symlink?(path) && File.directory?(File.realpath(path)))
421
+ FileUtils.mkdir_p(dstpath)
422
+ else
423
+ FileUtils.copy_file(path, dstpath)
424
+ end
425
+ end
426
+ end
427
+
428
+ def podfile_from_spec(platform, podspec, source_urls, linkage, local)
429
+ Pod::Podfile.new do
430
+ install!('cocoapods', warn_for_multiple_pod_sources: false)
431
+ source_urls.each { |u| source(u) }
432
+ use_frameworks!(linkage: linkage)
433
+ platform(platform.name, platform.deployment_target)
434
+ if local
435
+ pod podspec.name, path: podspec.defined_in_file.to_s
436
+ else
437
+ pod podspec.name, podspec: podspec.defined_in_file.to_s
438
+ end
439
+ target CONCRETE_TARGET_NAME
440
+ end
441
+ end
442
+
443
+ def podspec_to_pack
444
+ @podspec_to_pack = begin
445
+ path = @podspec_path
446
+ if path =~ %r{https?://}
447
+ require 'cocoapods/open-uri'
448
+ output_path = podspecs_tmp_dir + File.basename(path)
449
+ output_path.dirname.mkpath
450
+ begin
451
+ OpenURI.open_uri(path) do |io|
452
+ output_path.open('w') { |f| f << io.read }
453
+ end
454
+ rescue StandardError => e
455
+ raise Informative, "Downloading a podspec from `#{path}` failed: #{e}"
456
+ end
457
+ @is_local = false
458
+ output_path
459
+ elsif Pathname.new(path).directory?
460
+ raise Informative, "Podspec specified in `#{path}` is a directory."
461
+ else
462
+ pathname = Pathname.new(path)
463
+ raise Informative, "Unable to find a spec named `#{path}'." unless pathname.exist? && path.include?('.podspec')
464
+
465
+ @is_local = true
466
+ pathname
467
+ end
468
+ end
469
+ end
470
+
471
+ def podspecs_tmp_dir
472
+ Pathname.new(Dir.tmpdir) + "CocoaPods-Bin/#{CocoapodsPack::VERSION}/Pack_podspec"
473
+ end
474
+
475
+ def type_from_platform(platform)
476
+ return :simulator if platform == :ios
477
+ return :simulator if platform == :watchos
478
+ return :simulator if platform == :tvos
479
+
480
+ nil
481
+ end
482
+
483
+ def available_platforms(podspec)
484
+ return podspec.available_platforms if @skipped_platforms.empty?
485
+
486
+ podspec.available_platforms.reject do |platform|
487
+ @skipped_platforms.include? platform.string_name.gsub(/\s+/, '').downcase
488
+ end
489
+ end
490
+
491
+ def shellout(command)
492
+ output = `#{command}`
493
+ [output, $CHILD_STATUS]
494
+ end
495
+ end
496
+ end
497
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright 2021 Square, Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'cocoapods-pack/command/pack'
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright 2021 Square, Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ module EnvParser
20
+ def parse_env(output)
21
+ Hash[*output.lines.select { |l| l.include?(' = ') }.map { |l| l.split(' =', 2) }.flatten.map(&:strip)]
22
+ end
23
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Source: https://gist.github.com/akostadinov/05c2a976dc16ffee9cac
4
+
5
+ # * ruby implementation of find that follows symbolic directory links
6
+ # * tested on ruby 1.9.3, ruby 2.0 and jruby on Fedora 20 linux
7
+ # * you can use Find.prune
8
+ # * detect symlinks to dirs by path "/" suffix; does nothing with files so `symlink?` method is working fine
9
+ # * depth first order
10
+ # * detects cycles and raises an error
11
+ # * raises on broken links
12
+ # * uses recursion in the `do_find` proc when directory links are met (takes a lot of nested links until SystemStackError, that's practically never)
13
+ #
14
+ # * use like: find_follow(".") {|f| puts f}
15
+ #
16
+ # Copyright (c) 2014 Red Hat inc
17
+ #
18
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
19
+ # of this software and associated documentation files (the "Software"), to deal
20
+ # in the Software without restriction, including without limitation the rights
21
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
22
+ # copies of the Software, and to permit persons to whom the Software is
23
+ # furnished to do so, subject to the following conditions:
24
+ #
25
+ # The above copyright notice and this permission notice shall be included in
26
+ # all copies or substantial portions of the Software.
27
+ #
28
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
29
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
30
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
31
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
32
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
33
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
34
+ # THE SOFTWARE.
35
+
36
+ require 'find'
37
+ require 'pathname'
38
+
39
+ module FindFollow
40
+ def find_follow(*paths)
41
+ block_given? || (return enum_for(__method__, *paths))
42
+
43
+ link_cache = {}
44
+ link_resolve = lambda { |path|
45
+ # puts "++ link_resolve: #{path}" # trace
46
+ return link_cache[path] if link_cache[path]
47
+
48
+ return link_cache[path] = Pathname.new(path).realpath.to_s
49
+ }
50
+ # this lambda should cleanup `link_cache` from unnecessary entries
51
+ link_cache_reset = lambda { |path|
52
+ # puts "++ link_cache_reset: #{path}" # trace
53
+ # puts link_cache.to_s # trace
54
+ link_cache.select! do |k, _v|
55
+ path == k || k == '/' || path.start_with?(k + '/')
56
+ end
57
+ # puts link_cache.to_s # trace
58
+ }
59
+ link_is_recursive = lambda { |path|
60
+ # puts "++ link_is_recursive: #{path}" # trace
61
+ # the ckeck is useless if path is not a link but not our responsibility
62
+
63
+ # we need to check full path for link cycles
64
+ pn_initial = Pathname.new(path)
65
+ unless pn_initial.absolute?
66
+ # can we use `expand_path` here? Any issues with links?
67
+ pn_initial = Pathname.new(File.join(Dir.pwd, path))
68
+ end
69
+
70
+ # clear unnecessary cache
71
+ link_cache_reset.call(pn_initial.to_s)
72
+
73
+ link_dst = link_resolve.call(pn_initial.to_s)
74
+
75
+ pn_initial.ascend do |pn|
76
+ return { link: path, dst: pn } if pn != pn_initial && link_dst == link_resolve.call(pn.to_s)
77
+ end
78
+
79
+ return false
80
+ }
81
+
82
+ do_find = proc { |path|
83
+ Find.find(path) do |result|
84
+ if File.symlink?(result) && File.directory?(File.realpath(result))
85
+ if result[-1] == '/'
86
+ # probably hitting https://github.com/jruby/jruby/issues/1895
87
+ yield(result.dup)
88
+ Dir.new(result).each do |subpath|
89
+ do_find.call(result + subpath) unless ['.', '..'].include?(subpath)
90
+ end
91
+ elsif is_recursive = link_is_recursive.call(result)
92
+ raise "cannot handle recursive links: #{is_recursive[:link]} => #{is_recursive[:dst]}"
93
+ else
94
+ do_find.call(result + '/')
95
+ end
96
+ else
97
+ yield(result)
98
+ end
99
+ end
100
+ }
101
+
102
+ while path = paths.shift
103
+ do_find.call(path)
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright 2021 Square, Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ module CocoapodsPack
20
+ VERSION = '1.0.0'
21
+ end