summaryrefslogtreecommitdiffstats
path: root/mobile/android/mach_commands.py
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/mach_commands.py')
-rw-r--r--mobile/android/mach_commands.py216
1 files changed, 216 insertions, 0 deletions
diff --git a/mobile/android/mach_commands.py b/mobile/android/mach_commands.py
new file mode 100644
index 000000000..17628ad9f
--- /dev/null
+++ b/mobile/android/mach_commands.py
@@ -0,0 +1,216 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import argparse
+import logging
+import os
+
+import mozpack.path as mozpath
+
+from mozbuild.base import (
+ MachCommandBase,
+ MachCommandConditions as conditions,
+)
+
+from mozbuild.shellutil import (
+ split as shell_split,
+)
+
+from mach.decorators import (
+ CommandArgument,
+ CommandProvider,
+ Command,
+)
+
+
+# NOTE python/mach/mach/commands/commandinfo.py references this function
+# by name. If this function is renamed or removed, that file should
+# be updated accordingly as well.
+def REMOVED(cls):
+ """Command no longer exists! Use the Gradle configuration rooted in the top source directory instead.
+
+ See https://developer.mozilla.org/en-US/docs/Simple_Firefox_for_Android_build#Developing_Firefox_for_Android_in_Android_Studio_or_IDEA_IntelliJ.
+ """
+ return False
+
+
+@CommandProvider
+class MachCommands(MachCommandBase):
+ @Command('android', category='devenv',
+ description='Run the Android package manager tool.',
+ conditions=[conditions.is_android])
+ @CommandArgument('args', nargs=argparse.REMAINDER)
+ def android(self, args):
+ # Avoid logging the command
+ self.log_manager.terminal_handler.setLevel(logging.CRITICAL)
+
+ return self.run_process(
+ [os.path.join(self.substs['ANDROID_TOOLS'], 'android')] + args,
+ pass_thru=True, # Allow user to run gradle interactively.
+ ensure_exit_code=False, # Don't throw on non-zero exit code.
+ cwd=mozpath.join(self.topsrcdir))
+
+ @Command('gradle', category='devenv',
+ description='Run gradle.',
+ conditions=[conditions.is_android])
+ @CommandArgument('args', nargs=argparse.REMAINDER)
+ def gradle(self, args):
+ # Avoid logging the command
+ self.log_manager.terminal_handler.setLevel(logging.CRITICAL)
+
+
+ # In automation, JAVA_HOME is set via mozconfig, which needs
+ # to be specially handled in each mach command. This turns
+ # $JAVA_HOME/bin/java into $JAVA_HOME.
+ java_home = os.path.dirname(os.path.dirname(self.substs['JAVA']))
+
+ gradle_flags = shell_split(self.substs.get('GRADLE_FLAGS', ''))
+
+ # We force the Gradle JVM to run with the UTF-8 encoding, since we
+ # filter strings.xml, which is really UTF-8; the ellipsis character is
+ # replaced with ??? in some encodings (including ASCII). It's not yet
+ # possible to filter with encodings in Gradle
+ # (https://github.com/gradle/gradle/pull/520) and it's challenging to
+ # do our filtering with Gradle's Ant support. Moreover, all of the
+ # Android tools expect UTF-8: see
+ # http://tools.android.com/knownissues/encoding. See
+ # http://stackoverflow.com/a/21267635 for discussion of this approach.
+ return self.run_process([self.substs['GRADLE']] + gradle_flags + args,
+ append_env={
+ 'GRADLE_OPTS': '-Dfile.encoding=utf-8',
+ 'JAVA_HOME': java_home,
+ },
+ pass_thru=True, # Allow user to run gradle interactively.
+ ensure_exit_code=False, # Don't throw on non-zero exit code.
+ cwd=mozpath.join(self.topsrcdir))
+
+ @Command('gradle-install', category='devenv',
+ conditions=[REMOVED])
+ def gradle_install(self):
+ pass
+
+
+@CommandProvider
+class AndroidEmulatorCommands(MachCommandBase):
+ """
+ Run the Android emulator with one of the AVDs used in the Mozilla
+ automated test environment. If necessary, the AVD is fetched from
+ the tooltool server and installed.
+ """
+ @Command('android-emulator', category='devenv',
+ conditions=[],
+ description='Run the Android emulator with an AVD from test automation.')
+ @CommandArgument('--version', metavar='VERSION', choices=['4.3', '6.0', 'x86'],
+ help='Specify Android version to run in emulator. One of "4.3", "6.0", or "x86".',
+ default='4.3')
+ @CommandArgument('--wait', action='store_true',
+ help='Wait for emulator to be closed.')
+ @CommandArgument('--force-update', action='store_true',
+ help='Update AVD definition even when AVD is already installed.')
+ @CommandArgument('--verbose', action='store_true',
+ help='Log informative status messages.')
+ def emulator(self, version, wait=False, force_update=False, verbose=False):
+ from mozrunner.devices.android_device import AndroidEmulator
+
+ emulator = AndroidEmulator(version, verbose, substs=self.substs, device_serial='emulator-5554')
+ if emulator.is_running():
+ # It is possible to run multiple emulators simultaneously, but:
+ # - if more than one emulator is using the same avd, errors may
+ # occur due to locked resources;
+ # - additional parameters must be specified when running tests,
+ # to select a specific device.
+ # To avoid these complications, allow just one emulator at a time.
+ self.log(logging.ERROR, "emulator", {},
+ "An Android emulator is already running.\n"
+ "Close the existing emulator and re-run this command.")
+ return 1
+
+ if not emulator.is_available():
+ self.log(logging.WARN, "emulator", {},
+ "Emulator binary not found.\n"
+ "Install the Android SDK and make sure 'emulator' is in your PATH.")
+ return 2
+
+ if not emulator.check_avd(force_update):
+ self.log(logging.INFO, "emulator", {},
+ "Fetching and installing AVD. This may take a few minutes...")
+ emulator.update_avd(force_update)
+
+ self.log(logging.INFO, "emulator", {},
+ "Starting Android emulator running %s..." %
+ emulator.get_avd_description())
+ emulator.start()
+ if emulator.wait_for_start():
+ self.log(logging.INFO, "emulator", {},
+ "Android emulator is running.")
+ else:
+ # This is unusual but the emulator may still function.
+ self.log(logging.WARN, "emulator", {},
+ "Unable to verify that emulator is running.")
+
+ if conditions.is_android(self):
+ self.log(logging.INFO, "emulator", {},
+ "Use 'mach install' to install or update Firefox on your emulator.")
+ else:
+ self.log(logging.WARN, "emulator", {},
+ "No Firefox for Android build detected.\n"
+ "Switch to a Firefox for Android build context or use 'mach bootstrap'\n"
+ "to setup an Android build environment.")
+
+ if wait:
+ self.log(logging.INFO, "emulator", {},
+ "Waiting for Android emulator to close...")
+ rc = emulator.wait()
+ if rc is not None:
+ self.log(logging.INFO, "emulator", {},
+ "Android emulator completed with return code %d." % rc)
+ else:
+ self.log(logging.WARN, "emulator", {},
+ "Unable to retrieve Android emulator return code.")
+ return 0
+
+
+@CommandProvider
+class AutophoneCommands(MachCommandBase):
+ """
+ Run autophone, https://wiki.mozilla.org/Auto-tools/Projects/Autophone.
+
+ If necessary, autophone is cloned from github, installed, and configured.
+ """
+ @Command('autophone', category='devenv',
+ conditions=[],
+ description='Run autophone.')
+ @CommandArgument('--clean', action='store_true',
+ help='Delete an existing autophone installation.')
+ @CommandArgument('--verbose', action='store_true',
+ help='Log informative status messages.')
+ def autophone(self, clean=False, verbose=False):
+ import platform
+ from mozrunner.devices.autophone import AutophoneRunner
+
+ if platform.system() == "Windows":
+ # Autophone is normally run on Linux or OSX.
+ self.log(logging.ERROR, "autophone", {},
+ "This mach command is not supported on Windows!")
+ return -1
+
+ runner = AutophoneRunner(self, verbose)
+ runner.load_config()
+ if clean:
+ runner.reset_to_clean()
+ return 0
+ if not runner.setup_directory():
+ return 1
+ if not runner.install_requirements():
+ runner.save_config()
+ return 2
+ if not runner.configure():
+ runner.save_config()
+ return 3
+ runner.save_config()
+ runner.launch_autophone()
+ runner.command_prompts()
+ return 0