class Gym::Runner

Public Instance Methods

print_command(command, title) click to toggle source

@param [Array] An array containing all the parts of the command

run() click to toggle source

@return (String) The path to the resulting ipa

# File gym/lib/gym/runner.rb, line 16
def run
  unless Gym.config[:skip_build_archive]
    build_app
  end
  verify_archive unless Gym.config[:skip_archive]

  return nil if Gym.config[:skip_archive]

  FileUtils.mkdir_p(File.expand_path(Gym.config[:output_directory]))

  # Archive
  if Gym.building_for_ios?
    fix_generic_archive unless Gym.project.watchos? # See https://github.com/fastlane/fastlane/pull/4325
    return BuildCommandGenerator.archive_path if Gym.config[:skip_package_ipa]

    package_app
    compress_and_move_dsym

    unless Gym.export_destination_upload?
      path = move_ipa
      move_manifest
      move_app_thinning
      move_app_thinning_size_report
      move_apps_folder
      move_asset_packs
      move_appstore_info
    end
  elsif Gym.building_for_mac?
    path = File.expand_path(Gym.config[:output_directory])
    compress_and_move_dsym
    if Gym.project.mac_app? || Gym.building_mac_catalyst_for_mac?
      path = copy_mac_app
      return path if Gym.config[:skip_package_pkg]

      package_app
      unless Gym.export_destination_upload?
        path = move_pkg
        move_appstore_info
      end
      return path
    end
    copy_files_from_path(File.join(BuildCommandGenerator.archive_path, "Products/usr/local/bin/*")) if Gym.project.command_line_tool?
  end
  return path
end

Private Instance Methods

build_app() click to toggle source

Builds the app and prepares the archive

# File gym/lib/gym/runner.rb, line 107
def build_app
  command = BuildCommandGenerator.generate
  print_command(command, "Generated Build Command") if FastlaneCore::Globals.verbose?
  FastlaneCore::CommandExecutor.execute(command: command,
                                      print_all: true,
                                  print_command: !Gym.config[:silent],
                                          error: proc do |output|
                                            ErrorHandler.handle_build_error(output)
                                          end)

  unless Gym.config[:skip_archive]
    mark_archive_as_built_by_gym(BuildCommandGenerator.archive_path)
    UI.success("Successfully stored the archive. You can find it in the Xcode Organizer.") unless Gym.config[:archive_path].nil?
    UI.verbose("Stored the archive in: " + BuildCommandGenerator.archive_path)
  end

  post_build_app
end
compress_and_move_dsym() click to toggle source
# File gym/lib/gym/runner.rb, line 161
def compress_and_move_dsym
  return unless PackageCommandGenerator.dsym_path

  # Compress and move the dsym file
  containing_directory = File.expand_path("..", PackageCommandGenerator.dsym_path)
  bcsymbolmaps_directory = File.expand_path("../../BCSymbolMaps", PackageCommandGenerator.dsym_path)
  available_dsyms = Dir.glob("#{containing_directory}/*.dSYM")
  uuid_regex = /[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}/

  if Dir.exist?(bcsymbolmaps_directory)
    UI.message("Mapping dSYM(s) using generated BCSymbolMaps") unless Gym.config[:silent]
    available_dsyms.each do |dsym|
      dwarfdump_command = []
      dwarfdump_command << "dwarfdump"
      dwarfdump_command << "--uuid #{dsym.shellescape}"

      # Extract uuids
      dwarfdump_result = Helper.backticks(dwarfdump_command.join(" "), print: false)
      architecture_infos = dwarfdump_result.split("\n")
      architecture_infos.each do |info|
        info_array = info.split(" ", 4)
        uuid = info_array[1]
        dwarf_file_path = info_array[3]

        if uuid.nil? || !uuid.match(uuid_regex)
          next
        end

        # Find bcsymbolmap file to be used:
        #  - if a <uuid>.plist file exists, we will extract uuid of bcsymbolmap and use it
        #  - if a <uuid>.bcsymbolmap file exists, we will use it
        #  - otherwise let dsymutil figure it out
        symbol_map_path = nil
        split_dwarf_file_path = File.split(dwarf_file_path)
        dsym_plist_file_path = File.join(split_dwarf_file_path[0], "..", "#{uuid}.plist")
        if File.exist?(dsym_plist_file_path)
          dsym_plist = Plist.parse_xml(dsym_plist_file_path)
          original_uuid = dsym_plist['DBGOriginalUUID']
          possible_symbol_map_path = "#{bcsymbolmaps_directory}/#{original_uuid}.bcsymbolmap"
          if File.exist?(possible_symbol_map_path)
            symbol_map_path = possible_symbol_map_path.shellescape
          end
        end
        if symbol_map_path.nil?
          possible_symbol_map_path = File.join(bcsymbolmaps_directory, "#{uuid}.bcsymbolmap")
          if File.exist?(possible_symbol_map_path)
            symbol_map_path = possible_symbol_map_path.shellescape
          end
        end
        if symbol_map_path.nil?
          symbol_map_path = bcsymbolmaps_directory.shellescape
        end
        command = []
        command << "dsymutil"
        command << "--symbol-map #{symbol_map_path}"
        command << dsym.shellescape
        Helper.backticks(command.join(" "), print: !Gym.config[:silent])
      end
    end
  end

  UI.message("Compressing #{available_dsyms.count} dSYM(s)") unless Gym.config[:silent]

  output_path = File.expand_path(File.join(Gym.config[:output_directory], Gym.config[:output_name] + ".app.dSYM.zip"))
  command = "cd '#{containing_directory}' && zip -r '#{output_path}' *.dSYM"
  Helper.backticks(command, print: !Gym.config[:silent])
  puts("") # new line

  UI.success("Successfully exported and compressed dSYM file")
end
copy_files_from_path(path) click to toggle source

copys framework from temp folder:

# File gym/lib/gym/runner.rb, line 260
def copy_files_from_path(path)
  UI.success("Exporting Files:")
  Dir[path].each do |f|
    existing_file = File.join(File.expand_path(Gym.config[:output_directory]), File.basename(f))
    # If the target file already exists in output directory
    # we have to remove it first, otherwise cp_r fails even with remove_destination
    # e.g.: there are symlinks in the .framework
    if File.exist?(existing_file)
      UI.important("Removing #{File.basename(f)} from output directory") if FastlaneCore::Globals.verbose?
      FileUtils.rm_rf(existing_file)
    end
    FileUtils.cp_r(f, File.expand_path(Gym.config[:output_directory]), remove_destination: true)
    UI.message("\t ▸ #{File.basename(f)}")
  end
end
copy_mac_app() click to toggle source

Copies the .app from the archive into the output directory

# File gym/lib/gym/runner.rb, line 277
def copy_mac_app
  exe_name = Gym.project.build_settings(key: "EXECUTABLE_NAME")
  app_path = File.join(BuildCommandGenerator.archive_path, "Products/Applications/#{exe_name}.app")

  unless File.exist?(app_path)
    # Apparently the `EXECUTABLE_NAME` is not correct. This can happen when building a workspace which has a project
    # earlier in the build order that has a different `EXECUTABLE_NAME` than the app. Try to find the last `.app` as
    # a fallback for this situation.
    app_path = Dir[File.join(BuildCommandGenerator.archive_path, "Products", "Applications", "*.app")].last
  end

  UI.crash!("Couldn't find application in '#{BuildCommandGenerator.archive_path}'") unless File.exist?(app_path)

  joined_app_path = File.join(Gym.config[:output_directory], File.basename(app_path))
  FileUtils.rm_rf(joined_app_path)
  FileUtils.cp_r(app_path, File.expand_path(Gym.config[:output_directory]), remove_destination: true)

  UI.success("Successfully exported the .app file:")
  UI.message(joined_app_path)
  joined_app_path
end
find_archive_path() click to toggle source
# File gym/lib/gym/runner.rb, line 372
def find_archive_path
  Dir.glob(File.join(BuildCommandGenerator.build_path, "*.ipa")).last
end
fix_generic_archive() click to toggle source

@!group The individual steps

# File gym/lib/gym/runner.rb, line 96
def fix_generic_archive
  return unless FastlaneCore::Env.truthy?("GYM_USE_GENERIC_ARCHIVE_FIX")
  Gym::XcodebuildFixes.generic_archive_fix
end
mark_archive_as_built_by_gym(archive_path) click to toggle source
# File gym/lib/gym/runner.rb, line 101
def mark_archive_as_built_by_gym(archive_path)
  escaped_archive_path = archive_path.shellescape
  system("xattr -w info.fastlane.generated_by_gym 1 #{escaped_archive_path}")
end
move_app_thinning() click to toggle source

Move the app-thinning.plist file into the output directory

# File gym/lib/gym/runner.rb, line 312
def move_app_thinning
  if File.exist?(PackageCommandGenerator.app_thinning_path)
    FileUtils.mv(PackageCommandGenerator.app_thinning_path, File.expand_path(Gym.config[:output_directory]), force: true)
    app_thinning_path = File.join(File.expand_path(Gym.config[:output_directory]), File.basename(PackageCommandGenerator.app_thinning_path))

    UI.success("Successfully exported the app-thinning.plist file:")
    UI.message(app_thinning_path)
    app_thinning_path
  end
end
move_app_thinning_size_report() click to toggle source

Move the App Thinning Size Report.txt file into the output directory

# File gym/lib/gym/runner.rb, line 324
def move_app_thinning_size_report
  if File.exist?(PackageCommandGenerator.app_thinning_size_report_path)
    FileUtils.mv(PackageCommandGenerator.app_thinning_size_report_path, File.expand_path(Gym.config[:output_directory]), force: true)
    app_thinning_size_report_path = File.join(File.expand_path(Gym.config[:output_directory]), File.basename(PackageCommandGenerator.app_thinning_size_report_path))

    UI.success("Successfully exported the App Thinning Size Report.txt file:")
    UI.message(app_thinning_size_report_path)
    app_thinning_size_report_path
  end
end
move_apps_folder() click to toggle source

Move the Apps folder to the output directory

# File gym/lib/gym/runner.rb, line 336
def move_apps_folder
  if Dir.exist?(PackageCommandGenerator.apps_path)
    FileUtils.mv(PackageCommandGenerator.apps_path, File.expand_path(Gym.config[:output_directory]), force: true)
    apps_path = File.join(File.expand_path(Gym.config[:output_directory]), File.basename(PackageCommandGenerator.apps_path))

    UI.success("Successfully exported Apps folder:")
    UI.message(apps_path)
    apps_path
  end
end
move_appstore_info() click to toggle source

Move the AppStoreInfo.plist folder to the output directory

# File gym/lib/gym/runner.rb, line 361
def move_appstore_info
  if File.exist?(PackageCommandGenerator.appstore_info_path)
    FileUtils.mv(PackageCommandGenerator.appstore_info_path, File.expand_path(Gym.config[:output_directory]), force: true)
    appstore_info_path = File.join(File.expand_path(Gym.config[:output_directory]), File.basename(PackageCommandGenerator.appstore_info_path))

    UI.success("Successfully exported the AppStoreInfo.plist file:")
    UI.message(appstore_info_path)
    appstore_info_path
  end
end
move_asset_packs() click to toggle source

Move Asset Packs folder to the output directory @return (String) The path to the resulting Asset Packs (aka OnDemandResources) folder

# File gym/lib/gym/runner.rb, line 349
def move_asset_packs
  if Dir.exist?(PackageCommandGenerator.asset_packs_path)
    FileUtils.mv(PackageCommandGenerator.asset_packs_path, File.expand_path(Gym.config[:output_directory]), force: true)
    asset_packs_path = File.join(File.expand_path(Gym.config[:output_directory]), File.basename(PackageCommandGenerator.asset_packs_path))

    UI.success("Successfully exported Asset Pack folder:")
    UI.message(asset_packs_path)
    asset_packs_path
  end
end
move_ipa() click to toggle source

Moves over the binary and dsym file to the output directory @return (String) The path to the resulting ipa file

# File gym/lib/gym/runner.rb, line 234
def move_ipa
  FileUtils.mv(PackageCommandGenerator.ipa_path, File.expand_path(Gym.config[:output_directory]), force: true)
  ipa_path = File.expand_path(File.join(Gym.config[:output_directory], File.basename(PackageCommandGenerator.ipa_path)))

  UI.success("Successfully exported and signed the ipa file:")
  UI.message(ipa_path)
  ipa_path
end
move_manifest() click to toggle source

Move the manifest.plist if exists into the output directory

# File gym/lib/gym/runner.rb, line 300
def move_manifest
  if File.exist?(PackageCommandGenerator.manifest_path)
    FileUtils.mv(PackageCommandGenerator.manifest_path, File.expand_path(Gym.config[:output_directory]), force: true)
    manifest_path = File.join(File.expand_path(Gym.config[:output_directory]), File.basename(PackageCommandGenerator.manifest_path))

    UI.success("Successfully exported the manifest.plist file:")
    UI.message(manifest_path)
    manifest_path
  end
end
move_pkg() click to toggle source

Moves over the binary and dsym file to the output directory @return (String) The path to the resulting pkg file

# File gym/lib/gym/runner.rb, line 245
def move_pkg
  binary_path = File.expand_path(File.join(Gym.config[:output_directory], File.basename(PackageCommandGenerator.binary_path)))
  if File.exist?(binary_path)
    UI.important(" Removing #{File.basename(binary_path)}") if FastlaneCore::Globals.verbose?
    FileUtils.rm_rf(binary_path)
  end
  FileUtils.mv(PackageCommandGenerator.binary_path, File.expand_path(Gym.config[:output_directory]), force: true)

  UI.success("Successfully exported and signed the pkg file:")
  UI.message(binary_path)
  binary_path
end
package_app() click to toggle source
# File gym/lib/gym/runner.rb, line 149
def package_app
  command = PackageCommandGenerator.generate
  print_command(command, "Generated Package Command") if FastlaneCore::Globals.verbose?

  FastlaneCore::CommandExecutor.execute(command: command,
                                      print_all: false,
                                  print_command: !Gym.config[:silent],
                                          error: proc do |output|
                                            ErrorHandler.handle_package_error(output)
                                          end)
end
post_build_app() click to toggle source

Post-processing of build_app

# File gym/lib/gym/runner.rb, line 127
def post_build_app
  command = BuildCommandGenerator.post_build

  return if command.empty?

  print_command(command, "Generated Post-Build Command") if FastlaneCore::Globals.verbose?
  FastlaneCore::CommandExecutor.execute(command: command,
                                      print_all: true,
                                  print_command: !Gym.config[:silent],
                                          error: proc do |output|
                                            ErrorHandler.handle_build_error(output)
                                          end)
end
verify_archive() click to toggle source

Makes sure the archive is there and valid

# File gym/lib/gym/runner.rb, line 142
def verify_archive
  # from https://github.com/fastlane/fastlane/issues/3179
  if (Dir[BuildCommandGenerator.archive_path + "/*"]).count == 0
    ErrorHandler.handle_empty_archive
  end
end