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.
Files changed (27) hide show
  1. checksums.yaml +7 -0
  2. data/lib/cocoapods-modularization/command/install.rb +98 -0
  3. data/lib/cocoapods-modularization/command/mod/add.rb +97 -0
  4. data/lib/cocoapods-modularization/command/mod/base.rb +35 -0
  5. data/lib/cocoapods-modularization/command/mod/binary.rb +82 -0
  6. data/lib/cocoapods-modularization/command/mod/config.rb +51 -0
  7. data/lib/cocoapods-modularization/command/mod/create.rb +34 -0
  8. data/lib/cocoapods-modularization/command/mod/inspect.rb +26 -0
  9. data/lib/cocoapods-modularization/command/mod/source.rb +81 -0
  10. data/lib/cocoapods-modularization/command/mod/sync.rb +29 -0
  11. data/lib/cocoapods-modularization/command/mod/update.rb +64 -0
  12. data/lib/cocoapods-modularization/command/mod.rb +28 -0
  13. data/lib/cocoapods-modularization/command.rb +2 -0
  14. data/lib/cocoapods-modularization/gem_version.rb +3 -0
  15. data/lib/cocoapods-modularization/generate/configuration.rb +364 -0
  16. data/lib/cocoapods-modularization/generate/installer.rb +388 -0
  17. data/lib/cocoapods-modularization/generate/podfile_generator.rb +398 -0
  18. data/lib/cocoapods-modularization/generate.rb +7 -0
  19. data/lib/cocoapods-modularization/meta/meta_accessor.rb +134 -0
  20. data/lib/cocoapods-modularization/meta/meta_constants.rb +135 -0
  21. data/lib/cocoapods-modularization/meta/meta_reference.rb +255 -0
  22. data/lib/cocoapods-modularization/meta.rb +7 -0
  23. data/lib/cocoapods-modularization/private/private_cache.rb +277 -0
  24. data/lib/cocoapods-modularization/private.rb +5 -0
  25. data/lib/cocoapods-modularization.rb +1 -0
  26. data/lib/cocoapods_plugin.rb +1 -0
  27. metadata +96 -0
@@ -0,0 +1,388 @@
1
+ module Pod
2
+ module Generate
3
+ # Responsible for creating a workspace for a single specification,
4
+ # given a configuration and a generated podfile.
5
+ #
6
+ class Installer
7
+ # @return [Configuration]
8
+ # the configuration to use when installing
9
+ #
10
+ attr_reader :configuration
11
+
12
+ # @return [Specification]
13
+ # the spec whose workspace is being created
14
+ #
15
+ attr_reader :spec
16
+
17
+ # @return [Podfile]
18
+ # the podfile to install
19
+ #
20
+ attr_reader :podfile
21
+
22
+ def initialize(configuration, spec, podfile)
23
+ @configuration = configuration
24
+ @spec = spec
25
+ @podfile = podfile
26
+ end
27
+
28
+ # @return [Pathname]
29
+ # The directory that pods will be installed into
30
+ #
31
+ def install_directory
32
+ @install_directory ||= podfile.defined_in_file.dirname
33
+ end
34
+
35
+ # Installs the {podfile} into the {install_directory}
36
+ #
37
+ # @return [void]
38
+ #
39
+ def install!
40
+ UI.title "Generating #{spec.name} in #{UI.path install_directory}" do
41
+ clean! if configuration.clean?
42
+ install_directory.mkpath
43
+
44
+ UI.message 'Creating stub application' do
45
+ create_app_project
46
+ end
47
+
48
+ UI.message 'Writing Podfile' do
49
+ podfile.defined_in_file.open('w') { |f| f << podfile.to_yaml }
50
+ end
51
+
52
+ installer = nil
53
+ UI.section 'Installing...' do
54
+ configuration.pod_config.with_changes(installation_root: install_directory, podfile: podfile,
55
+ lockfile: configuration.lockfile, sandbox: nil,
56
+ sandbox_root: install_directory + 'Pods',
57
+ podfile_path: podfile.defined_in_file,
58
+ silent: !configuration.pod_config.verbose?, verbose: false,
59
+ lockfile_path: nil) do
60
+ installer = ::Pod::Installer.new(configuration.pod_config.sandbox, podfile, configuration.lockfile)
61
+ installer.use_default_plugins = configuration.use_default_plugins
62
+ installer.install!
63
+ end
64
+ end
65
+
66
+ UI.section 'Performing post-installation steps' do
67
+ should_perform_post_install = if installer.respond_to?(:generated_aggregate_targets) # CocoaPods 1.7.0
68
+ !installer.generated_aggregate_targets.empty?
69
+ else
70
+ true
71
+ end
72
+ perform_post_install_steps(open_app_project, installer) if should_perform_post_install
73
+ end
74
+
75
+ print_post_install_message
76
+ end
77
+ end
78
+
79
+ private
80
+
81
+ # Removes the {install_directory}
82
+ #
83
+ # @return [void]
84
+ #
85
+ def clean!
86
+ UI.message 'Cleaning gen install directory' do
87
+ FileUtils.rm_rf install_directory
88
+ end
89
+ end
90
+
91
+ def open_app_project(recreate: false)
92
+ app_project_path = install_directory.join("#{configuration.project_name_for_spec(spec)}.xcodeproj")
93
+ if !recreate && app_project_path.exist?
94
+ Xcodeproj::Project.open(app_project_path)
95
+ else
96
+ Xcodeproj::Project.new(app_project_path)
97
+ end
98
+ end
99
+
100
+ # Creates an app project that CocoaPods will integrate into
101
+ #
102
+ # @return [Xcodeproj::Project]
103
+ #
104
+ def create_app_project
105
+ app_project = open_app_project(recreate: !configuration.incremental_installation?)
106
+
107
+ spec_platforms = spec.available_platforms.flatten.reject do |platform|
108
+ !configuration.platforms.nil? && !configuration.platforms.include?(platform.string_name.downcase)
109
+ end
110
+
111
+ if spec_platforms.empty?
112
+ Pod::Command::Gen.help! Pod::StandardError.new "No available platforms in #{spec.name}.podspec match requested platforms: #{configuration.platforms}"
113
+ end
114
+
115
+ spec_platforms
116
+ .map do |platform|
117
+ consumer = spec.consumer(platform)
118
+ target_name = "App-#{Platform.string_name(consumer.platform_name)}"
119
+ next if app_project.targets.map(&:name).include? target_name
120
+ native_app_target = Pod::Generator::AppTargetHelper.add_app_target(app_project, consumer.platform_name,
121
+ deployment_target(consumer), target_name)
122
+ # Temporarily set Swift version to pass validator checks for pods which do not specify Swift version.
123
+ # It will then be re-set again within #perform_post_install_steps.
124
+ Pod::Generator::AppTargetHelper.add_swift_version(native_app_target, Pod::Validator::DEFAULT_SWIFT_VERSION)
125
+ native_app_target
126
+ end
127
+ .compact.tap do
128
+ app_project.recreate_user_schemes do |scheme, target|
129
+ installation_result = installation_result_from_target(target)
130
+ next unless installation_result
131
+ installation_result.test_native_targets.each do |test_native_target|
132
+ scheme.add_test_target(test_native_target)
133
+ end
134
+ end
135
+ end
136
+ .each do |target|
137
+ Xcodeproj::XCScheme.share_scheme(app_project.path, target.name) if target
138
+ end
139
+ app_project.save
140
+ end
141
+
142
+ def deployment_target(consumer)
143
+ deployment_target = consumer.spec.deployment_target(consumer.platform_name)
144
+ if consumer.platform_name == :ios && configuration.use_frameworks?
145
+ minimum = Version.new('8.0')
146
+ deployment_target = [Version.new(deployment_target), minimum].max.to_s
147
+ end
148
+ deployment_target
149
+ end
150
+
151
+ def perform_post_install_steps(app_project, installer)
152
+ app_project.native_targets.each do |native_app_target|
153
+ remove_script_phase_from_target(native_app_target, 'Check Pods Manifest.lock')
154
+
155
+ pod_target = installer.pod_targets.find { |pt| pt.platform.name == native_app_target.platform_name && pt.pod_name == spec.name }
156
+ raise "Unable to find a pod target for #{native_app_target} / #{spec}" unless pod_target
157
+
158
+ native_app_target.source_build_phase.clear
159
+ native_app_target.resources_build_phase.clear
160
+
161
+ if (app_host_source_dir = configuration.app_host_source_dir)
162
+ relative_app_host_source_dir = app_host_source_dir.relative_path_from(installer.sandbox.root)
163
+ groups = {}
164
+
165
+ app_host_source_dir.find do |file|
166
+ relative_path = file.relative_path_from(app_host_source_dir)
167
+
168
+ if file.directory?
169
+ groups[relative_path] =
170
+ if (base_group = groups[relative_path.dirname])
171
+ basename = relative_path.basename
172
+ base_group.new_group(basename.to_s, basename)
173
+ else
174
+ app_project.new_group(native_app_target.name, relative_app_host_source_dir)
175
+ end
176
+
177
+ next
178
+ elsif file.to_s.end_with?('-Bridging-Header.h')
179
+ native_app_target.build_configurations.each do |bc|
180
+ if (old_bridging_header = bc.build_settings['SWIFT_OBJC_BRIDGING_HEADER'])
181
+ raise Informative, "Conflicting Swift ObjC bridging headers specified, got #{old_bridging_header} and #{relative_path}. Only one `-Bridging-Header.h` file may be specified in the app host source dir."
182
+ end
183
+
184
+ bc.build_settings['SWIFT_OBJC_BRIDGING_HEADER'] = relative_path.to_s
185
+ end
186
+ end
187
+
188
+ group = groups[relative_path.dirname]
189
+ source_file_ref = group.new_file(file.basename)
190
+ native_app_target.add_file_references([source_file_ref])
191
+ end
192
+ elsif Pod::Generator::AppTargetHelper.method(:add_app_project_import).arity == -5 # CocoaPods >= 1.6
193
+ # If we are doing incremental installation then the file might already be there.
194
+ platform_name = Platform.string_name(native_app_target.platform_name)
195
+ group = group_for_platform_name(app_project, platform_name)
196
+ main_file_ref = group.files.find { |f| f.display_name == 'main.m' }
197
+ if main_file_ref.nil?
198
+ Pod::Generator::AppTargetHelper.add_app_project_import(app_project, native_app_target, pod_target,
199
+ pod_target.platform.name, native_app_target.name)
200
+ else
201
+ native_app_target.add_file_references([main_file_ref])
202
+ end
203
+ else
204
+ Pod::Generator::AppTargetHelper.add_app_project_import(app_project, native_app_target, pod_target,
205
+ pod_target.platform.name,
206
+ pod_target.requires_frameworks?,
207
+ native_app_target.name)
208
+ end
209
+
210
+ # Set `PRODUCT_BUNDLE_IDENTIFIER`
211
+ native_app_target.build_configurations.each do |bc|
212
+ bc.build_settings['PRODUCT_BUNDLE_IDENTIFIER'] = 'org.cocoapods-generate.${PRODUCT_NAME:rfc1034identifier}'
213
+ end
214
+
215
+ case native_app_target.platform_name
216
+ when :ios
217
+ make_ios_app_launchable(app_project, native_app_target)
218
+ end
219
+
220
+ Pod::Generator::AppTargetHelper.add_swift_version(native_app_target, pod_target.swift_version) unless pod_target.swift_version.blank?
221
+ if installer.pod_targets.any? { |pt| pt.spec_consumers.any? { |c| c.frameworks.include?('XCTest') } }
222
+ Pod::Generator::AppTargetHelper.add_xctest_search_paths(native_app_target)
223
+ end
224
+
225
+ # Share the pods xcscheme only if it exists. For pre-built vendored pods there is no xcscheme generated.
226
+ if installer.respond_to?(:generated_projects) # CocoaPods 1.7.0
227
+ installer.generated_projects.each do |project|
228
+ Xcodeproj::XCScheme.share_scheme(project.path, pod_target.label) if File.exist?(project.path + pod_target.label)
229
+ end
230
+ elsif File.exist?(installer.pods_project.path + pod_target.label)
231
+ Xcodeproj::XCScheme.share_scheme(installer.pods_project.path, pod_target.label)
232
+ end
233
+
234
+ add_test_spec_schemes_to_app_scheme(installer, app_project)
235
+ end
236
+
237
+ app_project.save
238
+ end
239
+
240
+ def installation_result_from_target(target)
241
+ return unless target.respond_to?(:symbol_type)
242
+ library_product_types = %i[framework dynamic_library static_library]
243
+ return unless library_product_types.include? target.symbol_type
244
+
245
+ results_by_native_target[target]
246
+ end
247
+
248
+ def remove_script_phase_from_target(native_target, script_phase_name)
249
+ script_phase = native_target.shell_script_build_phases.find { |bp| bp.name && bp.name.end_with?(script_phase_name) }
250
+ return unless script_phase.present?
251
+ native_target.build_phases.delete(script_phase)
252
+ end
253
+
254
+ def add_test_spec_schemes_to_app_scheme(installer, app_project)
255
+ test_native_targets =
256
+ if installer.respond_to?(:target_installation_results) # CocoaPods >= 1.6
257
+ installer
258
+ .target_installation_results
259
+ .pod_target_installation_results
260
+ .values
261
+ .flatten(1)
262
+ .select { |installation_result| installation_result.target.pod_name == spec.root.name }
263
+ else
264
+ installer
265
+ .pod_targets
266
+ .select { |pod_target| pod_target.pod_name == spec.root.name }
267
+ end
268
+ .flat_map(&:test_native_targets)
269
+ .group_by(&:platform_name)
270
+
271
+ workspace_path = install_directory + "#{spec.name}.xcworkspace"
272
+ Xcodeproj::Plist.write_to_path(
273
+ { 'IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded' => false },
274
+ workspace_path.join('xcshareddata').tap(&:mkpath).join('WorkspaceSettings.xcsettings')
275
+ )
276
+
277
+ test_native_targets.each do |platform_name, test_targets|
278
+ app_scheme_path = Xcodeproj::XCScheme.shared_data_dir(app_project.path).join("App-#{Platform.string_name(platform_name)}.xcscheme")
279
+ raise "Missing app scheme for #{platform_name}: #{app_scheme_path.inspect}" unless app_scheme_path.file?
280
+
281
+ app_scheme = Xcodeproj::XCScheme.new(app_scheme_path)
282
+ test_action = app_scheme.test_action
283
+ existing_test_targets = test_action.testables.flat_map(&:buildable_references).map(&:target_name)
284
+
285
+ test_targets.sort_by(&:name).each do |target|
286
+ next if existing_test_targets.include?(target.name)
287
+
288
+ testable = Xcodeproj::XCScheme::TestAction::TestableReference.new(target)
289
+ testable.buildable_references.each do |buildable|
290
+ buildable.xml_element.attributes['ReferencedContainer'] = 'container:Pods.xcodeproj'
291
+ end
292
+ test_action.add_testable(testable)
293
+ end
294
+
295
+ app_scheme.save!
296
+ end
297
+ end
298
+
299
+ def make_ios_app_launchable(app_project, native_app_target)
300
+ platform_name = Platform.string_name(native_app_target.platform_name)
301
+ generated_source_dir = install_directory.join("App-#{platform_name}").tap(&:mkpath)
302
+
303
+ # Add `LaunchScreen.storyboard`
304
+ launch_storyboard = generated_source_dir.join('LaunchScreen.storyboard')
305
+ launch_storyboard.write <<-XML.strip_heredoc
306
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
307
+ <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" systemVersion="17A277" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
308
+ <dependencies>
309
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
310
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
311
+ </dependencies>
312
+ <scenes>
313
+ <!--View Controller-->
314
+ <scene sceneID="EHf-IW-A2E">
315
+ <objects>
316
+ <viewController id="01J-lp-oVM" sceneMemberID="viewController">
317
+ <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
318
+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
319
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
320
+ <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
321
+ </view>
322
+ </viewController>
323
+ <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
324
+ </objects>
325
+ <point key="canvasLocation" x="53" y="375"/>
326
+ </scene>
327
+ </scenes>
328
+ </document>
329
+ XML
330
+
331
+ # Add & wire `Info.plist`
332
+ info_plist_contents = {
333
+ 'CFBundleDevelopmentRegion' => '$(DEVELOPMENT_LANGUAGE)',
334
+ 'CFBundleExecutable' => '$(EXECUTABLE_NAME)',
335
+ 'CFBundleIdentifier' => '$(PRODUCT_BUNDLE_IDENTIFIER)',
336
+ 'CFBundleInfoDictionaryVersion' => '6.0',
337
+ 'CFBundleName' => '$(PRODUCT_NAME)',
338
+ 'CFBundlePackageType' => 'APPL',
339
+ 'CFBundleShortVersionString' => '1.0',
340
+ 'CFBundleVersion' => '1',
341
+ 'LSRequiresIPhoneOS' => true,
342
+ 'UILaunchStoryboardName' => 'LaunchScreen',
343
+ 'UIRequiredDeviceCapabilities' => [
344
+ 'armv7'
345
+ ],
346
+ 'UISupportedInterfaceOrientations' => %w[
347
+ UIInterfaceOrientationPortrait
348
+ UIInterfaceOrientationLandscapeLeft
349
+ UIInterfaceOrientationLandscapeRight
350
+ ],
351
+ 'UISupportedInterfaceOrientations~ipad' => %w[
352
+ UIInterfaceOrientationPortrait
353
+ UIInterfaceOrientationPortraitUpsideDown
354
+ UIInterfaceOrientationLandscapeLeft
355
+ UIInterfaceOrientationLandscapeRight
356
+ ]
357
+ }
358
+ info_plist_path = generated_source_dir.join('Info.plist')
359
+ Xcodeproj::Plist.write_to_path(info_plist_contents, info_plist_path)
360
+
361
+ native_app_target.build_configurations.each do |bc|
362
+ bc.build_settings['INFOPLIST_FILE'] = "${SRCROOT}/App-#{platform_name}/Info.plist"
363
+ end
364
+
365
+ group = group_for_platform_name(app_project, platform_name)
366
+ group.files.find { |f| f.display_name == 'Info.plist' } || group.new_file(info_plist_path)
367
+ launch_storyboard_file_ref = group.files.find { |f| f.display_name == 'LaunchScreen.storyboard' } || group.new_file(launch_storyboard)
368
+ native_app_target.resources_build_phase.add_file_reference(launch_storyboard_file_ref)
369
+ end
370
+
371
+ def group_for_platform_name(project, platform_name, should_create = true)
372
+ project.main_group.find_subpath("App-#{platform_name}", should_create)
373
+ end
374
+
375
+ def print_post_install_message
376
+ workspace_path = install_directory.join(podfile.workspace_path)
377
+
378
+ if configuration.auto_open?
379
+ configuration.pod_config.with_changes(verbose: true) do
380
+ Executable.execute_command 'open', [workspace_path]
381
+ end
382
+ else
383
+ UI.info "Open #{UI.path workspace_path} to work on #{spec.name}"
384
+ end
385
+ end
386
+ end
387
+ end
388
+ end