class Pod::Generate::Installer
Responsible for creating a workspace for a single specification, given a configuration and a generated podfile.
Attributes
configuration[R]
@return [Configuration]
the configuration to use when installing
podfile[R]
@return [Podfile]
the podfile to install
spec[R]
@return [Specification]
the spec whose workspace is being created
Public Class Methods
new(configuration, spec, podfile)
click to toggle source
# File lib/cocoapods/generate/installer.rb, line 22 def initialize(configuration, spec, podfile) @configuration = configuration @spec = spec @podfile = podfile end
Public Instance Methods
install!()
click to toggle source
Installs the {podfile} into the {install_directory}
@return [void]
# File lib/cocoapods/generate/installer.rb, line 39 def install! UI.title "Generating #{spec.name} in #{UI.path install_directory}" do clean! if configuration.clean? install_directory.mkpath UI.message 'Creating stub application' do create_app_project end UI.message 'Writing Podfile' do podfile.defined_in_file.open('w') { |f| f << podfile.to_yaml } end installer = nil UI.section 'Installing...' do configuration.pod_config.with_changes(installation_root: install_directory, podfile: podfile, lockfile: configuration.lockfile, sandbox: nil, sandbox_root: install_directory, podfile_path: podfile.defined_in_file, silent: !configuration.pod_config.verbose?, verbose: false, lockfile_path: nil) do installer = ::Pod::Installer.new(configuration.pod_config.sandbox, podfile, configuration.lockfile) installer.use_default_plugins = configuration.use_default_plugins installer.install! end end UI.section 'Performing post-installation steps' do perform_post_install_steps(open_app_project, installer) end print_post_install_message end end
install_directory()
click to toggle source
@return [Pathname]
The directory that pods will be installed into
# File lib/cocoapods/generate/installer.rb, line 31 def install_directory @install_directory ||= podfile.defined_in_file.dirname end
Private Instance Methods
add_test_spec_schemes_to_app_scheme(installer, app_project)
click to toggle source
# File lib/cocoapods/generate/installer.rb, line 219 def add_test_spec_schemes_to_app_scheme(installer, app_project) test_native_targets = if installer.respond_to?(:target_installation_results) # CocoaPods >= 1.6 installer .target_installation_results .pod_target_installation_results .values .flatten(1) .select { |installation_result| installation_result.target.pod_name == spec.root.name } else installer .pod_targets .select { |pod_target| pod_target.pod_name == spec.root.name } end .flat_map(&:test_native_targets) .group_by(&:platform_name) Xcodeproj::Plist.write_to_path( { 'IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded' => false }, app_project.path.sub_ext('.xcworkspace').join('xcshareddata').tap(&:mkpath).join('WorkspaceSettings.xcsettings') ) test_native_targets.each do |platform_name, test_targets| app_scheme_path = Xcodeproj::XCScheme.shared_data_dir(app_project.path).join("App-#{Platform.string_name(platform_name)}.xcscheme") raise "Missing app scheme for #{platform_name}: #{app_scheme_path.inspect}" unless app_scheme_path.file? app_scheme = Xcodeproj::XCScheme.new(app_scheme_path) test_action = app_scheme.test_action existing_test_targets = test_action.testables.flat_map(&:buildable_references).map(&:target_name) test_targets.sort_by(&:name).each do |target| next if existing_test_targets.include?(target.name) testable = Xcodeproj::XCScheme::TestAction::TestableReference.new(target) testable.buildable_references.each do |buildable| buildable.xml_element.attributes['ReferencedContainer'] = 'container:Pods.xcodeproj' end test_action.add_testable(testable) end app_scheme.save! end end
clean!()
click to toggle source
Removes the {install_directory}
@return [void]
# File lib/cocoapods/generate/installer.rb, line 75 def clean! UI.message 'Cleaning gen install directory' do FileUtils.rm_rf install_directory end end
create_app_project()
click to toggle source
Creates an app project that CocoaPods will integrate into
@return [Xcodeproj::Project]
# File lib/cocoapods/generate/installer.rb, line 94 def create_app_project app_project = open_app_project(recreate: true) spec.available_platforms.map do |platform| consumer = spec.consumer(platform) target_name = "App-#{Platform.string_name(consumer.platform_name)}" native_app_target = Pod::Generator::AppTargetHelper.add_app_target(app_project, consumer.platform_name, deployment_target(consumer), target_name) # Temporarily set Swift version to pass validator checks for pods which do not specify Swift version. # It will then be re-set again within #perform_post_install_steps. Pod::Generator::AppTargetHelper.add_swift_version(native_app_target, Pod::Validator::DEFAULT_SWIFT_VERSION) native_app_target end .tap do app_project.recreate_user_schemes do |scheme, target| installation_result = installation_result_from_target(target) next unless installation_result installation_result.test_native_targets.each do |test_native_target| scheme.add_test_target(test_native_target) end end end .each do |target| Xcodeproj::XCScheme.share_scheme(app_project.path, target.name) end app_project.save end
deployment_target(consumer)
click to toggle source
# File lib/cocoapods/generate/installer.rb, line 122 def deployment_target(consumer) deployment_target = consumer.spec.deployment_target(consumer.platform_name) if consumer.platform_name == :ios && configuration.use_frameworks? minimum = Version.new('8.0') deployment_target = [Version.new(deployment_target), minimum].max.to_s end deployment_target end
installation_result_from_target(target)
click to toggle source
# File lib/cocoapods/generate/installer.rb, line 205 def installation_result_from_target(target) return unless target.respond_to?(:symbol_type) library_product_types = %i[framework dynamic_library static_library] return unless library_product_types.include? target.symbol_type results_by_native_target[target] end
make_ios_app_launchable(installer, app_project, native_app_target)
click to toggle source
# File lib/cocoapods/generate/installer.rb, line 263 def make_ios_app_launchable(installer, app_project, native_app_target) platform_name = Platform.string_name(native_app_target.platform_name) generated_source_dir = installer.sandbox.root.join('App', platform_name).tap(&:mkpath) # Add `LaunchScreen.storyboard` launch_storyboard = generated_source_dir.join('LaunchScreen.storyboard') launch_storyboard.write <<-XML.strip_heredoc <?xml version="1.0" encoding="UTF-8" standalone="no"?> <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"> <dependencies> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> </dependencies> <scenes> <!--View Controller--> <scene sceneID="EHf-IW-A2E"> <objects> <viewController id="01J-lp-oVM" sceneMemberID="viewController"> <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3"> <rect key="frame" x="0.0" y="0.0" width="375" height="667"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> </view> </viewController> <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/> </objects> <point key="canvasLocation" x="53" y="375"/> </scene> </scenes> </document> XML # Add & wire `Info.plist` info_plist_contents = { 'CFBundleDevelopmentRegion' => '$(DEVELOPMENT_LANGUAGE)', 'CFBundleExecutable' => '$(EXECUTABLE_NAME)', 'CFBundleIdentifier' => '$(PRODUCT_BUNDLE_IDENTIFIER)', 'CFBundleInfoDictionaryVersion' => '6.0', 'CFBundleName' => '$(PRODUCT_NAME)', 'CFBundlePackageType' => 'APPL', 'CFBundleShortVersionString' => '1.0', 'CFBundleVersion' => '1', 'LSRequiresIPhoneOS' => true, 'UILaunchStoryboardName' => 'LaunchScreen', 'UIRequiredDeviceCapabilities' => [ 'armv7' ], 'UISupportedInterfaceOrientations' => %w[ UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ], 'UISupportedInterfaceOrientations~ipad' => %w[ UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight ] } info_plist_path = generated_source_dir.join('Info.plist') Xcodeproj::Plist.write_to_path(info_plist_contents, info_plist_path) native_app_target.build_configurations.each do |bc| bc.build_settings['INFOPLIST_FILE'] = "${SRCROOT}/App/#{platform_name}/Info.plist" end group = app_project.main_group.find_subpath("App-#{platform_name}", true) group.new_file(info_plist_path) native_app_target.resources_build_phase.add_file_reference group.new_file(launch_storyboard) end
open_app_project(recreate: false)
click to toggle source
# File lib/cocoapods/generate/installer.rb, line 81 def open_app_project(recreate: false) app_project_path = install_directory.join("#{spec.name}.xcodeproj") if !recreate && app_project_path.exist? Xcodeproj::Project.open(app_project_path) else Xcodeproj::Project.new(app_project_path) end end
perform_post_install_steps(app_project, installer)
click to toggle source
# File lib/cocoapods/generate/installer.rb, line 131 def perform_post_install_steps(app_project, installer) app_project.native_targets.each do |native_app_target| remove_script_phase_from_target(native_app_target, 'Check Pods Manifest.lock') pod_target = installer.pod_targets.find { |pt| pt.platform.name == native_app_target.platform_name && pt.pod_name == spec.name } raise "unable to find a pod target for #{native_app_target} / #{spec}" unless pod_target if (app_host_source_dir = configuration.app_host_source_dir) relative_app_host_source_dir = app_host_source_dir.relative_path_from(installer.sandbox.root) groups = {} app_host_source_dir.find do |file| relative_path = file.relative_path_from(app_host_source_dir) if file.directory? groups[relative_path] = if (base_group = groups[relative_path.dirname]) basename = relative_path.basename base_group.new_group(basename.to_s, basename) else app_project.new_group(native_app_target.name, relative_app_host_source_dir) end next elsif file.to_s.end_with?('-Bridging-Header.h') native_app_target.build_configurations.each do |bc| if (old_bridging_header = bc.build_settings['SWIFT_OBJC_BRIDGING_HEADER']) 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." end bc.build_settings['SWIFT_OBJC_BRIDGING_HEADER'] = relative_path.to_s end end group = groups[relative_path.dirname] source_file_ref = group.new_file(file.basename) native_app_target.add_file_references([source_file_ref]) end elsif Pod::Generator::AppTargetHelper.method(:add_app_project_import).arity == -5 # CocoaPods >= 1.6 Pod::Generator::AppTargetHelper.add_app_project_import(app_project, native_app_target, pod_target, pod_target.platform.name, native_app_target.name) else Pod::Generator::AppTargetHelper.add_app_project_import(app_project, native_app_target, pod_target, pod_target.platform.name, pod_target.requires_frameworks?, native_app_target.name) end # Set `PRODUCT_BUNDLE_IDENTIFIER` native_app_target.build_configurations.each do |bc| bc.build_settings['PRODUCT_BUNDLE_IDENTIFIER'] = 'org.cocoapods-generate.${PRODUCT_NAME:rfc1034identifier}' end case native_app_target.platform_name when :ios make_ios_app_launchable(installer, app_project, native_app_target) end Pod::Generator::AppTargetHelper.add_swift_version(native_app_target, pod_target.swift_version) unless pod_target.swift_version.blank? if installer.pod_targets.any? { |pt| pt.spec_consumers.any? { |c| c.frameworks.include?('XCTest') } } Pod::Generator::AppTargetHelper.add_xctest_search_paths(native_app_target) end # Share the pods xcscheme only if it exists. For pre-built vendored pods there is no xcscheme generated. if installer.respond_to?(:generated_projects) # CocoaPods 1.7.0 installer.generated_projects.each do |project| Xcodeproj::XCScheme.share_scheme(project.path, pod_target.label) if File.exist?(project.path + pod_target.label) end elsif File.exist?(installer.pods_project.path + pod_target.label) Xcodeproj::XCScheme.share_scheme(installer.pods_project.path, pod_target.label) end add_test_spec_schemes_to_app_scheme(installer, app_project) end app_project.save end
print_post_install_message()
click to toggle source
# File lib/cocoapods/generate/installer.rb, line 334 def print_post_install_message workspace_path = install_directory.join(podfile.workspace_path) if configuration.auto_open? configuration.pod_config.with_changes(verbose: true) do Executable.execute_command 'open', [workspace_path] end else UI.info "Open #{UI.path workspace_path} to work on #{spec.name}" end end
remove_script_phase_from_target(native_target, script_phase_name)
click to toggle source
# File lib/cocoapods/generate/installer.rb, line 213 def remove_script_phase_from_target(native_target, script_phase_name) script_phase = native_target.shell_script_build_phases.find { |bp| bp.name && bp.name.end_with?(script_phase_name) } return unless script_phase.present? native_target.build_phases.delete(script_phase) end