cocoapods-pack 1.0.0

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