class PodBuilder::Command::Build

Public Class Methods

call() click to toggle source
# File lib/pod_builder/command/build.rb, line 6
def self.call 
  Configuration.check_inited
  PodBuilder::prepare_basepath

  argument_pods = ARGV.dup

  unless argument_pods.count > 0 
    return -1
  end

  raise "\n\nPlease rename your Xcode installation path removing spaces, current `#{`xcode-select -p`.strip()}`\n".red if `xcode-select -p`.strip().include?(" ")

  Podfile.sanity_check()
  check_not_building_subspecs(argument_pods)

  puts "Loading Podfile".yellow

  install_update_repo = OPTIONS.fetch(:update_repos, true)
  installer, analyzer = Analyze.installer_at(PodBuilder::basepath, install_update_repo)

  all_buildable_items = Analyze.podfile_items(installer, analyzer)
  prebuilt_items = all_buildable_items.select { |x| x.is_prebuilt }
  buildable_items = all_buildable_items - prebuilt_items

  build_all = argument_pods.first == "*"
  if build_all
    argument_pods = all_buildable_items.map(&:root_name).uniq
  else
    argument_pods = Podfile::resolve_pod_names(argument_pods, all_buildable_items)
    deps = all_buildable_items.select { |t| argument_pods.include?(t.root_name) }.map(&:dependency_names).flatten.map { |t| t.split("/").first }
    argument_pods += deps
    argument_pods.uniq!
  end

  available_argument_pods = argument_pods.select { |x| all_buildable_items.map(&:root_name).include?(x) }     
  (argument_pods - available_argument_pods).each { |x|
    puts "'#{x}' not found, skipping".magenta
  }
  argument_pods = available_argument_pods.uniq
  
  prebuilt_pods_to_install = prebuilt_items.select { |x| argument_pods.include?(x.root_name) }

  Podfile.restore_podfile_clean(all_buildable_items)

  restore_file_error = Podfile.restore_file_sanity_check
  
  check_pods_exists(argument_pods, all_buildable_items)

  pods_to_build = resolve_pods_to_build(argument_pods, buildable_items)
  buildable_items -= pods_to_build

  # We need to split pods to build in 3 groups
  # 1. pods to build in release
  # 2. pods to build in debug

  check_not_building_development_pods(pods_to_build)

  pods_to_build_debug = pods_to_build.select { |x| x.build_configuration == "debug" }
  pods_to_build_release = pods_to_build - pods_to_build_debug

  check_dependencies_build_configurations(all_buildable_items)

  podfiles_items = [pods_to_build_debug] + [pods_to_build_release]

  install_using_frameworks = Podfile::install_using_frameworks(analyzer)
  if Configuration.react_native_project
    if install_using_frameworks
      raise "\n\nOnly static library packaging currently supported for react native projects. Please remove 'use_frameworks!' in #{PodBuilder::basepath("Podfile")}".red 
    end  
    prepare_defines_modules_override(all_buildable_items)
  else
    unless install_using_frameworks
      raise "\n\nOnly framework packaging currently supported. Please add 'use_frameworks!' at root level (not nested in targets) in #{PodBuilder::basepath("Podfile")}".red
    end  
  end
  
  build_catalyst = should_build_catalyst(installer)

  install_result = InstallResult.new
  podfiles_items.reject { |x| x.empty? }.each do |podfile_items|
    build_configuration = podfile_items.map(&:build_configuration).uniq.first
    
    podfile_items = podfile_items.map { |t| t.recursive_dependencies(all_buildable_items) }.flatten.uniq
    podfile_content = Podfile.from_podfile_items(podfile_items, analyzer, build_configuration, install_using_frameworks, build_catalyst, Configuration.build_xcframeworks)
    
    install_result += Install.podfile(podfile_content, podfile_items, podfile_items.first.build_configuration)          
    
    FileUtils.rm_f(PodBuilder::basepath("Podfile.lock"))
  end

  install_result.write_prebuilt_info_files

  Clean::prebuilt_items(all_buildable_items)

  Licenses::write(install_result.licenses, all_buildable_items)

  Podspec::generate(all_buildable_items, analyzer, install_using_frameworks)

  builded_pods = podfiles_items.flatten
  
  builded_pods_and_deps = podfiles_items.flatten.map { |t| t.recursive_dependencies(all_buildable_items) }.flatten.uniq
  builded_pods_and_deps.select! { |x| !x.is_prebuilt }
  
  Podfile::write_restorable(builded_pods_and_deps + prebuilt_pods_to_install, all_buildable_items, analyzer)     
  if !OPTIONS.has_key?(:skip_prebuild_update)   
    Podfile::write_prebuilt(all_buildable_items, analyzer)
  end

  Podfile::install

  sanity_checks

  if (restore_file_error = restore_file_error) && Configuration.restore_enabled
    puts "\n\n⚠️ Podfile.restore was found invalid and was overwritten. Error:\n #{restore_file_error}".red
  end

  puts "\n\n🎉 done!\n".green
  return 0
end

Private Class Methods

check_dependencies_build_configurations(pods) click to toggle source
# File lib/pod_builder/command/build.rb, line 162
def self.check_dependencies_build_configurations(pods)
  pods.each do |pod|
    pod_dependency_names = pod.dependency_names.select { |x| !pod.has_common_spec(x) }

    remaining_pods = pods - [pod]
    pods_with_common_deps = remaining_pods.select { |x| x.dependency_names.any? { |y| pod_dependency_names.include?(y) && !x.has_common_spec(y) } }
    
    pods_with_unaligned_build_configuration = pods_with_common_deps.select { |x| x.build_configuration != pod.build_configuration }
    pods_with_unaligned_build_configuration.map!(&:name)

    raise "\n\nDependencies of `#{pod.name}` don't have the same build configuration (#{pod.build_configuration}) of `#{pods_with_unaligned_build_configuration.join(",")}`'s dependencies".red if pods_with_unaligned_build_configuration.count > 0
  end
end
check_not_building_development_pods(pods) click to toggle source
# File lib/pod_builder/command/build.rb, line 176
def self.check_not_building_development_pods(pods)
  if (development_pods = pods.select { |x| x.is_development_pod }) && development_pods.count > 0 && (OPTIONS[:allow_warnings].nil?  && Configuration.allow_building_development_pods == false && Configuration.react_native_project == false)
    pod_names = development_pods.map(&:name).join(", ")
    raise "\n\nThe following pods are in development mode: `#{pod_names}`, won't proceed building.\n\nYou can ignore this error by passing the `--allow-warnings` flag to the build command\n".red
  end
end
check_not_building_subspecs(pods_to_build) click to toggle source
# File lib/pod_builder/command/build.rb, line 145
def self.check_not_building_subspecs(pods_to_build)
  pods_to_build.each do |pod_to_build|
    if pod_to_build.include?("/")
      raise "\n\nCan't build subspec #{pod_to_build} refer to podspec name.\n\nUse `pod_builder build #{pods_to_build.map { |x| x.split("/").first }.uniq.join(" ")}` instead\n\n".red
    end
  end
end
check_pods_exists(pods, buildable_items) click to toggle source
# File lib/pod_builder/command/build.rb, line 153
def self.check_pods_exists(pods, buildable_items)
  raise "\n\nEmpty Podfile?".red if buildable_items.nil?

  buildable_items = buildable_items.map(&:root_name)
  pods.each do |pod|
    raise "\n\nPod `#{pod}` wasn't found in Podfile.\n\nFound:\n#{buildable_items.join("\n")}\n\n".red if !buildable_items.include?(pod)
  end
end
other_subspecs(pods_to_build, buildable_items) click to toggle source
# File lib/pod_builder/command/build.rb, line 183
def self.other_subspecs(pods_to_build, buildable_items)
  buildable_subspecs = buildable_items.select { |x| x.is_subspec }
  pods_to_build_subspecs = pods_to_build.select { |x| x.is_subspec }.map(&:root_name)

  buildable_subspecs.select! { |x| pods_to_build_subspecs.include?(x.root_name) }

  return buildable_subspecs - pods_to_build
end
prepare_defines_modules_override(all_buildable_items) click to toggle source
# File lib/pod_builder/command/build.rb, line 137
def self.prepare_defines_modules_override(all_buildable_items)
  all_buildable_items.each do |item|
    unless item.defines_module.nil?
      Pod::PodTarget.modules_override[item.root_name] = item.defines_module
    end
  end
end
resolve_pods_to_build(argument_pods, buildable_items) click to toggle source
# File lib/pod_builder/command/build.rb, line 208
def self.resolve_pods_to_build(argument_pods, buildable_items)
  pods_to_build = []
  
  pods_to_build = buildable_items.select { |x| argument_pods.include?(x.root_name) }
  pods_to_build += other_subspecs(pods_to_build, buildable_items)

  if OPTIONS[:resolve_parent_dependencies]
    dependencies = []
    buildable_items.each do |pod|
      if !(pod.dependencies(buildable_items) & pods_to_build).empty?
        dependencies.push(pod)
      end
    end
    pods_to_build += dependencies
  end

  return pods_to_build.uniq
end
sanity_checks() click to toggle source
# File lib/pod_builder/command/build.rb, line 192
def self.sanity_checks
  lines = File.read(PodBuilder::project_path("Podfile")).split("\n")
  stripped_lines = lines.map { |x| Podfile.strip_line(x) }.select { |x| !x.start_with?("#")}

  expected_stripped = Podfile::PRE_INSTALL_ACTIONS.map { |x| Podfile.strip_line(x) }

  if !expected_stripped.all? { |x| stripped_lines.include?(x) }
    warn_message = "PodBuilder's pre install actions missing from application Podfile!\n"
    if OPTIONS[:allow_warnings]
      puts "\n\n#{warn_message}".yellow
    else
      raise "\n\n#{warn_message}".red
    end
  end
end
should_build_catalyst(installer) click to toggle source
# File lib/pod_builder/command/build.rb, line 128
def self.should_build_catalyst(installer)
  build_settings = installer.analysis_result.targets.map { |t| t.user_project.root_object.targets.map { |u| u.build_configuration_list.build_configurations.map { |v| v.build_settings } } }.flatten
  build_catalyst = build_settings.detect { |t| t["SUPPORTS_MACCATALYST"] == "YES" } != nil 
  
  puts "\nTo support Catalyst you should enable 'build_xcframeworks' in PodBuilder.json\n".red if build_catalyst && !Configuration.build_xcframeworks

  return build_catalyst
end