cocoapods-modularization 0.0.2

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