class XcodeArchiveCache::BuildGraph::NativeTargetFinder

Attributes

all_targets[RW]

@return [Array<Xcodeproj::Project::Object::PBXNativeTarget>]

build_configuration_name[R]

@return [String]

platform_name[RW]

@return [String]

Public Class Methods

new(projects, build_configuration_name) click to toggle source

@param [Array<Xcodeproj::Project>] projects @param [String] build_configuration_name

# File lib/build_graph/native_target_finder.rb, line 8
def initialize(projects, build_configuration_name)
  @all_targets = extract_targets(projects)
  @build_configuration_name = build_configuration_name
  @interpolator = XcodeArchiveCache::BuildSettings::StringInterpolator.new

  setup_product_name_to_target_mapping
end

Public Instance Methods

extract_targets(projects) click to toggle source

@param [Array<Xcodeproj::Project>] projects

@return [Array<Xcodeproj::Project::Object::PBXNativeTarget>]

# File lib/build_graph/native_target_finder.rb, line 20
def extract_targets(projects)
  projects
    .map {|project| unnest(project)}
    .flatten
    .sort_by(&:path)
    .inject([]) {|unique, current| unique.last && unique.last.path == current.path ? unique : unique + [current]}
    .map(&:native_targets)
    .flatten
    .select {|target| !target.test_target_type?}
end
find_all_dependencies(target) click to toggle source

@param [Xcodeproj::Project::Object::PBXAbstractTarget] target

@return [Array<Xcodeproj::Project::Object::PBXAbstractTarget>]

# File lib/build_graph/native_target_finder.rb, line 55
def find_all_dependencies(target)
  direct_dependencies = target
                          .dependencies
                          .map {|dependency| find_any_for_dependency(dependency)}
  linked_dependencies = []
  
  if target.is_a?(Xcodeproj::Project::Object::PBXNativeTarget) 
    linked_dependencies = find_linked_dependencies(target)
  end

  join(direct_dependencies, linked_dependencies)
end
find_any_for_dependency(dependency) click to toggle source
# File lib/build_graph/native_target_finder.rb, line 78
def find_any_for_dependency(dependency)
  target = dependency.target ? dependency.target : dependency.target_proxy.proxied_object
  target && target.platform_name == platform_name ? target : nil
end
find_for_dependency(dependency) click to toggle source

@param [Xcodeproj::Project::Object::PBXTargetDependency] dependency

@return [Xcodeproj::Project::Object::PBXNativeTarget]

# File lib/build_graph/native_target_finder.rb, line 72
def find_for_dependency(dependency)
  # targets from embedded projects are proxied
  target = find_any_for_dependency(dependency)
  target.is_a?(Xcodeproj::Project::Object::PBXNativeTarget) ? target : nil
end
find_for_file(file) click to toggle source

@param [Xcodeproj::Project::Object::PBXBuildFile] file

@return [Xcodeproj::Project::Object::PBXNativeTarget]

# File lib/build_graph/native_target_finder.rb, line 87
def find_for_file(file)
  if file.file_ref.is_a?(Xcodeproj::Project::Object::PBXReferenceProxy)
    product_reference_uuid = file.file_ref.remote_ref.remote_global_id_string
    target = find_with_product_ref_uuid(product_reference_uuid)
    if target == nil
      project = file.file_ref.remote_ref.container_portal_object
      target = find_in_project(project, product_reference_uuid)

      # allow all targets from this project
      # to be linked to that exact project
      #
      # otherwise, injection will operate on different Xcodeproj::Project objects
      # resulting to only the last target being actually removed
      #
      @all_targets += extract_targets([project])
    end

    if target == nil
      raise XcodeArchiveCache::Informative, "Target for #{file.file_ref.path} not found"
    end

    target
  elsif file.file_ref.is_a?(Xcodeproj::Project::Object::PBXFileReference)
    # products of sibling project targets are added as PBXFileReferences
    targets = find_with_product_path(file.file_ref.path)
    if targets.length > 1
      raise XcodeArchiveCache::Informative, "Found more than one target with product #{File.basename(file.file_ref.path)} in:\n#{targets.map(&:project)}"
    end

    targets.first
  end
end
find_for_product_name(product_name) click to toggle source

@param [String] product_name

@return [Xcodeproj::Project::Object::PBXNativeTarget]

# File lib/build_graph/native_target_finder.rb, line 124
def find_for_product_name(product_name)
  canonical = all_targets
    .select {|native_target| native_target.name == product_name || native_target.product_reference.display_name == product_name}
    .first
  
  parsed = @product_name_to_target[product_name]

  canonical ? canonical : parsed
end
find_native_dependencies(target) click to toggle source

@param [Xcodeproj::Project::Object::PBXNativeTarget] target

@return [Array<Xcodeproj::Project::Object::PBXNativeTarget>]

# File lib/build_graph/native_target_finder.rb, line 43
def find_native_dependencies(target)
  direct_dependencies = target
                          .dependencies
                          .map {|dependency| find_for_dependency(dependency)}
  linked_dependencies = find_linked_dependencies(target)
  join(direct_dependencies, linked_dependencies)
end
set_platform_name_filter(platform_name) click to toggle source

@param [String] platform_name

@return [Array<Xcodeproj::Project::Object::PBXNativeTarget>]

# File lib/build_graph/native_target_finder.rb, line 35
def set_platform_name_filter(platform_name)
  @platform_name = platform_name
end

Private Instance Methods

find_in_project(project, uuid) click to toggle source

@param [Xcodeproj::Project] project @param [String] uuid

@return [Xcodeproj::Project::Object::PBXNativeTarget]

# File lib/build_graph/native_target_finder.rb, line 223
def find_in_project(project, uuid)
  project.native_targets.select {|target| target.product_reference.uuid == uuid}.first
end
find_linked_dependencies(target) click to toggle source

@param [Xcodeproj::Project::Object::PBXNativeTarget] target

@return [Array<Xcodeproj::Project::Object::PBXNativeTarget>]

# File lib/build_graph/native_target_finder.rb, line 192
def find_linked_dependencies(target)
  target
    .frameworks_build_phase
    .files
    .map {|file| find_for_file(file)}
end
find_with_product_path(path) click to toggle source

@param [String] path

@return [Array<Xcodeproj::Project::Object::PBXNativeTarget>]

# File lib/build_graph/native_target_finder.rb, line 231
def find_with_product_path(path)
  canonical = all_targets.select {|target| target.platform_name == platform_name && target.product_reference.path == path }
  parsed = @product_name_to_target[File.basename(path)]

  if canonical.length > 0
    canonical
  elsif parsed
    [parsed]
  else
    []
  end
end
find_with_product_ref_uuid(uuid) click to toggle source

@param [String] uuid

@return [Xcodeproj::Project::Object::PBXNativeTarget]

# File lib/build_graph/native_target_finder.rb, line 214
def find_with_product_ref_uuid(uuid)
  all_targets.select {|target| target.product_reference.uuid == uuid}.first
end
join(direct_dependencies, linked_dependencies) click to toggle source

@param [Array<Xcodeproj::Project::Object::PBXAbstractTarget>] direct_dependencies @params [Array<Xcodeproj::Project::Object::PBXNativeTarget>] linked_dependencies

@return [Array<Xcodeproj::Project::Object::PBXAbstractTarget>]

# File lib/build_graph/native_target_finder.rb, line 204
def join(direct_dependencies, linked_dependencies)
  (direct_dependencies + linked_dependencies)
    .compact
    .uniq(&:equatable_identifier)
end
setup_product_name_to_target_mapping() click to toggle source
# File lib/build_graph/native_target_finder.rb, line 148
def setup_product_name_to_target_mapping
  @product_name_to_target = Hash.new

  @all_targets.each do |target|
    build_settings = target.find_build_configuration(build_configuration_name, raise_if_not_found: false)&.build_settings
    next unless build_settings

    full_settings = build_settings
    full_settings[XcodeArchiveCache::BuildSettings::TARGET_NAME_KEY] = target.name
    product_name = @interpolator.interpolate(build_settings[XcodeArchiveCache::BuildSettings::PRODUCT_NAME_KEY], full_settings)

    next if product_name == nil

    product_name_extension = ""
    case target.product_type
    when Xcodeproj::Constants::PRODUCT_TYPE_UTI[:framework]
      product_name_extension = ".framework"
    when Xcodeproj::Constants::PRODUCT_TYPE_UTI[:static_library]
      product_name_extension = ".a"
    end

    full_product_name = "#{product_name}#{product_name_extension}"
    @product_name_to_target[full_product_name] = target
  end
end
unnest(project) click to toggle source

@param [Xcodeproj::Project] project

@return [Array<Xcodeproj::Project>]

Project + subprojects at all levels of nesting
# File lib/build_graph/native_target_finder.rb, line 180
def unnest(project)
  nested_projects = project.files
                        .select {|file_ref| File.extname(file_ref.path) == ".xcodeproj" && File.exist?(file_ref.real_path)}
                        .map {|file_ref| Xcodeproj::Project.open(file_ref.real_path)}
  subnested_projects = nested_projects.map {|nested_project| unnest(nested_project)}.flatten
  [project] + nested_projects + subnested_projects
end