class PodPrebuild::XcodebuildCommand

Constants

DESTINATION_OF_SDK
PLATFORM_OF_SDK

Public Class Methods

new(options) click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_command.rb, line 5
def initialize(options)
  @options = options
  case options[:targets][0].platform.name
  when :ios
    @options[:device] = "iphoneos"
    @options[:simulator] = "iphonesimulator"
  when :tvos
    @options[:device] = "appletvos"
    @options[:simulator] = "appletvsimulator"
  when :watchos
    @options[:device] = "watchos"
    @options[:simulator] = "watchsimulator"
  end
  @build_args = make_up_build_args(options[:args] || {})
end
xcodebuild(options) click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_raw.rb, line 16
def self.xcodebuild(options)
  sdk = options[:sdk] || "iphonesimulator"
  targets = options[:targets] || [options[:target]]
  platform = PLATFORM_OF_SDK[sdk]

  cmd = ["xcodebuild"]
  cmd << "-project" << options[:sandbox].project_path.realdirpath.shellescape
  targets.each { |target| cmd << "-target" << target }
  cmd << "-configuration" << options[:configuration]
  cmd << "-sdk" << sdk
  if DESTINATION_OF_SDK.key?(sdk)
    cmd << "-destination" << DESTINATION_OF_SDK[sdk]
  else
    unless platform.nil?
      cmd << Fourflusher::SimControl.new.destination(:oldest, platform, options[:deployment_target])
    end
  end
  cmd += options[:args] if options[:args]
  cmd << "build"

  if options[:log_path].nil?
    cmd << "2>&1"
  else
    FileUtils.mkdir_p(File.dirname(options[:log_path]))
    cmd << "> #{options[:log_path].shellescape}"
  end
  cmd = cmd.join(" ")

  Pod::UI.puts_indented "$ #{cmd}" unless PodPrebuild.config.silent_build?

  log = `#{cmd}`
  return if $?.exitstatus.zero? # rubocop:disable Style/SpecialGlobalVars

  begin
    require "xcpretty" # TODO (thuyen): Revise this dependency
    # use xcpretty to print build log
    # 64 represent command invalid. http://www.manpagez.com/man/3/sysexits/
    printer = XCPretty::Printer.new({:formatter => XCPretty::Simple, :colorize => "auto"})
    log.each_line do |line|
      printer.pretty_print(line)
    end
  rescue
    Pod::UI.puts log.red
  ensure
    raise "Fail to build targets: #{targets}"
  end
end

Public Instance Methods

run() click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_command.rb, line 21
def run
  sdks.each { |sdk| build_for_sdk(sdk) }

  targets.each do |target|
    if PodPrebuild.config.xcframework?
      create_xcframework(target)
    elsif sdks.count > 1
      create_fat_framework(target)
    else
      collect_output(target, Dir[target_products_dir_of(target, sdks[0]) + "/*"])
    end
  end
end

Private Instance Methods

bcsymbolmap_paths_of(target, sdk) click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_command.rb, line 209
def bcsymbolmap_paths_of(target, sdk)
  Dir["#{target_products_dir_of(target, sdk)}/*.bcsymbolmap"]
end
bitcode_enabled?() click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_command.rb, line 237
def bitcode_enabled?
  @options[:bitcode_enabled]
end
build_dir() click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_command.rb, line 217
def build_dir
  @options[:build_dir]
end
build_for_sdk(sdk) click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_command.rb, line 77
def build_for_sdk(sdk)
  PodPrebuild::XcodebuildCommand.xcodebuild(
    sandbox: sandbox,
    scheme: scheme,
    targets: targets.map(&:label),
    configuration: configuration,
    sdk: sdk,
    deployment_target: targets.map { |t| t.platform.deployment_target }.max.to_s,
    log_path: log_path(sdk),
    args: sdk == simulator ? @build_args[:simulator] : @build_args[:device]
  )
end
build_types() click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_command.rb, line 50
def build_types
  @build_types ||= begin
    # TODO (thuyen): Add DSL options `build_for_types` to specify build types
    types = [:simulator]
    types << :device if device_build_enabled?
    types
  end
end
collect_output(target, paths) click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_command.rb, line 188
def collect_output(target, paths)
  FileUtils.mkdir_p(output_path(target))
  paths = [paths] unless paths.is_a?(Array)
  paths.each do |path|
    FileUtils.rm_rf(File.join(output_path(target), File.basename(path)))
    FileUtils.cp_r(path, output_path(target))
  end
end
configuration() click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_command.rb, line 233
def configuration
  @options[:configuration]
end
create_fat_binary(options) click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_command.rb, line 181
def create_fat_binary(options)
  cmd = ["lipo", " -create"]
  cmd << "-output" << options[:output]
  cmd << options[:simulator] << options[:device]
  `#{cmd.join(" ")}`
end
create_fat_framework(target) click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_command.rb, line 126
def create_fat_framework(target)
  # When merging contents of `simulator` & `device`, prefer contents of `device` over `simulator`
  # https://github.com/grab/cocoapods-binary-cache/issues/25
  collect_output(target, Dir[target_products_dir_of(target, device) + "/*"])

  merge_framework_binary(target)
  merge_framework_dsym(target)
  merge_swift_headers(target)
  merge_swift_modules(target)
end
create_xcframework(target) click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_command.rb, line 90
def create_xcframework(target)
  non_framework_paths = Dir[target_products_dir_of(target, preferred_sdk) + "/*"] \
    - [framework_path_of(target, preferred_sdk)] \
    - dsym_paths_of(target, preferred_sdk) \
    - bcsymbolmap_paths_of(target, preferred_sdk)
  collect_output(target, non_framework_paths)

  output = "#{output_path(target)}/#{target.product_module_name}.xcframework"
  FileUtils.rm_rf(output)

  cmd = ["xcodebuild", "-create-xcframework", "-allow-internal-distribution"]

  # for each sdk, the order of params must be -framework then -debug-symbols
  # to prevent duplicated file error when copying dSYMs
  sdks.each do |sdk|
    cmd << "-framework" << framework_path_of(target, sdk)

    unless disable_dsym?
      dsyms = dsym_paths_of(target, sdk)
      cmd += dsyms.map { |dsym| "-debug-symbols #{dsym}" }
    end

    if bitcode_enabled?
      bcsymbolmaps = bcsymbolmap_paths_of(target, sdk)
      cmd += bcsymbolmaps.map { |bcsymbolmap| "-debug-symbols #{bcsymbolmap}" }
    end
  end

  cmd << "-output" << output

  Pod::UI.puts "- Create xcframework: #{target}".magenta
  Pod::UI.puts_indented "$ #{cmd.join(' ')}" unless PodPrebuild.config.silent_build?

  `#{cmd.join(" ")}`
end
device() click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_command.rb, line 245
def device
  @options[:device] || "iphoneos"
end
device_build_enabled?() click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_command.rb, line 241
def device_build_enabled?
  @options[:device_build_enabled]
end
disable_dsym?() click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_command.rb, line 253
def disable_dsym?
  @options[:disable_dsym]
end
dsym_paths_of(target, sdk) click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_command.rb, line 205
def dsym_paths_of(target, sdk)
  Dir["#{target_products_dir_of(target, sdk)}/*.dSYM"]
end
framework_path_of(target, sdk) click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_command.rb, line 201
def framework_path_of(target, sdk)
  "#{target_products_dir_of(target, sdk)}/#{target.product_module_name}.framework"
end
log_path(sdk) click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_command.rb, line 257
def log_path(sdk)
  @options[:log_path].nil? ? nil : "#{@options[:log_path]}_#{sdk}"
end
make_up_build_args(args) click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_command.rb, line 59
def make_up_build_args(args)
  # Note: The build arguments explicitly passed from config_cocoapods_binary_cache
  # should be preceded by the default arguments so that they could take higher priority
  # when there are argument collisions in the xcodebuild command.
  # For ex. `xcodebuild AN_ARG=1 AN_ARG=2` should use `AN_ARG=2` instead.
  args_ = args.clone
  args_[:default] ||= []
  args_[:simulator] ||= []
  args_[:device] ||= []
  args_[:default].prepend("BITCODE_GENERATION_MODE=bitcode") if bitcode_enabled?
  args_[:default].prepend("DEBUG_INFORMATION_FORMAT=dwarf") if disable_dsym?
  args_[:simulator].prepend("ARCHS=x86_64", "ONLY_ACTIVE_ARCH=NO") if simulator == "iphonesimulator"
  args_[:simulator] += args_[:default]
  args_[:device].prepend("ONLY_ACTIVE_ARCH=NO")
  args_[:device] += args_[:default]
  args_
end
merge_contents(target, path_suffix, &merger) click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_command.rb, line 170
def merge_contents(target, path_suffix, &merger)
  simulator_, device_, output_ = [
    framework_path_of(target, simulator),
    framework_path_of(target, device),
    "#{output_path(target)}/#{target.product_module_name}.framework"
  ].map { |p| p + path_suffix }
  return unless File.exist?(simulator_) && File.exist?(device_)

  merger.call(simulator: simulator_, device: device_, output: output_)
end
merge_framework_binary(target) click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_command.rb, line 137
def merge_framework_binary(target)
  merge_contents(target, "/#{target.product_module_name}", &method(:create_fat_binary))
end
merge_framework_dsym(target) click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_command.rb, line 141
def merge_framework_dsym(target)
  merge_contents(
    target,
    ".dSYM/Contents/Resources/DWARF/#{target.product_module_name}",
    &method(:create_fat_binary)
  )
end
merge_swift_headers(target) click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_command.rb, line 149
    def merge_swift_headers(target)
      merge_contents(target, "/Headers/#{target.product_module_name}-Swift.h") do |options|
        merged_header = <<~HEREDOC
          #if TARGET_OS_SIMULATOR // merged by cocoapods-binary
          #{File.read(options[:simulator])}
          #else // merged by cocoapods-binary
          #{File.read(options[:device])}
          #endif // merged by cocoapods-binary
        HEREDOC
        File.write(options[:output], merged_header.strip)
      end
    end
merge_swift_modules(target) click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_command.rb, line 162
def merge_swift_modules(target)
  merge_contents(target, "/Modules/#{target.product_module_name}.swiftmodule") do |options|
    # Note: swiftmodules of `device` were copied beforehand,
    # here, we only need to copy swiftmodules of `simulator`
    FileUtils.cp_r(options[:simulator] + "/.", options[:output])
  end
end
output_path(target) click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_command.rb, line 221
def output_path(target)
  "#{@options[:output_path]}/#{target.label}"
end
preferred_sdk() click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_command.rb, line 46
def preferred_sdk
  @preferred_sdk ||= sdks.include?(device) ? device : sdks[0]
end
sandbox() click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_command.rb, line 213
def sandbox
  @options[:sandbox]
end
scheme() click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_command.rb, line 225
def scheme
  @options[:scheme]
end
sdks() click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_command.rb, line 37
def sdks
  @sdks ||= begin
    sdks_ = []
    sdks_ << simulator if build_types.include?(:simulator)
    sdks_ << device if build_types.include?(:device)
    sdks_
  end
end
simulator() click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_command.rb, line 249
def simulator
  @options[:simulator] || "iphonesimulator"
end
target_products_dir_of(target, sdk) click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_command.rb, line 197
def target_products_dir_of(target, sdk)
  "#{build_dir}/#{configuration}-#{sdk}/#{target.name}"
end
targets() click to toggle source
# File lib/cocoapods-binary-cache/pod-rome/xcodebuild_command.rb, line 229
def targets
  @options[:targets]
end