class XcodeArchiveCache::Injection::Injector

Attributes

build_flags_changer[R]

@return [BuildFlagsChanger]

configuration_name[R]

@return [String]

dependency_remover[R]

@return [DependencyRemover]

framework_embedder[R]

@return [FrameworkEmbedder]

headers_mover[R]

@return [HeadersMover]

modulemap_fixer[R]

@return [ModulemapFixer]

pods_fixer[R]

@return [PodsScriptFixer]

storage[R]

@return [Storage]

Public Class Methods

new(configuration_name, storage) click to toggle source

@param [String] configuration_name @param [XcodeArchiveCache::Injection::Storage] storage

# File lib/injection/injector.rb, line 10
def initialize(configuration_name, storage)
  @configuration_name = configuration_name
  @storage = storage
  @headers_mover = HeadersMover.new(storage)
  @dependency_remover = DependencyRemover.new
  @build_flags_changer = BuildFlagsChanger.new
  @pods_fixer = PodsScriptFixer.new
  @modulemap_fixer = XcodeArchiveCache::Modulemap::HeaderPathFixer.new(storage)
  @framework_embedder = FrameworkEmbedder.new
end

Public Instance Methods

perform_internal_injection(graph) click to toggle source

@param [XcodeArchiveCache::BuildGraph::Graph] graph

# File lib/injection/injector.rb, line 23
def perform_internal_injection(graph)
  inject_unpacked_and_rebuilt(graph.nodes)
  add_header_paths(graph.nodes)
  save_graph_projects(graph)
end
perform_outgoing_injection(graph, target) click to toggle source

@param [XcodeArchiveCache::BuildGraph::Graph] graph @param [Xcodeproj::Project::Object::PBXNativeTarget] target

# File lib/injection/injector.rb, line 32
def perform_outgoing_injection(graph, target)
  root_node = graph.root_node
  if root_node.native_target.project == target.project && root_node.native_target.uuid == target.uuid
    return
  end

  no_rebuild_performed = root_node.state == :unpacked

  graph.nodes.each do |node|
    if no_rebuild_performed || node.state == :rebuilt_and_cached
      headers_mover.prepare_headers_for_injection(node)
      modulemap_fixer.fix_modulemap(node)
    end

    add_as_prebuilt_dependency(node, target)
    remove_native_target_from_project(node)
  end

  add_header_paths_to_target(target, storage.get_all_headers_storage_paths)

  # pretty dummy but should work in most cases;
  # here we assume that if graph has a pods target
  # then all graph nodes are built as pods and therefore
  # are covered by "Embed Pods Frameworks" script
  #
  if graph.node_by_name(get_pods_target_name(target))
    pods_fixer.fix_embed_frameworks_script(target, graph, storage.container_dir_path)
    pods_fixer.fix_copy_resources_script(target, graph, storage.container_dir_path)
  else
    framework_nodes = graph.nodes.select { |node| node.has_framework_product? }
    framework_file_paths = framework_nodes.map { |node| File.join(storage.get_storage_path(node), node.product_file_name) }
    framework_embedder.embed(framework_file_paths, target)
  end

  save_graph_projects(graph)
  target.project.save
end

Private Instance Methods

add_as_prebuilt_dependency(prebuilt_node, dependent_target) click to toggle source

@param [XcodeArchiveCache::BuildGraph::Node] prebuilt_node @param [Xcodeproj::Project::Object::PBXNativeTarget] dependent_target

# File lib/injection/injector.rb, line 171
def add_as_prebuilt_dependency(prebuilt_node, dependent_target)
  target_identifier = get_target_identifier(dependent_target)
  return if prebuilt_node.targets_injected_to.include?(target_identifier)

  debug("adding #{prebuilt_node.name} as prebuilt to #{dependent_target.display_name}")

  unless prebuilt_node.has_acceptable_product?
    raise XcodeArchiveCache::Informative, "#{prebuilt_node.name} has unsupported product type: #{prebuilt_node.native_target.product_type}"
  end

  if prebuilt_node.has_framework_product?
    add_as_prebuilt_framework(prebuilt_node, dependent_target)
  elsif prebuilt_node.has_static_library_product?
    add_as_prebuilt_static_lib(prebuilt_node, dependent_target)
  end

  prebuilt_node.targets_injected_to.push(target_identifier)

  debug("done with #{prebuilt_node.name} for #{dependent_target.display_name}")
end
add_as_prebuilt_framework(prebuilt_node, dependent_target) click to toggle source

@param [XcodeArchiveCache::BuildGraph::Node] prebuilt_node @param [Xcodeproj::Project::Object::PBXNativeTarget] dependent_target

# File lib/injection/injector.rb, line 195
def add_as_prebuilt_framework(prebuilt_node, dependent_target)
  build_configuration = dependent_target.find_build_configuration(configuration_name)

  artifact_location = storage.get_storage_path(prebuilt_node)
  build_flags_changer.replace_or_add_framework_search_path(build_configuration, prebuilt_node.native_target.name, artifact_location)
  build_flags_changer.add_framework_headers_iquote(build_configuration, artifact_location, prebuilt_node)

  if dependency_remover.is_linked(prebuilt_node, dependent_target)
    build_flags_changer.add_framework_linker_flag(build_configuration, prebuilt_node)
  end

  dependency_remover.remove_dependency(prebuilt_node, dependent_target)
end
add_as_prebuilt_static_lib(prebuilt_node, dependent_target) click to toggle source

@param [XcodeArchiveCache::BuildGraph::Node] prebuilt_node @param [Xcodeproj::Project::Object::PBXNativeTarget] dependent_target

# File lib/injection/injector.rb, line 212
def add_as_prebuilt_static_lib(prebuilt_node, dependent_target)
  build_configuration = dependent_target.find_build_configuration(configuration_name)

  injected_modulemap_file_path = storage.get_modulemap_path(prebuilt_node)
  if injected_modulemap_file_path
    modulemap_file_names = [prebuilt_node.resulting_modulemap_file_name]
    build_flags_changer.fix_module_map_path(build_configuration, modulemap_file_names, injected_modulemap_file_path)

    original_modulemap_path = prebuilt_node.original_modulemap_file_path
    add_header_paths_to_target(dependent_target, [File.dirname(original_modulemap_path)])
  end

  artifact_location = storage.get_storage_path(prebuilt_node)
  build_flags_changer.replace_or_add_library_search_path(build_configuration, prebuilt_node.native_target.name, artifact_location)
  build_flags_changer.add_swift_include_path(build_configuration, artifact_location)

  if dependency_remover.is_linked(prebuilt_node, dependent_target)
    if dependent_target.product_type == Xcodeproj::Constants::PRODUCT_TYPE_UTI[:static_library]
      build_flags_changer.add_static_lib_libtool_flag(build_configuration, prebuilt_node)
    else
      build_flags_changer.add_static_lib_linker_flag(build_configuration, prebuilt_node)
    end
  end

  dependency_remover.remove_dependency(prebuilt_node, dependent_target)
end
add_as_prebuilt_to_dependents(prebuilt_node) click to toggle source

@param [XcodeArchiveCache::BuildGraph::Node] prebuilt_node

# File lib/injection/injector.rb, line 149
def add_as_prebuilt_to_dependents(prebuilt_node)
  dependent_to_rebuild = prebuilt_node
                             .all_dependent_nodes
                             .select(&:waiting_for_rebuild)
  dependent_to_rebuild.each do |dependent_node|
    add_as_prebuilt_dependency(prebuilt_node, dependent_node.native_target)
  end

  remove_native_target_from_project(prebuilt_node)
end
add_header_paths(nodes) click to toggle source

@param [Array<XcodeArchiveCache::BuildGraph::Node>] nodes

# File lib/injection/injector.rb, line 122
def add_header_paths(nodes)
  header_storage_paths = storage.get_all_headers_storage_paths
  return if header_storage_paths.length == 0

  nodes
      .select(&:waiting_for_rebuild)
      .each { |node| add_header_paths_to_target(node.native_target, header_storage_paths) }
end
add_header_paths_to_target(target, paths) click to toggle source

@param [Xcodeproj::Project::Object::PBXNativeTarget] target @param [Array<String>] paths

# File lib/injection/injector.rb, line 134
def add_header_paths_to_target(target, paths)
  return if paths == nil

  debug("adding #{paths} to #{target.display_name}")

  build_configuration = target.find_build_configuration(configuration_name)
  paths.each do |path|
    build_flags_changer.add_headers_search_path(build_configuration, path)
    build_flags_changer.add_iquote_path(build_configuration, path)
    build_flags_changer.add_capital_i_path(build_configuration, path)
  end
end
get_pods_target_name(target) click to toggle source

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

# File lib/injection/injector.rb, line 241
def get_pods_target_name(target)
  "Pods-#{target.display_name}"
end
get_target_identifier(target) click to toggle source

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

@return [String]

# File lib/injection/injector.rb, line 261
def get_target_identifier(target)
  target.uuid + target.project.path.realpath.to_s
end
inject_unpacked_and_rebuilt(nodes) click to toggle source

@param [Array<XcodeArchiveCache::BuildGraph::Node>] nodes

# File lib/injection/injector.rb, line 106
def inject_unpacked_and_rebuilt(nodes)
  cached_nodes = nodes.select { |node| node.state == :unpacked }
  cached_nodes.each do |node|
    headers_mover.prepare_headers_for_injection(node)
    modulemap_fixer.fix_modulemap(node)
    add_as_prebuilt_to_dependents(node)
  end

  built_nodes = nodes.select { |node| node.state == :rebuilt_and_cached }
  built_nodes.each do |node|
    add_as_prebuilt_to_dependents(node)
  end
end
remove_native_target_from_project(node) click to toggle source

@param [XcodeArchiveCache::BuildGraph::Node] node

since 10.2 Xcode looks for implicit dependencies in -l and -framework linker flags, so we need to delete dependency target to make sure Xcode has no way to build it as implicit dependency

# File lib/injection/injector.rb, line 252
def remove_native_target_from_project(node)
  debug("deleting #{node.name} target")
  node.native_target.project.targets.delete(node.native_target)
end
save_graph_projects(graph) click to toggle source

@param [XcodeArchiveCache::BuildGraph::Graph] graph

# File lib/injection/injector.rb, line 162
def save_graph_projects(graph)
  projects = graph.nodes.map(&:native_target).map(&:project).uniq
  debug("updating #{projects.length} projects")
  projects.each { |project| project.save }
end