cocoapods-modularization 0.0.2
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.
- checksums.yaml +7 -0
- data/lib/cocoapods-modularization/command/install.rb +98 -0
- data/lib/cocoapods-modularization/command/mod/add.rb +97 -0
- data/lib/cocoapods-modularization/command/mod/base.rb +35 -0
- data/lib/cocoapods-modularization/command/mod/binary.rb +82 -0
- data/lib/cocoapods-modularization/command/mod/config.rb +51 -0
- data/lib/cocoapods-modularization/command/mod/create.rb +34 -0
- data/lib/cocoapods-modularization/command/mod/inspect.rb +26 -0
- data/lib/cocoapods-modularization/command/mod/source.rb +81 -0
- data/lib/cocoapods-modularization/command/mod/sync.rb +29 -0
- data/lib/cocoapods-modularization/command/mod/update.rb +64 -0
- data/lib/cocoapods-modularization/command/mod.rb +28 -0
- data/lib/cocoapods-modularization/command.rb +2 -0
- data/lib/cocoapods-modularization/gem_version.rb +3 -0
- data/lib/cocoapods-modularization/generate/configuration.rb +364 -0
- data/lib/cocoapods-modularization/generate/installer.rb +388 -0
- data/lib/cocoapods-modularization/generate/podfile_generator.rb +398 -0
- data/lib/cocoapods-modularization/generate.rb +7 -0
- data/lib/cocoapods-modularization/meta/meta_accessor.rb +134 -0
- data/lib/cocoapods-modularization/meta/meta_constants.rb +135 -0
- data/lib/cocoapods-modularization/meta/meta_reference.rb +255 -0
- data/lib/cocoapods-modularization/meta.rb +7 -0
- data/lib/cocoapods-modularization/private/private_cache.rb +277 -0
- data/lib/cocoapods-modularization/private.rb +5 -0
- data/lib/cocoapods-modularization.rb +1 -0
- data/lib/cocoapods_plugin.rb +1 -0
- metadata +96 -0
@@ -0,0 +1,364 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pod
|
4
|
+
module Generate
|
5
|
+
class Configuration
|
6
|
+
@options = []
|
7
|
+
class << self
|
8
|
+
# @return [Array<Option>]
|
9
|
+
# all of the options available in the configuration
|
10
|
+
#
|
11
|
+
attr_reader :options
|
12
|
+
end
|
13
|
+
|
14
|
+
Option = Struct.new(:name, :type, :default, :message, :arg_name, :validator, :coercer) do
|
15
|
+
def validate(value)
|
16
|
+
return if value.nil?
|
17
|
+
errors = []
|
18
|
+
if (exceptions = Array(value).grep(Exception)) && exceptions.any?
|
19
|
+
errors << "Error computing #{name}"
|
20
|
+
errors.concat exceptions.map(&:message)
|
21
|
+
else
|
22
|
+
errors << "got type #{value.class}, expected object of type #{Array(type).join('|')}" unless Array(type).any? { |t| t === value }
|
23
|
+
validator_errors = begin
|
24
|
+
validator && validator[value]
|
25
|
+
rescue StandardError
|
26
|
+
"failed to run validator (#{$ERROR_INFO})"
|
27
|
+
end
|
28
|
+
errors.concat Array(validator_errors) if validator_errors
|
29
|
+
errors.unshift "#{value.inspect} invalid for #{name}" if errors.any?
|
30
|
+
end
|
31
|
+
errors.join(', ') unless errors.empty?
|
32
|
+
end
|
33
|
+
|
34
|
+
def cli_name
|
35
|
+
return unless message
|
36
|
+
@cli_name ||= name.to_s.tr '_', '-'
|
37
|
+
end
|
38
|
+
|
39
|
+
def flag?
|
40
|
+
arg_name.nil?
|
41
|
+
end
|
42
|
+
|
43
|
+
def coerce(value)
|
44
|
+
coercer ? coercer[value] : value
|
45
|
+
end
|
46
|
+
end
|
47
|
+
private_constant :Option
|
48
|
+
|
49
|
+
# Declares a new option
|
50
|
+
#
|
51
|
+
# @!macro [attach] $0
|
52
|
+
# @attribute [r] $1
|
53
|
+
# @return [$2] $4
|
54
|
+
# defaults to `$3`
|
55
|
+
#
|
56
|
+
def self.option(*args)
|
57
|
+
options << Option.new(*args)
|
58
|
+
end
|
59
|
+
private_class_method :option
|
60
|
+
|
61
|
+
# @visibility private
|
62
|
+
#
|
63
|
+
# Implements `===` to do type checking against an array.
|
64
|
+
#
|
65
|
+
class ArrayOf
|
66
|
+
attr_reader :types
|
67
|
+
|
68
|
+
def initialize(*types)
|
69
|
+
@types = types
|
70
|
+
end
|
71
|
+
|
72
|
+
def to_s
|
73
|
+
"Array<#{types.join('|')}>"
|
74
|
+
end
|
75
|
+
|
76
|
+
# @return [Boolean] whether the given object is an array with elements all of the given types
|
77
|
+
#
|
78
|
+
def ===(other)
|
79
|
+
other.is_a?(Array) && other.all? { |o| types.any? { |t| t === o } }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
private_constant :ArrayOf
|
83
|
+
|
84
|
+
# @visibility private
|
85
|
+
#
|
86
|
+
# Implements `===` to do type checking against a hash.
|
87
|
+
#
|
88
|
+
class HashOf
|
89
|
+
attr_reader :key_types, :value_types
|
90
|
+
|
91
|
+
def initialize(keys:, values:)
|
92
|
+
@key_types = keys
|
93
|
+
@value_types = values
|
94
|
+
end
|
95
|
+
|
96
|
+
def to_s
|
97
|
+
"Hash<#{key_types.join('|')} => #{value_types.join('|')}}>"
|
98
|
+
end
|
99
|
+
|
100
|
+
# @return [Boolean] whether the given object is a hash with elements all of the given types
|
101
|
+
#
|
102
|
+
def ===(other)
|
103
|
+
other.is_a?(Hash) && other.all? do |key, value|
|
104
|
+
key_types.any? { |t| t === key } &&
|
105
|
+
value_types.any? { |t| t === value }
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
private_constant :HashOf
|
110
|
+
|
111
|
+
coerce_to_bool = lambda do |value|
|
112
|
+
if value.is_a?(String)
|
113
|
+
value =
|
114
|
+
case value.downcase
|
115
|
+
when ''
|
116
|
+
nil
|
117
|
+
when 'true'
|
118
|
+
true
|
119
|
+
when 'false'
|
120
|
+
false
|
121
|
+
end
|
122
|
+
end
|
123
|
+
value
|
124
|
+
end
|
125
|
+
|
126
|
+
coerce_to_pathname = lambda do |path|
|
127
|
+
path && Pathname(path).expand_path
|
128
|
+
end
|
129
|
+
|
130
|
+
BOOLEAN = [TrueClass, FalseClass].freeze
|
131
|
+
private_constant :BOOLEAN
|
132
|
+
|
133
|
+
option :pod_config, Config, 'Pod::Config.instance', nil
|
134
|
+
|
135
|
+
option :podfile_path, [String, Pathname], 'pod_config.podfile_path', 'Path to podfile to use', 'PATH', ->(path) { 'file does not exist' unless path.file? }, coerce_to_pathname
|
136
|
+
option :podfile, [Podfile], 'Podfile.from_file(podfile_path) if (podfile_path && File.file?(File.expand_path(podfile_path)))'
|
137
|
+
option :use_podfile, BOOLEAN, '!!podfile', 'Whether restrictions should be copied from the podfile', nil, nil, coerce_to_bool
|
138
|
+
option :use_podfile_plugins, BOOLEAN, 'use_podfile', 'Whether plugins should be copied from the podfile', nil, nil, coerce_to_bool
|
139
|
+
option :podfile_plugins, HashOf.new(keys: [String], values: [NilClass, HashOf.new(keys: [String], values: [TrueClass, FalseClass, NilClass, String, Hash, Array])]),
|
140
|
+
'(use_podfile && podfile) ? podfile.plugins : {}',
|
141
|
+
nil,
|
142
|
+
nil,
|
143
|
+
nil,
|
144
|
+
->(hash) { Hash[hash] }
|
145
|
+
|
146
|
+
option :lockfile, [Pod::Lockfile], 'pod_config.lockfile', nil
|
147
|
+
option :use_lockfile, BOOLEAN, '!!lockfile', 'Whether the lockfile should be used to discover transitive dependencies', nil, nil, coerce_to_bool
|
148
|
+
option :use_lockfile_versions, BOOLEAN, 'use_lockfile', 'Whether versions from the lockfile should be used', nil, nil, coerce_to_bool
|
149
|
+
|
150
|
+
option :use_libraries, BOOLEAN, 'false', 'Whether to use libraries instead of frameworks', nil, nil, coerce_to_bool
|
151
|
+
|
152
|
+
option :generate_multiple_pod_projects, BOOLEAN, 'false', 'Whether to generate multiple Xcode projects', nil, nil, coerce_to_bool
|
153
|
+
option :incremental_installation, BOOLEAN, 'false', 'Whether to use incremental installation', nil, nil, coerce_to_bool
|
154
|
+
|
155
|
+
option :gen_directory, [String, Pathname], 'Pathname("gen").expand_path', 'Path to generate workspaces in', 'PATH', ->(path) { 'path is file' if path.file? }, coerce_to_pathname
|
156
|
+
option :auto_open, BOOLEAN, 'false', 'Whether to automatically open the generated workspaces', nil, nil, coerce_to_bool
|
157
|
+
option :clean, BOOLEAN, 'false', 'Whether to clean the generated directories before generating', nil, nil, coerce_to_bool
|
158
|
+
|
159
|
+
option :app_host_source_dir, [String, Pathname], 'nil',
|
160
|
+
'A directory containing sources to use for the app host',
|
161
|
+
'DIR',
|
162
|
+
->(dir) { 'not a directory' unless dir.directory? },
|
163
|
+
coerce_to_pathname
|
164
|
+
|
165
|
+
option :podspec_paths, ArrayOf.new(String, Pathname, URI),
|
166
|
+
'[Pathname(?.)]',
|
167
|
+
nil,
|
168
|
+
nil,
|
169
|
+
->(paths) { ('paths do not exist' unless paths.all? { |p| p.is_a?(URI) || p.exist? }) },
|
170
|
+
->(paths) { paths && paths.map { |path| path.to_s =~ %r{https?://} ? URI(path) : Pathname(path).expand_path } }
|
171
|
+
option :podspecs, ArrayOf.new(Pod::Specification),
|
172
|
+
'self.class.podspecs_from_paths(podspec_paths)',
|
173
|
+
nil,
|
174
|
+
nil,
|
175
|
+
->(specs) { 'no podspecs found' if specs.empty? },
|
176
|
+
->(paths) { paths && paths.map { |path| Pathname(path).expand_path } }
|
177
|
+
|
178
|
+
# installer options
|
179
|
+
option :sources, ArrayOf.new(String),
|
180
|
+
'if use_podfile && podfile then ::Pod::Installer::Analyzer.new(:sandbox, podfile).sources.map(&:url) else pod_config.sources_manager.all.map(&:url) end',
|
181
|
+
'The sources from which to pull dependent pods (defaults to all repos in the podfile if using the podfile, else all available repos). Can be a repo name or URL. Multiple sources must be comma-delimited.',
|
182
|
+
'SOURCE1,SOURCE2',
|
183
|
+
->(_) { nil },
|
184
|
+
->(sources) { Array(sources).flat_map { |s| s.split(',') } }
|
185
|
+
option :local_sources, ArrayOf.new(String),
|
186
|
+
[],
|
187
|
+
'Paths from which to find local podspecs for transitive dependencies. Multiple local-sources must be comma-delimited.',
|
188
|
+
'SOURCE1,SOURCE2',
|
189
|
+
->(_) { nil },
|
190
|
+
->(local_sources) { Array(local_sources).flat_map { |s| s.split(',') } }
|
191
|
+
option :platforms, ArrayOf.new(String),
|
192
|
+
nil,
|
193
|
+
'Limit to specific platforms. Default is all platforms supported by the podspec. Multiple platforms must be comma-delimited.',
|
194
|
+
'ios,macos',
|
195
|
+
lambda { |platforms|
|
196
|
+
valid_platforms = Platform.all.map { |p| p.string_name.downcase }
|
197
|
+
valid_platforms unless (platforms - valid_platforms).empty?
|
198
|
+
}, # validates platforms is a subset of Platform.all
|
199
|
+
->(platforms) { Array(platforms).flat_map { |s| s.split(',') } }
|
200
|
+
option :repo_update, BOOLEAN, 'false', 'Force running `pod repo update` before install', nil, nil, coerce_to_bool
|
201
|
+
option :use_default_plugins, BOOLEAN, 'false', 'Whether installation should activate default plugins', nil, nil, coerce_to_bool
|
202
|
+
option :deterministic_uuids, BOOLEAN, 'false', 'Whether installation should use deterministic UUIDs for pods projects', nil, nil, coerce_to_bool
|
203
|
+
option :share_schemes_for_development_pods, BOOLEAN, 'true', 'Whether installation should share schemes for development pods', nil, nil, coerce_to_bool
|
204
|
+
option :warn_for_multiple_pod_sources, BOOLEAN, 'false', 'Whether installation should warn when a pod is found in multiple sources', nil, nil, coerce_to_bool
|
205
|
+
option :use_modular_headers, BOOLEAN, 'false', 'Whether the target should be generated as a clang module, treating dependencies as modules, as if `use_modular_headers!` were specified. Will error if both this option and a podfile are specified', nil, nil, coerce_to_bool
|
206
|
+
|
207
|
+
options.freeze
|
208
|
+
options.each do |o|
|
209
|
+
attr_reader o.name
|
210
|
+
alias_method :"#{o.name}?", o.name if o.type == BOOLEAN
|
211
|
+
end
|
212
|
+
|
213
|
+
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
214
|
+
# @!visibility private
|
215
|
+
def initialize(
|
216
|
+
#{options.map { |o| "#{o.name}: (begin (#{o.default}); rescue => e; e; end)" }.join(', ')}
|
217
|
+
)
|
218
|
+
#{options.map { |o| "@#{o.name} = #{o.name}" }.join('; ')}
|
219
|
+
end
|
220
|
+
RUBY
|
221
|
+
|
222
|
+
# @return [Hash<Symbol,Object>] the configuration hash parsed from the given file
|
223
|
+
#
|
224
|
+
# @param [Pathname] path
|
225
|
+
#
|
226
|
+
# @raises [Informative] if the file does not exist or is not a YAML hash
|
227
|
+
#
|
228
|
+
def self.from_file(path)
|
229
|
+
raise Informative, "No cocoapods-generate configuration found at #{UI.path path}" unless path.file?
|
230
|
+
require 'yaml'
|
231
|
+
yaml = YAML.load_file(path)
|
232
|
+
unless yaml.is_a?(Hash)
|
233
|
+
unless path.read.strip.empty?
|
234
|
+
raise Informative, "Hash not found in configuration at #{UI.path path} -- got #{yaml.inspect}"
|
235
|
+
end
|
236
|
+
yaml = {}
|
237
|
+
end
|
238
|
+
yaml = yaml.with_indifferent_access
|
239
|
+
|
240
|
+
Dir.chdir(path.dirname) do
|
241
|
+
options.each_with_object({}) do |option, config|
|
242
|
+
next unless yaml.key?(option.name)
|
243
|
+
config[option.name] = option.coerce yaml[option.name]
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
# @return [Hash<Symbol,Object>] the configuration hash parsed from the env
|
249
|
+
#
|
250
|
+
# @param [ENV,Hash<String,String>] env
|
251
|
+
#
|
252
|
+
def self.from_env(env = ENV)
|
253
|
+
options.each_with_object({}) do |option, config|
|
254
|
+
next unless (value = env["COCOAPODS_GENERATE_#{option.name.upcase}"])
|
255
|
+
config[option.name] = option.coerce(value)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
# @return [Array<String>] errors in the configuration
|
260
|
+
#
|
261
|
+
def validate
|
262
|
+
hash = to_h
|
263
|
+
self.class.options.map do |option|
|
264
|
+
option.validate(hash[option.name])
|
265
|
+
end.compact
|
266
|
+
end
|
267
|
+
|
268
|
+
# @return [Configuration] a new configuration object with the given changes applies
|
269
|
+
#
|
270
|
+
# @param [Hash<Symbol,Object>] changes
|
271
|
+
#
|
272
|
+
def with_changes(changes)
|
273
|
+
self.class.new(**to_h.merge(changes))
|
274
|
+
end
|
275
|
+
|
276
|
+
# @return [Hash<Symbol,Object>]
|
277
|
+
# a hash where the keys are option names and values are the non-nil set values
|
278
|
+
#
|
279
|
+
def to_h
|
280
|
+
self.class.options.each_with_object({}) do |option, hash|
|
281
|
+
value = send(option.name)
|
282
|
+
next if value.nil?
|
283
|
+
hash[option.name] = value
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
# @return [Boolean] whether this configuration is equivalent to other
|
288
|
+
#
|
289
|
+
def ==(other)
|
290
|
+
self.class == other.class &&
|
291
|
+
to_h == other.to_h
|
292
|
+
end
|
293
|
+
|
294
|
+
# @return [String] a string describing the configuration, suitable for UI presentation
|
295
|
+
#
|
296
|
+
def to_s
|
297
|
+
hash = to_h
|
298
|
+
hash.delete(:pod_config)
|
299
|
+
hash.each_with_index.each_with_object('`pod gen` configuration {'.dup) do |((k, v), i), s|
|
300
|
+
s << ',' unless i.zero?
|
301
|
+
s << "\n" << ' ' << k.to_s << ': ' << v.to_s.gsub(/:0x\h+/, '')
|
302
|
+
end << ' }'
|
303
|
+
end
|
304
|
+
|
305
|
+
# @return [Pathname] the directory for installation of the generated workspace
|
306
|
+
#
|
307
|
+
# @param [String] name the name of the pod
|
308
|
+
#
|
309
|
+
def gen_dir_for_pod(name)
|
310
|
+
gen_directory.join(name)
|
311
|
+
end
|
312
|
+
|
313
|
+
# @return [Boolean] whether gen should install with dynamic frameworks
|
314
|
+
#
|
315
|
+
def use_frameworks?
|
316
|
+
!use_libraries?
|
317
|
+
end
|
318
|
+
|
319
|
+
# @return [String] The project name to use for generating this workspace.
|
320
|
+
#
|
321
|
+
# @param [Specification] spec
|
322
|
+
# the specification to generate project name for.
|
323
|
+
#
|
324
|
+
def project_name_for_spec(spec)
|
325
|
+
project_name = spec.name.dup
|
326
|
+
# When using multiple Xcode project the project name will collide with the actual .xcodeproj meant for the pod
|
327
|
+
# that we are generating the workspace for.
|
328
|
+
project_name << 'Sample' if generate_multiple_pod_projects?
|
329
|
+
project_name
|
330
|
+
end
|
331
|
+
|
332
|
+
# @return [Array<Specification>] the podspecs found at the given paths.
|
333
|
+
# This method will download specs from URLs and traverse a directory's children.
|
334
|
+
#
|
335
|
+
# @param [Array<Pathname,URI>] paths
|
336
|
+
# the paths to search for podspecs
|
337
|
+
#
|
338
|
+
def self.podspecs_from_paths(paths)
|
339
|
+
paths = [Pathname('.')] if paths.empty?
|
340
|
+
paths.flat_map do |path|
|
341
|
+
if path.is_a?(URI)
|
342
|
+
require 'cocoapods/open-uri'
|
343
|
+
begin
|
344
|
+
contents = open(path.to_s).read
|
345
|
+
rescue StandardError => e
|
346
|
+
next e
|
347
|
+
end
|
348
|
+
begin
|
349
|
+
Pod::Specification.from_string contents, path.to_s
|
350
|
+
rescue StandardError
|
351
|
+
$ERROR_INFO
|
352
|
+
end
|
353
|
+
elsif path.directory?
|
354
|
+
glob = Pathname.glob(path + '*.podspec{.json,}')
|
355
|
+
next StandardError.new "no specs found in #{UI.path path}" if glob.empty?
|
356
|
+
glob.map { |f| Pod::Specification.from_file(f) }.sort_by(&:name)
|
357
|
+
else
|
358
|
+
Pod::Specification.from_file(path)
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|