Exemple #1
0
def BuildAppWithShrinker(
    app, repo, shrinker, checkout_dir, out_dir, temp_dir, options,
    keepRuleSynthesisForRecompilation=False):
  print('[{}] Building {} with {}{}'.format(
      datetime.now().strftime("%H:%M:%S"),
      app.name,
      shrinker,
      ' for recompilation' if keepRuleSynthesisForRecompilation else ''))
  print('To compile locally: '
        'tools/run_on_as_app.py --shrinker {} --r8-compilation-steps {} '
        '--app {} {}'.format(
            shrinker,
            options.r8_compilation_steps,
            app.name,
            '--r8-compilation-steps-only'
              if options.r8_compilation_steps_only else ''))
  print('HINT: use --shrinker r8-nolib --no-build if you have a local R8.jar')
  # Add settings.gradle file if it is not present to prevent gradle from finding
  # the settings.gradle file in the r8 root when apps are placed under
  # $R8/build.
  as_utils.add_settings_gradle(checkout_dir, app.name)

  # Add 'r8.jar' to top-level build.gradle.
  as_utils.add_r8_dependency(checkout_dir, temp_dir, IsMinifiedR8(shrinker))

  archives_base_name = app.archives_base_name

  if not os.path.exists(out_dir):
    os.makedirs(out_dir)

  # Set -printconfiguration in Proguard rules.
  proguard_config_dest = os.path.abspath(
      os.path.join(out_dir, 'proguard-rules.pro'))
  as_utils.SetPrintConfigurationDirective(
      app, checkout_dir, proguard_config_dest)

  env_vars = {}
  env_vars['JAVA_HOME'] = jdk.GetJdk8Home()
  env_vars['ANDROID_HOME'] = utils.getAndroidHome()
  if not options.disable_assertions:
    env_vars['JAVA_OPTS'] = '-ea:com.android.tools.r8...'

  release_target = app.releaseTarget
  if not release_target:
    app_module = app.module.replace('/', ':')
    app_flavor = (app.flavor.capitalize() if app.flavor else '') + 'Release'
    release_target = app_module + ':' + 'assemble' + app_flavor

  # Build using gradle.
  args = [release_target,
          '-g=' + os.path.join(checkout_dir, GRADLE_USER_HOME),
          '-Pandroid.enableR8=' + str(IsR8(shrinker)).lower(),
          '-Pandroid.enableR8.fullMode=' + str(IsR8FullMode(shrinker)).lower()]
  if app.has_lint_task:
    args.extend(['-x', app_module + ':lintVital' + app_flavor])
  if options.bot:
    args.extend(['--console=plain', '--info'])

  # Warm up gradle if pre_runs > 0. For posterity we generate the same sequence
  # as the benchmarking at https://github.com/madsager/santa-tracker-android.
  for i in range(0, options.gradle_pre_runs):
    if i == 0:
      utils.RunGradlew(
          ["--stop"],
          env_vars=env_vars,
          quiet=options.quiet,
          logging=IsLoggingEnabledFor(app, options),
          use_daemon=options.use_daemon)
    utils.RunGradlew(
        args,
        env_vars=env_vars,
        quiet=options.quiet,
        clean=i > 0,
        use_daemon=options.use_daemon,
        logging=IsLoggingEnabledFor(app, options))

  if keepRuleSynthesisForRecompilation:
    args.append('-Dcom.android.tools.r8.keepRuleSynthesisForRecompilation=true')
  if options.gradle_flags:
    args.extend(options.gradle_flags.split(' '))

  args.append('--profile')

  stdout = utils.RunGradlew(
      args,
      env_vars=env_vars,
      quiet=options.quiet,
      use_daemon=options.use_daemon,
      logging=IsLoggingEnabledFor(app, options))

  apk_base_name = (archives_base_name
      + (('-' + app.flavor) if app.flavor else '') + '-release')
  signed_apk_name = (
      app.signed_apk_name
      if app.signed_apk_name
      else apk_base_name + '.apk')
  unsigned_apk_name = apk_base_name + '-unsigned.apk'

  build_dir = os.path.join(app.module, app.build_dir)
  build_output_apks = os.path.join(build_dir, 'outputs', 'apk')
  if app.flavor:
    build_output_apks = os.path.join(build_output_apks, app.flavor, 'release')
  else:
    build_output_apks = os.path.join(build_output_apks, 'release')

  signed_apk = os.path.join(build_output_apks, signed_apk_name)
  unsigned_apk = os.path.join(build_output_apks, unsigned_apk_name)

  assert os.path.isfile(signed_apk) or os.path.isfile(unsigned_apk), (
      "Expected a file to be present at {} or {}, found: {}\n"
      "Standard out from compilation: {}".format(
          signed_apk,
          unsigned_apk,
          ', '.join(
              as_utils.ListFiles(build_dir, lambda x : x.endswith('.apk'))),
          stdout))

  if options.sign_apks and not os.path.isfile(signed_apk):
    assert os.path.isfile(unsigned_apk)
    if options.sign_apks:
      apk_utils.sign_with_apksigner(
          unsigned_apk,
          signed_apk,
          options.keystore,
          options.keystore_password,
          quiet=options.quiet,
          logging=IsLoggingEnabledFor(app, options))

  if os.path.isfile(signed_apk):
    apk_dest = os.path.join(out_dir, signed_apk_name)
    as_utils.MoveFile(signed_apk, apk_dest, quiet=options.quiet)
  else:
    apk_dest = os.path.join(out_dir, unsigned_apk_name)
    as_utils.MoveFile(unsigned_apk, apk_dest, quiet=options.quiet)

  assert ('r8' not in shrinker
      or CheckIsBuiltWithExpectedR8(apk_dest, temp_dir, shrinker, options))

  profile_dest_dir = os.path.join(out_dir, 'profile')
  as_utils.MoveProfileReportTo(profile_dest_dir, stdout, quiet=options.quiet)
  # Ensure that the gradle daemon is stopped if we are running with it.
  if options.use_daemon:
    utils.RunGradlew(['--stop', '-g=' + os.path.join(checkout_dir, GRADLE_USER_HOME)])

  return (apk_dest, profile_dest_dir, proguard_config_dest)
Exemple #2
0
def main(argv):
  (options, args) = ParseOptions(argv)

  if options.bot:
    utils.DownloadFromGoogleCloudStorage(utils.OPENSOURCE_APPS_SHA_FILE)
    utils.DownloadFromGoogleCloudStorage(utils.ANDROID_SDK + '.tar.gz.sha1',
                                         bucket='r8-deps-internal',
                                         auth=True)
    if os.path.exists(WORKING_DIR):
      shutil.rmtree(WORKING_DIR)
    shutil.copytree(utils.OPENSOURCE_APPS_FOLDER, WORKING_DIR)
    os.environ[utils.ANDROID_HOME_ENVIROMENT_NAME] = os.path.join(
        utils.ANDROID_SDK)
    os.environ[utils.ANDROID_TOOLS_VERSION_ENVIRONMENT_NAME] = '28.0.3'
    # TODO(b/141081520): Set to True once fixed.
    options.no_logging = False
    # TODO(b/141081520): Remove logging filter once fixed.
    options.app_logging_filter = ['sqldelight']
    options.shrinker = ['r8', 'r8-full']
    print(options.shrinker)

  if options.golem:
    golem.link_third_party()
    if os.path.exists(WORKING_DIR):
      shutil.rmtree(WORKING_DIR)
    shutil.copytree(utils.OPENSOURCE_APPS_FOLDER, WORKING_DIR)
    os.environ[utils.ANDROID_HOME_ENVIROMENT_NAME] = os.path.join(
        utils.ANDROID_SDK)
    os.environ[utils.ANDROID_TOOLS_VERSION_ENVIRONMENT_NAME] = '28.0.3'
    options.disable_assertions = True
    options.ignore_versions = True
    options.no_build = True
    options.r8_compilation_steps = 1
    options.quiet = True
    options.gradle_pre_runs = 2
    options.use_daemon = True
    options.no_logging = True

  if not os.path.exists(WORKING_DIR):
    os.makedirs(WORKING_DIR)

  if options.download_only:
    clone_repositories(options.quiet)
    return

  with utils.TempDir() as temp_dir:
    if not (options.no_build or options.golem):
      gradle.RunGradle(['r8', '-Pno_internal'])
      build_r8lib = False
      for shrinker in options.shrinker:
        if IsMinifiedR8(shrinker):
          build_r8lib = True
      if build_r8lib:
        gradle.RunGradle(['r8lib', '-Pno_internal'])

    if options.hash:
      # Download r8-<hash>.jar from
      # https://storage.googleapis.com/r8-releases/raw/.
      target = 'r8-{}.jar'.format(options.hash)
      update_prebuilds_in_android.download_hash(
          temp_dir, 'com/android/tools/r8/' + options.hash, target)
      as_utils.MoveFile(
          os.path.join(temp_dir, target), os.path.join(temp_dir, 'r8lib.jar'),
          quiet=options.quiet)
    elif options.version:
      # Download r8-<version>.jar from
      # https://storage.googleapis.com/r8-releases/raw/.
      target = 'r8-{}.jar'.format(options.version)
      update_prebuilds_in_android.download_version(
          temp_dir, 'com/android/tools/r8/' + options.version, target)
      as_utils.MoveFile(
          os.path.join(temp_dir, target), os.path.join(temp_dir, 'r8lib.jar'),
          quiet=options.quiet)
    else:
      # Make a copy of r8.jar and r8lib.jar such that they stay the same for
      # the entire execution of this script.
      if 'r8-nolib' in options.shrinker or 'r8-nolib-full' in options.shrinker:
        assert os.path.isfile(utils.R8_JAR), 'Cannot build without r8.jar'
        shutil.copyfile(utils.R8_JAR, os.path.join(temp_dir, 'r8.jar'))
      if 'r8' in options.shrinker or 'r8-full' in options.shrinker:
        assert os.path.isfile(utils.R8LIB_JAR), 'Cannot build without r8lib.jar'
        shutil.copyfile(utils.R8LIB_JAR, os.path.join(temp_dir, 'r8lib.jar'))

    result_per_shrinker_per_app = []
    # If we are running on golem we kill all java processes after the run
    # to ensure no hanging gradle daemons.
    with EnsureNoGradleAlive(options.golem):
      for (app, repo) in options.apps:
        if app.skip:
          continue
        result_per_shrinker_per_app.append(
            (app, GetResultsForApp(app, repo, options, temp_dir)))
    return LogResultsForApps(result_per_shrinker_per_app, options)
Exemple #3
0
def BuildAppWithShrinker(app,
                         repo,
                         shrinker,
                         checkout_dir,
                         out_dir,
                         temp_dir,
                         options,
                         keepRuleSynthesisForRecompilation=False):
    print('Building {} with {}{}'.format(
        app.name, shrinker,
        ' for recompilation' if keepRuleSynthesisForRecompilation else ''))

    # Add settings.gradle file if it is not present to prevent gradle from finding
    # the settings.gradle file in the r8 root when apps are placed under
    # $R8/build.
    as_utils.add_settings_gradle(checkout_dir, app.name)

    # Add 'r8.jar' to top-level build.gradle.
    as_utils.add_r8_dependency(checkout_dir, temp_dir, IsMinifiedR8(shrinker))

    archives_base_name = app.archives_base_name

    if not os.path.exists(out_dir):
        os.makedirs(out_dir)

    # Set -printconfiguration in Proguard rules.
    proguard_config_dest = os.path.abspath(
        os.path.join(out_dir, 'proguard-rules.pro'))
    as_utils.SetPrintConfigurationDirective(app, checkout_dir,
                                            proguard_config_dest)

    env_vars = {}
    env_vars['ANDROID_HOME'] = utils.getAndroidHome()
    if not options.disable_assertions:
        env_vars['JAVA_OPTS'] = '-ea:com.android.tools.r8...'

    releaseTarget = app.releaseTarget
    if not releaseTarget:
        releaseTarget = app.module.replace('/', ':') + ':' + 'assemble' + (
            app.flavor.capitalize() if app.flavor else '') + 'Release'

    # Build using gradle.
    args = [
        releaseTarget, '-Pandroid.enableR8=' + str(IsR8(shrinker)).lower(),
        '-Pandroid.enableR8.fullMode=' + str(IsR8FullMode(shrinker)).lower()
    ]

    # Warm up gradle if pre_runs > 0. For posterity we generate the same sequence
    # as the benchmarking at https://github.com/madsager/santa-tracker-android.
    for i in range(0, options.gradle_pre_runs):
        if i == 0:
            utils.RunGradlew(["--stop"],
                             env_vars=env_vars,
                             quiet=options.quiet,
                             logging=IsLoggingEnabledFor(app, options),
                             use_daemon=options.use_daemon)
        utils.RunGradlew(args,
                         env_vars=env_vars,
                         quiet=options.quiet,
                         clean=i > 0,
                         use_daemon=options.use_daemon,
                         logging=IsLoggingEnabledFor(app, options))

    if keepRuleSynthesisForRecompilation:
        args.append(
            '-Dcom.android.tools.r8.keepRuleSynthesisForRecompilation=true')
    if options.gradle_flags:
        args.extend(options.gradle_flags.split(' '))

    args.append('--profile')

    stdout = utils.RunGradlew(args,
                              env_vars=env_vars,
                              quiet=options.quiet,
                              use_daemon=options.use_daemon,
                              logging=IsLoggingEnabledFor(app, options))

    apk_base_name = (archives_base_name +
                     (('-' + app.flavor) if app.flavor else '') + '-release')
    signed_apk_name = (app.signed_apk_name
                       if app.signed_apk_name else apk_base_name + '.apk')
    unsigned_apk_name = apk_base_name + '-unsigned.apk'

    build_dir = os.path.join(app.module, app.build_dir)
    build_output_apks = os.path.join(build_dir, 'outputs', 'apk')
    if app.flavor:
        build_output_apks = os.path.join(build_output_apks, app.flavor,
                                         'release')
    else:
        build_output_apks = os.path.join(build_output_apks, 'release')

    signed_apk = os.path.join(build_output_apks, signed_apk_name)
    unsigned_apk = os.path.join(build_output_apks, unsigned_apk_name)

    assert os.path.isfile(signed_apk) or os.path.isfile(unsigned_apk), (
        "Expected a file to be present at {} or {}, found: {}".format(
            signed_apk, unsigned_apk, ', '.join(
                as_utils.ListFiles(build_dir, lambda x: x.endswith('.apk')))))

    if options.sign_apks and not os.path.isfile(signed_apk):
        assert os.path.isfile(unsigned_apk)
        if options.sign_apks:
            apk_utils.sign_with_apksigner(unsigned_apk,
                                          signed_apk,
                                          options.keystore,
                                          options.keystore_password,
                                          quiet=options.quiet,
                                          logging=IsLoggingEnabledFor(
                                              app, options))

    if os.path.isfile(signed_apk):
        apk_dest = os.path.join(out_dir, signed_apk_name)
        as_utils.MoveFile(signed_apk, apk_dest, quiet=options.quiet)
    else:
        apk_dest = os.path.join(out_dir, unsigned_apk_name)
        as_utils.MoveFile(unsigned_apk, apk_dest, quiet=options.quiet)

    assert ('r8' not in shrinker or CheckIsBuiltWithExpectedR8(
        apk_dest, temp_dir, shrinker, options))

    profile_dest_dir = os.path.join(out_dir, 'profile')
    as_utils.MoveProfileReportTo(profile_dest_dir, stdout, quiet=options.quiet)

    return (apk_dest, profile_dest_dir, proguard_config_dest)
def main(argv):
    (options, args) = parse_options(argv)

    if options.bot:
        options.no_logging = True
        options.shrinker = ['r8', 'r8-full']
        print(options.shrinker)

    if options.golem:
        options.disable_assertions = True
        options.no_build = True
        options.r8_compilation_steps = 1
        options.quiet = True
        options.no_logging = True

    if options.generate_golem_config:
        print_golem_config(options)
        return 0

    with utils.TempDir() as temp_dir:
        if options.hash:
            # Download r8-<hash>.jar from
            # https://storage.googleapis.com/r8-releases/raw/.
            target = 'r8-{}.jar'.format(options.hash)
            update_prebuilds_in_android.download_hash(
                temp_dir, 'com/android/tools/r8/' + options.hash, target)
            as_utils.MoveFile(os.path.join(temp_dir, target),
                              os.path.join(temp_dir, 'r8lib.jar'),
                              quiet=options.quiet)
        elif version_is_built_jar(options.version):
            # Download r8-<version>.jar from
            # https://storage.googleapis.com/r8-releases/raw/.
            target = 'r8-{}.jar'.format(options.version)
            update_prebuilds_in_android.download_version(
                temp_dir, 'com/android/tools/r8/' + options.version, target)
            as_utils.MoveFile(os.path.join(temp_dir, target),
                              os.path.join(temp_dir, 'r8lib.jar'),
                              quiet=options.quiet)
        elif options.version == 'master':
            if not (options.no_build or options.golem):
                gradle.RunGradle(['r8', '-Pno_internal'])
                build_r8lib = False
                for shrinker in options.shrinker:
                    if is_minified_r8(shrinker):
                        build_r8lib = True
                if build_r8lib:
                    gradle.RunGradle(['r8lib', '-Pno_internal'])
            # Make a copy of r8.jar and r8lib.jar such that they stay the same for
            # the entire execution of this script.
            if 'r8-nolib' in options.shrinker or 'r8-nolib-full' in options.shrinker:
                assert os.path.isfile(
                    utils.R8_JAR), 'Cannot build without r8.jar'
                shutil.copyfile(utils.R8_JAR, os.path.join(temp_dir, 'r8.jar'))
            if 'r8' in options.shrinker or 'r8-full' in options.shrinker:
                assert os.path.isfile(
                    utils.R8LIB_JAR), 'Cannot build without r8lib.jar'
                shutil.copyfile(utils.R8LIB_JAR,
                                os.path.join(temp_dir, 'r8lib.jar'))

        result_per_shrinker_per_app = []
        for app in options.apps:
            if app.skip:
                continue
            result_per_shrinker_per_app.append(
                (app, get_results_for_app(app, options, temp_dir)))
        errors = log_results_for_apps(result_per_shrinker_per_app, options)
        if errors > 0:
            dest = 'gs://r8-test-results/r8-libs/' + str(int(time.time()))
            utils.upload_file_to_cloud_storage(
                os.path.join(temp_dir, 'r8lib.jar'), dest)
            print('R8lib saved to %s' % dest)
        return errors
Exemple #5
0
def main(argv):
    (options, args) = ParseOptions(argv)

    if options.golem:
        golem.link_third_party()
        if os.path.exists(WORKING_DIR):
            shutil.rmtree(WORKING_DIR)
        shutil.copytree(utils.OPENSOURCE_APPS_FOLDER, WORKING_DIR)
        os.environ[utils.ANDROID_HOME_ENVIROMENT_NAME] = os.path.join(
            utils.ANDROID_SDK)
        os.environ[utils.ANDROID_TOOLS_VERSION_ENVIRONMENT_NAME] = '28.0.3'
        options.disable_assertions = True
        options.ignore_versions = True
        options.no_build = True
        options.r8_compilation_steps = 1
        options.quiet = True
        options.gradle_pre_runs = 2
        options.use_daemon = True

    if not os.path.exists(WORKING_DIR):
        os.makedirs(WORKING_DIR)

    if options.download_only:
        clone_repositories(options.quiet)
        return

    with utils.TempDir() as temp_dir:
        if not (options.no_build or options.golem):
            gradle.RunGradle(['r8', 'r8lib'])

        if options.version:
            # Download r8-<version>.jar from
            # http://storage.googleapis.com/r8-releases/raw/.
            target = 'r8-{}.jar'.format(options.version)
            update_prebuilds_in_android.download_version(
                temp_dir, 'com/android/tools/r8/' + options.version, target)
            as_utils.MoveFile(os.path.join(temp_dir, target),
                              os.path.join(temp_dir, 'r8lib.jar'),
                              quiet=options.quiet)
        else:
            # Make a copy of r8.jar and r8lib.jar such that they stay the same for
            # the entire execution of this script.
            if 'r8-nolib' in options.shrinker or 'r8-nolib-full' in options.shrinker:
                assert os.path.isfile(
                    utils.R8_JAR), 'Cannot build without r8.jar'
                shutil.copyfile(utils.R8_JAR, os.path.join(temp_dir, 'r8.jar'))
            if 'r8' in options.shrinker or 'r8-full' in options.shrinker:
                assert os.path.isfile(
                    utils.R8LIB_JAR), 'Cannot build without r8lib.jar'
                shutil.copyfile(utils.R8LIB_JAR,
                                os.path.join(temp_dir, 'r8lib.jar'))

        result_per_shrinker_per_app = []

        for (app, repo) in options.apps:
            if app.skip:
                continue
            result_per_shrinker_per_app.append(
                (app, GetResultsForApp(app, repo, options, temp_dir)))

        LogResultsForApps(result_per_shrinker_per_app, options)
Exemple #6
0
def BuildAppWithShrinker(app,
                         config,
                         shrinker,
                         checkout_dir,
                         out_dir,
                         temp_dir,
                         options,
                         keepRuleSynthesisForRecompilation=False):
    print('Building {} with {}{}'.format(
        app, shrinker,
        ' for recompilation' if keepRuleSynthesisForRecompilation else ''))

    # Add/remove 'r8.jar' from top-level build.gradle.
    if options.disable_tot:
        as_utils.remove_r8_dependency(checkout_dir)
    else:
        as_utils.add_r8_dependency(checkout_dir, temp_dir,
                                   IsMinifiedR8(shrinker))

    app_module = config.get('app_module', 'app')
    archives_base_name = config.get('archives_base_name', app_module)
    flavor = config.get('flavor')

    if not os.path.exists(out_dir):
        os.makedirs(out_dir)

    # Set -printconfiguration in Proguard rules.
    proguard_config_dest = os.path.abspath(
        os.path.join(out_dir, 'proguard-rules.pro'))
    as_utils.SetPrintConfigurationDirective(app, config, checkout_dir,
                                            proguard_config_dest)

    env = {}
    env['ANDROID_HOME'] = utils.ANDROID_HOME
    env['JAVA_OPTS'] = '-ea:com.android.tools.r8...'

    releaseTarget = config.get('releaseTarget')
    if not releaseTarget:
        releaseTarget = app_module + ':' + 'assemble' + (
            flavor.capitalize() if flavor else '') + 'Release'

    # Value for property android.enableR8.
    enableR8 = 'r8' in shrinker
    # Value for property android.enableR8.fullMode.
    enableR8FullMode = shrinker == 'r8full' or shrinker == 'r8full-minified'
    # Build gradlew command line.
    cmd = [
        './gradlew', '--no-daemon', 'clean', releaseTarget, '--profile',
        '--stacktrace', '-Pandroid.enableR8=' + str(enableR8).lower(),
        '-Pandroid.enableR8.fullMode=' + str(enableR8FullMode).lower()
    ]
    if keepRuleSynthesisForRecompilation:
        cmd.append(
            '-Dcom.android.tools.r8.keepRuleSynthesisForRecompilation=true')
    if options.gradle_flags:
        cmd.extend(options.gradle_flags.split(' '))

    stdout = utils.RunCmd(cmd, env, quiet=options.quiet)

    apk_base_name = (archives_base_name + (('-' + flavor) if flavor else '') +
                     '-release')
    signed_apk_name = config.get('signed-apk-name', apk_base_name + '.apk')
    unsigned_apk_name = apk_base_name + '-unsigned.apk'

    build_dir = config.get('build_dir', 'build')
    build_output_apks = os.path.join(app_module, build_dir, 'outputs', 'apk')
    if flavor:
        build_output_apks = os.path.join(build_output_apks, flavor, 'release')
    else:
        build_output_apks = os.path.join(build_output_apks, 'release')

    signed_apk = os.path.join(build_output_apks, signed_apk_name)
    unsigned_apk = os.path.join(build_output_apks, unsigned_apk_name)

    if options.sign_apks and not os.path.isfile(signed_apk):
        assert os.path.isfile(unsigned_apk)
        if options.sign_apks:
            keystore = 'app.keystore'
            keystore_password = '******'
            apk_utils.sign_with_apksigner(utils.ANDROID_BUILD_TOOLS,
                                          unsigned_apk, signed_apk, keystore,
                                          keystore_password)

    if os.path.isfile(signed_apk):
        apk_dest = os.path.join(out_dir, signed_apk_name)
        as_utils.MoveFile(signed_apk, apk_dest, quiet=options.quiet)
    else:
        apk_dest = os.path.join(out_dir, unsigned_apk_name)
        as_utils.MoveFile(unsigned_apk, apk_dest, quiet=options.quiet)

    assert IsBuiltWithR8(apk_dest, temp_dir, options) == ('r8' in shrinker), (
        'Unexpected marker in generated APK for {}'.format(shrinker))

    profile_dest_dir = os.path.join(out_dir, 'profile')
    as_utils.MoveProfileReportTo(profile_dest_dir, stdout, quiet=options.quiet)

    return (apk_dest, profile_dest_dir, proguard_config_dest)