gecko/mobile/android/mach_commands.py
Nick Alexander 2bbfb700b2 Bug 1230848 - Remove Gradle configuration rooted in the object directory. r=gps
DONTBUILD NPOTB

The top source directory configuration requires
mobile/android/gradle/m2repo/**, so it stays.  There's no value
changing the location; it contains an Android-specific Gradle plugin.

We note the removal of |mach gradle-install| and point to the new
documentation.
2015-12-06 15:02:11 -08:00

269 lines
10 KiB
Python

# 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.util import (
FileAvoidWrite,
)
from mach.decorators import (
CommandArgument,
CommandProvider,
Command,
SubCommand,
)
SUCCESS = '''
You should be ready to build with Gradle and import into IntelliJ! Test with
./mach gradle build
and in IntelliJ select File > Import project... and choose
{topobjdir}/mobile/android/gradle
'''
# 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)
return self.run_process(['./gradlew'] + 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-install', category='devenv',
conditions=[REMOVED])
def gradle_install(self):
pass
class ArtifactSubCommand(SubCommand):
def __init__(self, *args, **kwargs):
SubCommand.__init__(self, *args, **kwargs)
def __call__(self, func):
after = SubCommand.__call__(self, func)
args = [
CommandArgument('--tree', metavar='TREE', type=str,
help='Firefox tree.'),
CommandArgument('--job', metavar='JOB', choices=['android-api-11', 'android-x86'],
help='Build job.'),
CommandArgument('--verbose', '-v', action='store_true',
help='Print verbose output.'),
]
for arg in args:
after = arg(after)
return after
@CommandProvider
class PackageFrontend(MachCommandBase):
"""Fetch and install binary artifacts from Mozilla automation."""
@Command('artifact', category='post-build',
description='Use pre-built artifacts to build Fennec.',
conditions=[
conditions.is_android, # mobile/android only for now.
conditions.is_hg, # mercurial only for now.
])
def artifact(self):
'''Download, cache, and install pre-built binary artifacts to build Fennec.
Invoke |mach artifact| before each |mach package| to freshen your installed
binary libraries. That is, package using
mach artifact install && mach package
to download, cache, and install binary artifacts from Mozilla automation,
replacing whatever may be in your object directory. Use |mach artifact last|
to see what binary artifacts were last used.
Never build libxul again!
'''
pass
def _set_log_level(self, verbose):
self.log_manager.terminal_handler.setLevel(logging.INFO if not verbose else logging.DEBUG)
def _make_artifacts(self, tree=None, job=None):
self._activate_virtualenv()
self.virtualenv_manager.install_pip_package('pylru==1.0.9')
self.virtualenv_manager.install_pip_package('taskcluster==0.0.16')
self.virtualenv_manager.install_pip_package('mozregression==1.0.2')
state_dir = self._mach_context.state_dir
cache_dir = os.path.join(state_dir, 'package-frontend')
import which
hg = which.which('hg')
# Absolutely must come after the virtualenv is populated!
from mozbuild.artifacts import Artifacts
artifacts = Artifacts(tree, job, log=self.log, cache_dir=cache_dir, hg=hg)
return artifacts
def _compute_defaults(self, tree=None, job=None):
# Firefox front-end developers mostly use fx-team. Post auto-land, make this central.
tree = tree or 'fx-team'
if job:
return (tree, job)
if self.substs['ANDROID_CPU_ARCH'] == 'x86':
return (tree, 'android-x86')
return (tree, 'android-api-11')
@ArtifactSubCommand('artifact', 'install',
'Install a good pre-built artifact.')
@CommandArgument('source', metavar='SRC', nargs='?', type=str,
help='Where to fetch and install artifacts from. Can be omitted, in '
'which case the current hg repository is inspected; an hg revision; '
'a remote URL; or a local file.',
default=None)
def artifact_install(self, source=None, tree=None, job=None, verbose=False):
self._set_log_level(verbose)
tree, job = self._compute_defaults(tree, job)
artifacts = self._make_artifacts(tree=tree, job=job)
return artifacts.install_from(source, self.distdir)
@ArtifactSubCommand('artifact', 'last',
'Print the last pre-built artifact installed.')
def artifact_print_last(self, tree=None, job=None, verbose=False):
self._set_log_level(verbose)
tree, job = self._compute_defaults(tree, job)
artifacts = self._make_artifacts(tree=tree, job=job)
artifacts.print_last()
return 0
@ArtifactSubCommand('artifact', 'print-cache',
'Print local artifact cache for debugging.')
def artifact_print_cache(self, tree=None, job=None, verbose=False):
self._set_log_level(verbose)
tree, job = self._compute_defaults(tree, job)
artifacts = self._make_artifacts(tree=tree, job=job)
artifacts.print_cache()
return 0
@ArtifactSubCommand('artifact', 'clear-cache',
'Delete local artifacts and reset local artifact cache.')
def artifact_clear_cache(self, tree=None, job=None, verbose=False):
self._set_log_level(verbose)
tree, job = self._compute_defaults(tree, job)
artifacts = self._make_artifacts(tree=tree, job=job)
artifacts.clear_cache()
return 0
@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=['2.3', '4.3', 'x86'],
help='Specify Android version to run in emulator. One of "2.3", "4.3", 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)
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 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