From a3364e5051220b40f9c3be3bd02603669616b38f Mon Sep 17 00:00:00 2001 From: Ben Henning Date: Fri, 24 Jan 2025 11:54:33 -0800 Subject: [PATCH 1/2] Ensure mobile-install works. This updates documentation after local testing, and also replaces the install* run targets with universal APKs. Note that these APKs do NOT work with mobile-install, and they're going to be largely ignored for now (though they should work fine with adb install, just with worse performance than mobile-install). They may, instead, be used for emulator targets in the future. --- BUILD.bazel | 6 +- build_flavors.bzl | 8 +- oppia_android_application.bzl | 101 +++++++++++---------- wiki/Bazel-Setup-Instructions-for-Linux.md | 4 +- wiki/Bazel-Setup-Instructions-for-Mac.md | 4 +- 5 files changed, 65 insertions(+), 58 deletions(-) diff --git a/BUILD.bazel b/BUILD.bazel index 4c6d5a97c2a..0563e369bd5 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -131,9 +131,9 @@ package_group( ] ] -# Define all binary flavors that can be built. Note that these are AABs, not APKs, and can be -# be installed on a local device or emulator using a 'bazel run' command like so: -# bazel run //:install_oppia_dev +# Define all binary flavors that can be built. Note that these are AABs and thus cannot be installed +# directly. However, their binaries can be installed using mobile-install like so: +# bazel mobile-install //:oppia_dev_binary [ define_oppia_aab_binary_flavor(flavor = flavor) for flavor in AVAILABLE_FLAVORS diff --git a/build_flavors.bzl b/build_flavors.bzl index aa5493a391e..b0ed8e2169c 100644 --- a/build_flavors.bzl +++ b/build_flavors.bzl @@ -2,7 +2,7 @@ Macros & definitions corresponding to Oppia binary build flavors. """ -load("//:oppia_android_application.bzl", "declare_deployable_application", "oppia_android_application") +load("//:oppia_android_application.bzl", "generate_universal_apk", "oppia_android_application") load("//:version.bzl", "MAJOR_VERSION", "MINOR_VERSION", "OPPIA_ALPHA_KITKAT_VERSION_CODE", "OPPIA_ALPHA_VERSION_CODE", "OPPIA_BETA_VERSION_CODE", "OPPIA_DEV_KITKAT_VERSION_CODE", "OPPIA_DEV_VERSION_CODE", "OPPIA_GA_VERSION_CODE") # Defines the list of flavors available to build the Oppia app in. Note to developers: this list @@ -242,7 +242,7 @@ def define_oppia_aab_binary_flavor(flavor): This will define two targets: - //:oppia_ (the AAB) - - //:install_oppia_ (the installable binary target--see declare_deployable_application + - //:oppia__universal_apk (the installable binary target--see generate_universal_apk for details) Args: @@ -278,7 +278,7 @@ def define_oppia_aab_binary_flavor(flavor): shrink_resources = True if len(_FLAVOR_METADATA[flavor]["proguard_specs"]) != 0 else False, deps = _FLAVOR_METADATA[flavor]["deps"], ) - declare_deployable_application( - name = "install_oppia_%s" % flavor, + generate_universal_apk( + name = "oppia_%s_universal_apk" % flavor, aab_target = ":oppia_%s" % flavor, ) diff --git a/oppia_android_application.bzl b/oppia_android_application.bzl index d9951cefa18..4c3a0550bcc 100644 --- a/oppia_android_application.bzl +++ b/oppia_android_application.bzl @@ -158,61 +158,57 @@ def _package_metadata_into_deployable_aab_impl(ctx): runfiles = ctx.runfiles(files = [output_aab_file]), ) -def _generate_apks_and_install_impl(ctx): - input_file = ctx.attr.input_file.files.to_list()[0] +def _generate_universal_apk_impl(ctx): + input_aab_file = ctx.attr.input_aab_file.files.to_list()[0] + output_apk_file = ctx.outputs.output_apk_file debug_keystore_file = ctx.attr.debug_keystore.files.to_list()[0] apks_file = ctx.actions.declare_file("%s_processed.apks" % ctx.label.name) - deploy_shell = ctx.actions.declare_file("%s_run.sh" % ctx.label.name) - # Reference: https://developer.android.com/studio/command-line/bundletool#generate_apks. + # Reference: https://developer.android.com/tools/bundletool#generate_apks. # See also the Bazel BUILD file for the keystore for details on its password and alias. - generate_apks_arguments = [ + generate_universal_apk_arguments = [ "build-apks", - "--bundle=%s" % input_file.path, + "--bundle=%s" % input_aab_file.path, "--output=%s" % apks_file.path, "--ks=%s" % debug_keystore_file.path, "--ks-pass=pass:android", "--ks-key-alias=androiddebugkey", "--key-pass=pass:android", + "--mode=universal", ] + # bundletool only generats an APKs file, so the universal APK still needs to be extracted. + # Reference: https://docs.bazel.build/versions/master/skylark/lib/actions.html#run. ctx.actions.run( outputs = [apks_file], - inputs = ctx.files.input_file + ctx.files.debug_keystore, + inputs = ctx.files.input_aab_file + ctx.files.debug_keystore, tools = [ctx.executable._bundletool_tool], executable = ctx.executable._bundletool_tool.path, - arguments = generate_apks_arguments, - mnemonic = "BuildApksFromDeployAab", - progress_message = "Preparing AAB deploy to device", + arguments = generate_universal_apk_arguments, + mnemonic = "GenerateUniversalAPk", + progress_message = "Generating universal APK from AAB", ) - # References: https://github.com/bazelbuild/bazel/issues/7390, - # https://developer.android.com/studio/command-line/bundletool#deploy_with_bundletool, and - # https://docs.bazel.build/versions/main/skylark/rules.html#executable-rules-and-test-rules. - # Note that the bundletool can be executed directly since Bazel creates a wrapper script that - # utilizes its own internal Java toolchain. - ctx.actions.write( - output = deploy_shell, - content = """ - #!/bin/sh - {0} install-apks --apks={1} - echo The APK should now be installed - """.format(ctx.executable._bundletool_tool.short_path, apks_file.short_path), - is_executable = True, + command = """ + # Extract APK to working directory. + unzip -q "$(pwd)/{0}" universal.apk + mv universal.apk "$(pwd)/{1}" + """.format(apks_file.path, output_apk_file.path) + + # Reference: https://docs.bazel.build/versions/main/skylark/lib/actions.html#run_shell. + ctx.actions.run_shell( + outputs = [output_apk_file], + inputs = [apks_file], + tools = [], + command = command, + mnemonic = "ExtractUniversalAPk", + progress_message = "Extracting universal APK from .apks file", ) - # Reference for including necessary runfiles for Java: - # https://github.com/bazelbuild/bazel/issues/487#issuecomment-178119424. - runfiles = ctx.runfiles( - files = [ - ctx.executable._bundletool_tool, - apks_file, - ], - ).merge(ctx.attr._bundletool_tool.default_runfiles) return DefaultInfo( - executable = deploy_shell, - runfiles = runfiles, + files = depset([output_apk_file]), + runfiles = ctx.runfiles(files = [output_apk_file]), ) _convert_apk_to_module_aab = rule( @@ -303,10 +299,13 @@ _package_metadata_into_deployable_aab = rule( implementation = _package_metadata_into_deployable_aab_impl, ) -_generate_apks_and_install = rule( +_generate_universal_apk = rule( attrs = { - "input_file": attr.label( - allow_single_file = True, + "input_aab_file": attr.label( + allow_single_file = [".aab"], + mandatory = True, + ), + "output_apk_file": attr.output( mandatory = True, ), "debug_keystore": attr.label( @@ -319,14 +318,19 @@ _generate_apks_and_install = rule( default = "//third_party:android_bundletool_binary", ), }, - executable = True, - implementation = _generate_apks_and_install_impl, + implementation = _generate_universal_apk_impl, ) def oppia_android_application(name, config_file, proguard_generate_mapping, **kwargs): """ Creates an Android App Bundle (AAB) binary with the specified name and arguments. + This generates a mobile-installable target that ends in '_binary'. For example, if there's an + Oppia Android application defined with the name 'oppia_dev' then its APK binary can be + mobile-installed using: + + bazel mobile-install //:oppia_dev_binary + Args: name: str. The name of the Android App Bundle to build. This will corresponding to the name of the generated .aab file. @@ -390,30 +394,29 @@ def oppia_android_application(name, config_file, proguard_generate_mapping, **kw tags = ["manual"], ) -def declare_deployable_application(name, aab_target): +def generate_universal_apk(name, aab_target): """ - Creates a new target that can be run with 'bazel run' to install an AAB file. + Creates a new installable universal APK target for the provided AAB target. + + This new target is expected to be installable via 'bazel mobile-install' or directly with ADB. Example: - declare_deployable_application( - name = "install_oppia_prod", + generate_universal_apk( + name = "oppia_prod_universal_apk", aab_target = "//:oppia_prod", ) - $ bazel run //:install_oppia_prod - - This will build (if necessary) and install the correct APK derived from the Android app bundle - on the locally attached emulator or device. Note that this command does not support targeting a - specific device so it will not work if more than one device is available via 'adb devices'. + $ bazel mobile-install //:oppia_prod_universal_apk Args: name: str. The name of the runnable target to install an AAB file on a local device. aab_target: target. The target (declared via oppia_android_application) that should be made installable. """ - _generate_apks_and_install( + _generate_universal_apk( name = name, - input_file = aab_target, + input_aab_file = aab_target, + output_apk_file = "%s.apk" % name, debug_keystore = "@bazel_tools//tools/android:debug_keystore", tags = ["manual"], ) diff --git a/wiki/Bazel-Setup-Instructions-for-Linux.md b/wiki/Bazel-Setup-Instructions-for-Linux.md index 6410ca034b6..2a57b4914b4 100644 --- a/wiki/Bazel-Setup-Instructions-for-Linux.md +++ b/wiki/Bazel-Setup-Instructions-for-Linux.md @@ -42,5 +42,7 @@ INFO: Build completed successfully, ... Note also that the ``oppia_dev.aab`` under the ``bazel-bin`` directory of your local copy of Oppia Android should be a fully functioning development version of the app that can be installed using bundle-tool. However, it's recommended to deploy Oppia to an emulator or connected device using the following Bazel command: ```sh -bazel run //:install_oppia_dev +bazel mobile-install //:oppia_dev_binary ``` + +``mobile-install`` is much faster for local development (especially for the developer flavor of the app) because it does more sophisticated dex regeneration detection for faster incremental installs. See https://bazel.build/docs/mobile-install for details. diff --git a/wiki/Bazel-Setup-Instructions-for-Mac.md b/wiki/Bazel-Setup-Instructions-for-Mac.md index f7b3b2e7dfd..efc80825b9b 100644 --- a/wiki/Bazel-Setup-Instructions-for-Mac.md +++ b/wiki/Bazel-Setup-Instructions-for-Mac.md @@ -70,5 +70,7 @@ INFO: Build completed successfully, ... Note also that the ``oppia_dev.aab`` under the ``bazel-bin`` directory of your local copy of Oppia Android should be a fully functioning development version of the app that can be installed using bundle-tool. However, it's recommended to deploy Oppia to an emulator or connected device using the following Bazel command: ```sh -bazel run //:install_oppia_dev +bazel mobile-install //:oppia_dev_binary ``` + +``mobile-install`` is much faster for local development (especially for the developer flavor of the app) because it does more sophisticated dex regeneration detection for faster incremental installs. See https://bazel.build/docs/mobile-install for details. From 5d52e78b2b2196140120d8bd089f21035982b00d Mon Sep 17 00:00:00 2001 From: Ben Henning Date: Fri, 24 Jan 2025 11:59:35 -0800 Subject: [PATCH 2/2] Fix incorrect detail in doc string. --- oppia_android_application.bzl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/oppia_android_application.bzl b/oppia_android_application.bzl index 4c3a0550bcc..93f482fccbc 100644 --- a/oppia_android_application.bzl +++ b/oppia_android_application.bzl @@ -398,7 +398,8 @@ def generate_universal_apk(name, aab_target): """ Creates a new installable universal APK target for the provided AAB target. - This new target is expected to be installable via 'bazel mobile-install' or directly with ADB. + This new target is installable via 'adb install'. This should generally never be used for direct + development installs as it will be far less performant than 'bazel mobile-install'. Example: generate_universal_apk(