class Pod::Generate::PodfileGenerator

Generates podfiles for pod specifications given a configuration.

Attributes

configuration[R]

@return [Configuration]

the configuration used when generating podfiles

Public Class Methods

new(configuration) click to toggle source
# File lib/cocoapods/generate/podfile_generator.rb, line 13
def initialize(configuration)
  @configuration = configuration
end

Public Instance Methods

dependency_compilation_kwargs(pod_name) click to toggle source

@return [Hash]

a hash with "compilation"-related dependency options for the `pod` DSL method

@param [String] pod_name

# File lib/cocoapods/generate/podfile_generator.rb, line 235
def dependency_compilation_kwargs(pod_name)
  options = {}
  options[:inhibit_warnings] = inhibit_warnings?(pod_name) if inhibit_warnings?(pod_name) != inhibit_all_warnings?
  options[:modular_headers] = modular_headers?(pod_name) if modular_headers?(pod_name) != use_modular_headers?
  options
end
inhibit_all_warnings?() click to toggle source

@return [Boolean]

whether all warnings should be inhibited
# File lib/cocoapods/generate/podfile_generator.rb, line 185
def inhibit_all_warnings?
  return false unless configuration.use_podfile?
  target_definition_list.all? do |target_definition|
    target_definition.send(:inhibit_warnings_hash)['all']
  end
end
installation_options() click to toggle source
# File lib/cocoapods/generate/podfile_generator.rb, line 337
def installation_options
  installation_options = {
    deterministic_uuids: configuration.deterministic_uuids?,
    share_schemes_for_development_pods: configuration.share_schemes_for_development_pods,
    warn_for_multiple_pod_sources: configuration.warn_for_multiple_pod_sources?
  }

  if Pod::Installer::InstallationOptions.all_options.include?('generate_multiple_pod_projects')
    installation_options[:generate_multiple_pod_projects] = configuration.generate_multiple_pod_projects?
  end

  if Pod::Installer::InstallationOptions.all_options.include?('incremental_installation')
    installation_options[:incremental_installation] = configuration.incremental_installation?
  end

  if Pod::Installer::InstallationOptions.all_options.include?('disable_input_output_paths')
    installation_options[:disable_input_output_paths] = configuration.disable_input_output_paths
  end

  installation_options
end
lockfile_versions() click to toggle source

@return [Hash<String,String>]

versions in the lockfile keyed by pod name
# File lib/cocoapods/generate/podfile_generator.rb, line 281
def lockfile_versions
  return {} unless configuration.use_lockfile_versions?
  @lockfile_versions ||= Hash[configuration.lockfile.pod_names.map { |name| [name, "= #{configuration.lockfile.version(name)}"] }]
end
pod_args_for_dependency(podfile, dependency) click to toggle source

@return [Hash<String,Array<Dependency>>]

returns the arguments that should be passed to the Podfile DSL's
`pod` method for the given podfile and dependency

@param [Podfile] podfile

@param [Dependency] dependency

# File lib/cocoapods/generate/podfile_generator.rb, line 294
def pod_args_for_dependency(podfile, dependency)
  dependency = podfile_dependencies[dependency.root_name]
               .map { |dep| dep.dup.tap { |d| d.name = dependency.name } }
               .push(dependency)
               .reduce(&:merge)

  options = dependency_compilation_kwargs(dependency.name)
  options[:source] = dependency.podspec_repo if dependency.podspec_repo
  options.update(dependency.external_source) if dependency.external_source
  %i[path podspec].each do |key|
    next unless (path = options[key])
    options[key] = Pathname(path)
                   .expand_path(configuration.podfile.defined_in_file.dirname)
                   .relative_path_from(podfile.defined_in_file.dirname)
                   .to_s
  end
  args = [dependency.name]
  if dependency.external_source.nil?
    requirements = dependency.requirement.as_list
    if (version = lockfile_versions[dependency.name])
      requirements << version
    end
    args.concat requirements.uniq
  end
  args << options unless options.empty?
  args
end
podfile_dependencies() click to toggle source

@return [Hash<String,Array<Dependency>>]

dependencies in the podfile grouped by root name
# File lib/cocoapods/generate/podfile_generator.rb, line 273
def podfile_dependencies
  return {} unless configuration.use_podfile?
  @podfile_dependencies ||= configuration.podfile.dependencies.group_by(&:root_name).tap { |h| h.default = [] }
end
podfile_for_specs(specs) click to toggle source

@return [Podfile] a podfile suitable for installing the given spec

@param [Array<Specification>] specs

# File lib/cocoapods/generate/podfile_generator.rb, line 30
def podfile_for_specs(specs)
  generator = self
  dir = configuration.gen_dir_for_specs(specs)
  project_name = configuration.project_name_for_specs(specs)

  Pod::Podfile.new do
    project "#{project_name}.xcodeproj"
    workspace "#{project_name}.xcworkspace"

    plugin 'cocoapods-generate'

    install! 'cocoapods', generator.installation_options

    generator.podfile_plugins.each do |name, options|
      plugin(*[name, options].compact)
    end

    use_frameworks!(generator.use_frameworks_value)

    if (supported_swift_versions = generator.supported_swift_versions)
      supports_swift_versions(supported_swift_versions)
    end

    # Explicitly set sources
    generator.configuration.sources.each do |source_url|
      source(source_url)
    end

    self.defined_in_file = dir.join('CocoaPods.podfile.yaml')

    test_specs_by_spec = Hash[specs.map do |spec|
      [spec, spec.recursive_subspecs.select(&:test_specification?)]
    end]
    app_specs_by_spec = Hash[specs.map do |spec|
      app_specs = if spec.respond_to?(:app_specification?)
                    spec.recursive_subspecs.select(&:app_specification?)
                  else
                    []
                  end
      [spec, app_specs]
    end]

    # Stick all of the transitive dependencies in an abstract target.
    # This allows us to force CocoaPods to use the versions / sources / external sources
    # that we want.
    abstract_target 'Transitive Dependencies' do
      pods_for_transitive_dependencies = specs.flat_map do |spec|
        [spec.name]
          .concat(test_specs_by_spec.keys.map(&:name))
          .concat(test_specs_by_spec.values.flatten.flat_map { |ts| ts.dependencies.flat_map(&:name) })
          .concat(app_specs_by_spec.keys.map(&:name))
          .concat(app_specs_by_spec.values.flatten.flat_map { |as| as.dependencies.flat_map(&:name) })
      end
      pods_for_transitive_dependencies.uniq!

      spec_names = specs.map { |s| s.root.name }.to_set
      dependencies = generator
                     .transitive_dependencies_by_pod
                     .values_at(*pods_for_transitive_dependencies)
                     .compact
                     .flatten(1)
                     .uniq
                     .sort_by(&:name)
                     .reject { |d| spec_names.include?(d.root_name) }

      dependencies.each do |dependency|
        pod_args = generator.pod_args_for_dependency(self, dependency)
        pod(*pod_args)
      end
    end

    # Add platform-specific concrete targets that inherit the `pod` declaration for the local pod.
    spec_platform_names = specs.flat_map { |s| s.available_platforms.map(&:string_name) }.uniq.each.reject do |platform_name|
      !generator.configuration.platforms.nil? && !generator.configuration.platforms.include?(platform_name.downcase)
    end

    spec_platform_names.sort.each do |platform_name|
      target "App-#{platform_name}" do
        current_target_definition.swift_version = generator.swift_version if generator.swift_version
      end
    end

    # this block has to come _before_ inhibit_all_warnings! / use_modular_headers!,
    # and the local `pod` declaration
    current_target_definition.instance_exec do
      transitive_dependencies = children.find { |c| c.name == 'Transitive Dependencies' }

      %w[use_modular_headers inhibit_warnings].each do |key|
        value = transitive_dependencies.send(:internal_hash).delete(key)
        next if value.blank?
        set_hash_value(key, value)
      end
    end

    inhibit_all_warnings! if generator.inhibit_all_warnings?
    use_modular_headers! if generator.use_modular_headers?

    specs.each do |spec|
      # This is the pod declaration for the local pod,
      # it will be inherited by the concrete target definitions below
      pod_options = generator.dependency_compilation_kwargs(spec.name)

      path = spec.defined_in_file.relative_path_from(dir).to_s
      pod_options[:path] = path
      { testspecs: test_specs_by_spec[spec], appspecs: app_specs_by_spec[spec] }.each do |key, subspecs|
        pod_options[key] = subspecs.map { |s| s.name.sub(%r{^#{Regexp.escape spec.root.name}/}, '') }.sort unless subspecs.blank?
      end
      pod spec.name, **pod_options
    end

    # Implement local-sources option to set up dependencies to podspecs in the local filesystem.
    next if generator.configuration.local_sources.empty?
    specs.each do |spec|
      generator.transitive_local_dependencies(spec, generator.configuration.local_sources).sort_by(&:first).each do |dependency, podspec_file|
        pod_options = generator.dependency_compilation_kwargs(dependency.name)
        pod_options[:path] = if podspec_file[0] == '/' # absolute path
                               podspec_file
                             else
                               '../../' + podspec_file
                             end
        pod dependency.name, **pod_options
      end
    end
  end
end
podfile_plugins() click to toggle source
# File lib/cocoapods/generate/podfile_generator.rb, line 359
def podfile_plugins
  configuration.podfile_plugins.merge('cocoapods-disable-podfile-validations' => { 'no_abstract_only_pods' => true }) do |_key, old_value, new_value|
    old_value.merge(new_value)
  end
end
podfiles_by_specs() click to toggle source

@return [Hash<Array<Specification>, Podfile>] the podfiles keyed by the specs that are part of each.

# File lib/cocoapods/generate/podfile_generator.rb, line 19
def podfiles_by_specs
  return { configuration.podspecs => podfile_for_specs(configuration.podspecs) } if configuration.single_workspace?
  Hash[configuration.podspecs.map do |spec|
    [[spec], podfile_for_specs([spec])]
  end]
end
supported_swift_versions() click to toggle source
# File lib/cocoapods/generate/podfile_generator.rb, line 326
def supported_swift_versions
  return unless configuration.use_podfile?
  return if target_definition_list.empty?
  return unless target_definition_list.first.respond_to?(:swift_version_requirements)
  target_definition_list.reduce(nil) do |supported_swift_versions, target_definition|
    target_swift_versions = target_definition.swift_version_requirements
    next supported_swift_versions unless target_swift_versions
    Array(target_swift_versions) | Array(supported_swift_versions)
  end
end
swift_version() click to toggle source
# File lib/cocoapods/generate/podfile_generator.rb, line 322
def swift_version
  @swift_version ||= target_definition_list.map(&:swift_version).compact.max
end
transitive_dependencies_by_pod() click to toggle source

@return [Hash<String,Array<Dependency>>]

the transitive dependency objects dependency upon by each pod
# File lib/cocoapods/generate/podfile_generator.rb, line 245
def transitive_dependencies_by_pod
  return {} unless configuration.use_lockfile?
  @transitive_dependencies_by_pod ||= begin
    lda = ::Pod::Installer::Analyzer::LockingDependencyAnalyzer
    dependency_graph = Molinillo::DependencyGraph.new
    configuration.lockfile.dependencies.each do |dependency|
      dependency_graph.add_vertex(dependency.name, dependency, true)
    end
    add_to_dependency_graph = if lda.method(:add_to_dependency_graph).parameters.size == 4 # CocoaPods < 1.6.0
                                ->(pod) { lda.add_to_dependency_graph(pod, [], dependency_graph, []) }
                              else
                                ->(pod) { lda.add_to_dependency_graph(pod, [], dependency_graph, [], Set.new) }
                              end
    configuration.lockfile.internal_data['PODS'].each(&add_to_dependency_graph)

    transitive_dependencies_by_pod = Hash.new { |hash, key| hash[key] = [] }
    dependency_graph.each do |v|
      transitive_dependencies_by_pod[v.name].concat v.recursive_successors.map(&:payload) << v.payload
    end

    transitive_dependencies_by_pod.each_value(&:uniq!)
    transitive_dependencies_by_pod
  end
end
transitive_local_dependencies(spec, paths, found_podspecs: {}, include_non_library_subspecs: true) click to toggle source
# File lib/cocoapods/generate/podfile_generator.rb, line 156
def transitive_local_dependencies(spec, paths, found_podspecs: {}, include_non_library_subspecs: true)
  if include_non_library_subspecs
    non_library_specs = spec.recursive_subspecs.select do |ss|
      ss.test_specification? || (ss.respond_to?(:app_specification?) && ss.app_specification?)
    end
    non_library_specs.each do |subspec|
      transitive_local_dependencies(subspec, paths, found_podspecs: found_podspecs, include_non_library_subspecs: false)
    end
  end
  spec.dependencies.each do |dependency|
    next if found_podspecs.key?(dependency)
    found_podspec_file = nil
    name = dependency.name.split('/')[0]
    paths.each do |path|
      podspec_file = File.join(path, name + '.podspec')
      next unless File.file?(podspec_file)
      found_podspec_file = podspec_file
      break
    end
    next unless found_podspec_file
    found_podspecs[dependency] = found_podspec_file.sub(%r{\A\./}, '')
    transitive_local_dependencies(Pod::Specification.from_file(found_podspec_file), paths, found_podspecs: found_podspecs)
  end
  found_podspecs
end
use_frameworks_value() click to toggle source

@return [Boolean, Hash]

the value to use for `use_frameworks!` DSL directive
# File lib/cocoapods/generate/podfile_generator.rb, line 212
def use_frameworks_value
  return configuration.use_frameworks? unless configuration.use_podfile?
  use_framework_values = target_definition_list.map do |target_definition|
    if target_definition.respond_to?(:build_type) # CocoaPods >= 1.9
      build_type = target_definition.build_type
      if build_type.static_library?
        false
      else
        { linkage: build_type == BuildType.dynamic_framework ? :dynamic : :static }
      end
    else
      target_definition.uses_frameworks?
    end
  end.uniq
  raise Informative, 'Multiple use_frameworks! values detected in user Podfile.' unless use_framework_values.count == 1
  use_framework_values.first
end
use_modular_headers?() click to toggle source

@return [Boolean]

whether all pods should use modular headers
# File lib/cocoapods/generate/podfile_generator.rb, line 195
def use_modular_headers?
  if configuration.use_podfile? && configuration.use_modular_headers?
    raise Informative, 'Conflicting `use_modular_headers` option. Cannot specify both `--use-modular-headers` and `--use-podfile`.'
  end

  if configuration.use_podfile?
    target_definition_list.all? do |target_definition|
      target_definition.use_modular_headers_hash['all']
    end
  else
    configuration.use_modular_headers?
  end
end

Private Instance Methods

inhibit_warnings?(pod_name) click to toggle source

@return [Boolean]

whether warnings should be inhibited for the given pod

@param [String] pod_name

# File lib/cocoapods/generate/podfile_generator.rb, line 384
def inhibit_warnings?(pod_name)
  return false unless configuration.use_podfile?
  target_definitions_for_pod(pod_name).all? do |target_definition|
    target_definition.inhibits_warnings_for_pod?(pod_name)
  end
end
modular_headers?(pod_name) click to toggle source

@return [Boolean]

whether modular headers should be enabled for the given pod

@param [String] pod_name

# File lib/cocoapods/generate/podfile_generator.rb, line 396
def modular_headers?(pod_name)
  return true if configuration.use_modular_headers?
  return false unless configuration.use_podfile?
  target_definitions_for_pod(pod_name).all? do |target_definition|
    target_definition.build_pod_as_module?(pod_name)
  end
end
target_definition_list() click to toggle source

@return [Array<Podfile::TargetDefinition>]

a list of all target definitions to consider from the podfile
# File lib/cocoapods/generate/podfile_generator.rb, line 370
def target_definition_list
  return [] unless configuration.use_podfile?
  @target_definition_list ||= begin
    list = configuration.podfile.target_definition_list
    list.reject!(&:abstract?) unless list.all?(&:abstract?)
    list
  end
end
target_definitions_for_pod(pod_name) click to toggle source

@return [Podfile::TargetDefinition]

@param [String] pod_name

# File lib/cocoapods/generate/podfile_generator.rb, line 408
def target_definitions_for_pod(pod_name)
  target_definitions = target_definition_list.reject { |td| td.dependencies.none? { |d| d.name == pod_name } }
  target_definitions.empty? ? target_definition_list : target_definitions
end