tenfourfox/testing/mozharness/scripts/android_emulator_build.py
Cameron Kaiser c9b2922b70 hello FPR
2017-04-19 00:56:45 -07:00

942 lines
37 KiB
Python
Executable File

#!/usr/bin/env python
# ***** BEGIN LICENSE BLOCK *****
# 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/.
# ***** END LICENSE BLOCK *****
import sys, os, re, multiprocessing
import subprocess, pprint, time, datetime, getpass, platform
from ftplib import FTP
# load modules from parent dir
sys.path.insert(1, os.path.dirname(sys.path[0]))
from mozharness.base.script import BaseScript
from mozharness.mozilla.purge import PurgeMixin
CONFIG_INI = {
"avd.ini.encoding": "ISO-8859-1",
"hw.dPad": "no",
"hw.lcd.density": "160",
"sdcard.size": "500M",
"hw.cpu.arch": "arm",
"hw.gpu.enabled": "yes",
"hw.device.hash": "-671137758",
"hw.camera.back": "none",
"disk.dataPartition.size": "600M",
"skin.path": "1024x816",
"skin.dynamic": "yes",
"hw.keyboard.lid": "yes",
"hw.keyboard": "yes",
"hw.ramSize": "1024",
"hw.device.manufacturer": "User",
"hw.sdCard": "yes",
"hw.mainKeys": "no",
"hw.accelerometer": "yes",
"skin.name": "1024x816",
"hw.trackBall": "no",
"hw.device.name": "mozilla-device",
"hw.battery": "yes",
"hw.sensors.proximity": "yes",
"hw.sensors.orientation": "yes",
"hw.audioInput": "yes",
"hw.gps": "yes",
"vm.heapSize": "64",
}
MOZILLA_DEVICE_DEFINITION = """
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<d:devices xmlns:d="http://schemas.android.com/sdk/devices/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<d:device>
<d:name>mozilla-device</d:name>
<d:manufacturer>User</d:manufacturer>
<d:meta/>
<d:hardware>
<d:screen>
<d:screen-size>large</d:screen-size>
<d:diagonal-length>7.00</d:diagonal-length>
<d:pixel-density>mdpi</d:pixel-density>
<d:screen-ratio>long</d:screen-ratio>
<d:dimensions>
<d:x-dimension>1024</d:x-dimension>
<d:y-dimension>816</d:y-dimension>
</d:dimensions>
<d:xdpi>187.05</d:xdpi>
<d:ydpi>187.05</d:ydpi>
<d:touch>
<d:multitouch>jazz-hands</d:multitouch>
<d:mechanism>finger</d:mechanism>
<d:screen-type>capacitive</d:screen-type>
</d:touch>
</d:screen>
<d:networking>
Bluetooth
Wifi
NFC</d:networking>
<d:sensors>
Accelerometer
Barometer
Compass
GPS
Gyroscope
LightSensor
ProximitySensor</d:sensors>
<d:mic>true</d:mic>
<d:camera>
<d:location>back</d:location>
<d:autofocus>true</d:autofocus>
<d:flash>true</d:flash>
</d:camera>
<d:keyboard>qwerty</d:keyboard>
<d:nav>nonav</d:nav>
<d:ram unit="GiB">1</d:ram>
<d:buttons>soft</d:buttons>
<d:internal-storage unit="GiB">
4</d:internal-storage>
<d:removable-storage unit="TiB"/>
<d:cpu>Generic CPU</d:cpu>
<d:gpu>Generic GPU</d:gpu>
<d:abi>
armeabi
armeabi-v7a
x86
mips</d:abi>
<d:dock/>
<d:power-type>battery</d:power-type>
</d:hardware>
<d:software>
<d:api-level>-</d:api-level>
<d:live-wallpaper-support>true</d:live-wallpaper-support>
<d:bluetooth-profiles/>
<d:gl-version>2.0</d:gl-version>
<d:gl-extensions/>
<d:status-bar>false</d:status-bar>
</d:software>
<d:state default="true" name="Portrait">
<d:description>The device in portrait orientation</d:description>
<d:screen-orientation>port</d:screen-orientation>
<d:keyboard-state>keyshidden</d:keyboard-state>
<d:nav-state>navhidden</d:nav-state>
</d:state>
<d:state name="Landscape">
<d:description>The device in landscape orientation</d:description>
<d:screen-orientation>land</d:screen-orientation>
<d:keyboard-state>keyshidden</d:keyboard-state>
<d:nav-state>navhidden</d:nav-state>
</d:state>
<d:state name="Portrait with keyboard">
<d:description>The device in portrait orientation with a keyboard open</d:description>
<d:screen-orientation>land</d:screen-orientation>
<d:keyboard-state>keysexposed</d:keyboard-state>
<d:nav-state>navhidden</d:nav-state>
</d:state>
<d:state name="Landscape with keyboard">
<d:description>The device in landscape orientation with a keyboard open</d:description>
<d:screen-orientation>land</d:screen-orientation>
<d:keyboard-state>keysexposed</d:keyboard-state>
<d:nav-state>navhidden</d:nav-state>
</d:state>
</d:device>
</d:devices>
"""
def sniff_host_arch():
host_arch = 'x86_64'
if platform.architecture()[0] == '32bit':
host_arch = 'x86'
return host_arch
class EmulatorBuild(BaseScript, PurgeMixin):
config_options = [
[["--host-arch"], {
"dest": "host_arch",
"help": "architecture of the host the emulator will run on (x86, x86_64; default autodetected)",
}],
[["--target-arch"], {
"dest": "target_arch",
"help": "architecture of the target the emulator will emulate (armv5te, armv7a, x86; default armv7a)",
}],
[["--android-version"], {
"dest": "android_version",
"help": "android version to build (eg. 2.3.7, 4.0, 4.3.1, gingerbread, ics, jb; default gingerbread)",
}],
[["--android-tag"], {
"dest": "android_tag",
"help": "android tag to check out (eg. android-2.3.7_r1; default inferred from --android-version)",
}],
[["--patch"], {
"dest": "patch",
"help": "'dir=url' comma-separated list of patches to apply to AOSP before building (eg. development=http://foo.com/bar.patch; default inferred)",
}],
[["--android-apilevel"], {
"dest": "android_apilevel",
"help": "android API-level to build AVD for (eg. 10, 14, 18; default inferred from --android-version)",
}],
[["--android-url"], {
"dest": "android_url",
"help": "where to fetch AOSP from, default https://android.googlesource.com/platform/manifest",
}],
[["--ndk-version"], {
"dest": "ndk_version",
"help": "version of the NDK to fetch, default r9",
}],
[["--install-android-dir"], {
"dest": "install_android_dir",
"help": "location bundled AVDs will be unpacked to, default /home/cltbld/.android"
}],
[["--avd-count"], {
"dest": "avd_count",
"help": "number of AVDs to build, default 4"
}],
[["--jdk"], {
"dest": "jdk",
"help": "which jdk to use (sun or openjdk; default sun)"
}]
]
def __init__(self, require_config_file=False):
BaseScript.__init__(self,
config_options=self.config_options,
all_actions=[
'clobber',
'apt-get-dependencies',
'download-aosp',
'download-kernel',
'download-ndk',
'download-test-binaries',
'checkout-orangutan',
'patch-aosp',
'build-aosp',
'build-kernel',
'build-orangutan-su',
'make-base-avd',
'customize-avd',
'clone-customized-avd',
'bundle-avds',
'bundle-emulators'
],
default_actions=[
'apt-get-dependencies',
'download-aosp',
'download-kernel',
'download-ndk',
'download-test-binaries',
'checkout-orangutan',
'patch-aosp',
'build-aosp',
'build-kernel',
'build-orangutan-su',
'make-base-avd',
'customize-avd',
'clone-customized-avd',
'bundle-avds',
'bundle-emulators'
],
require_config_file=require_config_file,
# Default configuration
config={
'host_arch': sniff_host_arch(),
'target_arch': 'armv7a',
'android_version': 'gingerbread',
'android_tag': 'inferred',
'patch': 'inferred',
'android_apilevel': 'inferred',
'work_dir': 'android_emulator_build',
'android_url': 'https://android.googlesource.com/platform/manifest',
'ndk_version': 'r9',
'install_android_dir': '/home/cltbld/.android',
'avd_count': '4',
'jdk': 'sun'
})
if platform.system() != "Linux":
self.fatal("this script only works on (ubuntu) linux")
if platform.dist() != ('Ubuntu', '12.04', 'precise'):
self.fatal("this script only works on ubuntu 12.04 precise")
if not (platform.machine() in ['i386', 'i486', 'i586', 'i686', 'x86_64']):
self.fatal("this script only works on x86 and x86_64")
self.tag = self.config['android_tag']
if self.tag == 'inferred':
self.tag = self.select_android_tag(self.config['android_version'])
self.patches = self.config['patch']
if self.patches == 'inferred':
self.patches = self.select_patches(self.tag)
else:
self.patches = [x.split('=') for x in self.patches.split(',')]
self.apilevel = self.config['android_apilevel']
if self.apilevel == 'inferred':
self.apilevel = self.android_apilevel(self.tag)
self.workdir = os.path.abspath(self.config['work_dir'])
self.bindir = os.path.join(self.workdir, "bin")
self.aospdir = os.path.join(self.workdir, "aosp")
self.goldfishdir = os.path.join(self.workdir, "goldfish")
self.ndkdir = os.path.join(self.workdir, "android-ndk-" + self.config['ndk_version'])
self.ncores = multiprocessing.cpu_count()
self.androiddir = os.path.join(self.workdir, ".android")
self.avddir = os.path.join(self.androiddir, "avd")
self.aosphostdir = os.path.join(self.aospdir, "out/host/linux-x86")
self.aosphostbindir = os.path.join(self.aosphostdir, "bin")
self.aospprodoutdir = os.path.join(self.aospdir, "out/target/product/generic")
self.emu = os.path.join(self.aosphostbindir, "emulator")
self.adb = os.path.join(self.aosphostbindir, "adb")
self.navds = int(self.config['avd_count'])
def apt_get_dependencies(self):
jdk = "oracle-java6-installer"
if self.config['jdk'] == "openjdk":
jdk = "openjdk-6-jdk"
self.apt_update()
self.apt_get(["python-software-properties"])
for repo in [ "ppa:webupd8team/java",
"deb http://archive.canonical.com/ precise partner",
"deb http://us.archive.ubuntu.com/ubuntu/ precise universe",
"deb http://us.archive.ubuntu.com/ubuntu/ precise-updates universe",
"deb http://us.archive.ubuntu.com/ubuntu/ precise-backports main restricted universe multiverse",
"deb http://security.ubuntu.com/ubuntu precise-security universe" ]:
self.apt_add_repo(repo)
self.apt_update()
self.apt_get(["python-software-properties"])
for pkgset in [[jdk],
["libglw1-mesa"],
["git", "gnupg", "flex", "bison", "gperf", "zip", "curl"],
["mingw32", "tofrodos"],
["build-essential", "gcc-4.4-multilib", "g++-4.4-multilib"],
["python-markdown", "libxml2-utils", "xsltproc"],
["x11proto-core-dev:i386", "libx11-dev:i386", "libreadline6-dev:i386",
"libgl1-mesa-glx:i386", "libgl1-mesa-dev:i386", "mesa-common-dev:i386",
"libxext-dev:i386"],
["libc6-dev:i386", "libncurses5-dev:i386", "zlib1g-dev:i386"],
["zlib1g-dev"],
["gcc", "g++"]]:
self.apt_get(pkgset)
def download_aosp(self):
self.download_file("http://commondatastorage.googleapis.com/git-repo-downloads/repo",
file_name="repo", parent_dir=self.bindir)
repo = os.path.join(self.bindir, "repo")
self.chmod(repo, 0755)
self.mkdir_p(self.aospdir)
self.run_command([repo, "init", "-u", self.config['android_url'],
"-b", self.tag],
cwd=self.aospdir,
halt_on_failure=True)
self.run_command([repo, "sync", "-j", str(self.ncores)],
cwd=self.aospdir,
halt_on_failure=True)
# Note: AOSP upstream tagged a version of the emulator with 2.3.x that will not
# actually boot gingerbread images. Quoting their explanation for posterity here, from
# http://source.android.com/source/known-issues.html:
#
# Symptom: The emulator built directly from the gingerbread branch doesn't start and
# stays stuck on a black screen.
#
# Cause: The gingerbread branch uses version R7 of the emulator, which doesn't have all
# the features necessary to run recent versions of gingerbread.
#
# Fix: Use version R12 of the emulator, and a newer kernel that matches those tools. No
# need to do a clean build.
#
if self.tag.startswith("android-2.3"):
self.info("updating QEMU sub-repository to R12")
self.info("to compensate for known 2.3.x-incompatible R7 in-tree")
self.run_command([repo, "forall", "platform/external/qemu",
"-c", "git", "checkout", "aosp/tools_r12"],
cwd=self.aospdir,
halt_on_failure=True)
# For 2.3.x you _probably_ want to use the symbolic tag 'gingerbread' because it is a
# post-release development branch on which they have backported the GL emulation that
# fennec relies on. In order to support that, we need to update qemu to R17.
if self.tag == 'gingerbread':
self.info("updating QEMU sub-repository to R17")
self.info("to acquire machinery needed for GL emulation backport")
self.run_command([repo, "forall", "platform/external/qemu",
"-c", "git", "checkout", "aosp/tools_r17"],
cwd=self.aospdir,
halt_on_failure=True)
def download_kernel(self):
self.mkdir_p(self.workdir)
self.run_command(["git", "clone",
"https://android.googlesource.com/kernel/goldfish.git"],
cwd=self.workdir,
halt_on_failure=True)
self.run_command(["git", "checkout",
"origin/android-goldfish-2.6.29"],
cwd=self.goldfishdir,
halt_on_failure=True)
def download_ndk(self):
ndk = "android-ndk-%s-linux-%s.tar.bz2" % (self.config['ndk_version'], self.config['host_arch'])
self.download_file("http://dl.google.com/android/ndk/" + ndk,
file_name=ndk, parent_dir=self.workdir)
self.run_command(["tar", "-xjf", ndk],
cwd=self.workdir, halt_on_failure=True)
def download_test_binaries(self):
lines = []
zipname = None
host = "ftp.mozilla.org"
path = None
if self.is_arm_target():
path = 'pub/mobile/nightly/latest-mozilla-central-android'
else:
path = 'pub/mobile/nightly/latest-mozilla-central-android-x86'
ftp = FTP(host)
ftp.login()
ftp.cwd(path)
ftp.retrlines('NLST', lambda x: lines.append(x.strip()))
for line in lines:
if line.endswith("tests.zip"):
zipname = line
break
if zipname == None:
self.fatal("unable to find *tests.zip at ftp://%s/%s" % (host,path))
url = "ftp://%s/%s/%s" % (host,path,zipname)
self.download_file(url, file_name=zipname, parent_dir=self.workdir)
self.run_command(["unzip", zipname,
"bin/sutAgentAndroid.apk",
"bin/Watcher.apk"],
cwd=self.workdir,
halt_on_failure=True)
def checkout_orangutan(self):
self.mkdir_p(self.workdir)
self.run_command(["git", "clone",
"https://github.com/wlach/orangutan.git"],
cwd=self.workdir,
halt_on_failure=True)
def patch_aosp(self):
if self.patches != None:
for patch in self.patches:
projectdir = patch[0]
url = patch[1]
patchdir = os.path.join(self.aospdir, projectdir)
self.info("downloading and applying AOSP patch %s to %s" % (url, patchdir))
self.download_file(url,
file_name='aosp.patch',
parent_dir=self.workdir)
self.run_command(['patch', '-p1',
'-i', os.path.join(self.workdir, 'aosp.patch')],
cwd=patchdir,
halt_on_failure=True)
def build_aosp(self):
arch = None
variant = None
abi = None
abi2 = ""
if self.is_arm_target():
arch = "arm"
if self.is_armv7_target():
variant = "armv7-a"
abi = "armeabi"
abi2 = "armeabi-v7a"
else:
variant = "armv5te"
abi = "armeabi"
else:
arch = "x86"
variant = "x86"
abi = "x86"
if abi2 != "":
abi2 = " TARGET_CPU_ABI2=" + abi2
env = { "BUILD_EMULATOR_OPENGL": "true",
"BUILD_EMULATOR_OPENGL_DRIVER": "true" }
self.run_command(["/bin/bash", "-c",
". build/envsetup.sh "
"&& lunch sdk-eng "
"&& make -j " + str(self.ncores) +
" sdk" +
" TARGET_ARCH=" + arch +
" TARGET_ARCH_VARIANT=" + variant +
" TARGET_CPU_ABI=" + abi +
abi2 +
" CC=gcc-4.4 CXX=g++-4.4" +
" && make out/host/linux-x86/bin/mksdcard"],
cwd=self.aospdir,
halt_on_failure=True,
partial_env=env)
def build_kernel(self):
env = {}
targ = 'goldfish_defconfig'
if self.is_arm_target():
env['ARCH']='arm'
env['PATH']=(self.ndk_bin_dir() +
':/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin')
env['CROSS_COMPILE']='arm-linux-androideabi-'
if self.is_armv7_target():
env['SUBARCH']='armv7'
targ='goldfish_armv7_defconfig'
else:
env['SUBARCH']='armv5'
else:
env['ARCH']='x86'
self.run_command(["make", targ], cwd=self.goldfishdir,
halt_on_failure=True, partial_env=env)
self.run_command(["make", "-j", str(self.ncores)], cwd=self.goldfishdir,
halt_on_failure=True, partial_env=env)
def build_orangutan_su(self):
self.run_command([self.ndk_bin("gcc"),
"--sysroot", self.ndk_sysroot(),
"-fPIC", "-mandroid", "-o", "su", "su.c"],
cwd=os.path.join(self.workdir, "orangutan"),
halt_on_failure=True)
def write_registry_file(self, avddir, avdname):
# Write the (mostly redundant) file that registers the AVD
self.info("writing %s.ini AVD-registry file pointing to %s/avd/%s.avd"
% (avdname, avddir, avdname))
reg = "avd.ini.encoding=ISO-8859-1\n"
reg += "target=android-%s\n" % self.apilevel
reg += "path=%s\n" % os.path.abspath(avddir)
reg += "path.rel=avd/%s.avd\n" % avdname
self.write_to_file(os.path.join(self.androiddir, "avd", avdname + ".ini"),
reg, create_parent_dir=True)
def make_one_avd(self, avdname):
named_avddir = os.path.join(self.avddir, avdname + ".avd")
self.write_registry_file(named_avddir, avdname)
self.mkdir_p(named_avddir)
# Write the foo.avd/config.ini file that defines the AVD
self.info("writing %s.avd/config.ini that defines the AVD" % avdname)
ini = CONFIG_INI.copy()
ini["image.sysdir.1"] = "platforms/android-" + self.apilevel + "/images/"
if self.is_arm_target():
ini["hw.cpu.arch"] = "arm"
if self.is_armv7_target():
ini["abi.type"] = "armeabi-v7a"
else:
ini["abi.type"] = "armeabi"
else:
ini["hw.cpu.arch"] = "x86"
ini["abi.type"] = "x86"
for i in ["bios.bin", "vgabios-cirrus.bin"]:
self.copyfile(os.path.join(self.aospdir, "prebuilts/qemu-kernel/x86/pc-bios", i),
os.path.join(named_avddir, i))
ini_file = open(os.path.join(named_avddir, "config.ini"), "w")
for (k,v) in ini.items():
ini_file.write("%s=%s\n" % (k,v))
ini_file.close()
# Copy the per-AVD filesystem images into place
self.info("copying userdata images from AOSP build to AVD")
for i in ["system.img", "ramdisk.img", "userdata.img", "userdata-qemu.img"]:
self.copyfile(os.path.join(self.aospprodoutdir, i),
os.path.join(named_avddir, i))
self.info("making 500M %s/sdcard.img" % named_avddir)
self.run_command([os.path.join(self.aosphostbindir, "mksdcard"),
"500M",
os.path.join(named_avddir, "sdcard.img")],
halt_on_failure=True)
# Copy the kernel into place
self.info("copying kernel from goldfish build dir into %s" % self.avddir)
kern = None
if self.is_arm_target():
kpath = "arch/arm/boot"
else:
kpath = "arch/x86/boot"
kpath = os.path.join(self.goldfishdir, kpath)
for i in ["zImage", "bzImage"]:
if os.path.exists(os.path.join(kpath,i)):
kern = os.path.join(kpath,i)
break
self.copyfile(os.path.join(self.goldfishdir, kern),
os.path.join(named_avddir, "kernel-qemu"))
def make_base_avd(self):
# Write the devices.xml file that defines 'mozilla-device'
self.info("writing devices.xml that contains mozilla-device")
self.write_to_file(os.path.join(self.androiddir, "devices.xml"),
MOZILLA_DEVICE_DEFINITION, create_parent_dir=True)
self.make_one_avd("test-1")
def emu_env(self):
partial_env = { "ANDROID_SDK_HOME": self.workdir,
"ANDROID_PRODUCT_OUT": self.aospprodoutdir }
return self.query_env(partial_env=partial_env)
def cpu_specific_args(self, avddir):
args = []
if self.is_armv7_target():
args = ["-qemu", "-cpu", "cortex-a8"]
else:
# point to avddir to pick up x86 BIOS
args = ["-qemu", "-L", avddir]
return args
def customize_avd(self):
self.info("starting emulator for customization run")
args = [self.emu, "-avd", "test-1", "-no-window", "-gpu", "off", "-partition-size", "1024"]
# unknown reason, on AWS the 64bit emulators don't seem to enjoy working;
# for now we punt to 32bit any time we might hit a 64bit one by accident.
if (os.path.exists(os.path.join(self.aosphostbindir, "emulator64-arm")) or
os.path.exists(os.path.join(self.aosphostbindir, "emulator64-x86"))):
args += ["-force-32bit"]
# CPU-specific flags fall through to qemu, so come last
args += self.cpu_specific_args(os.path.join(self.avddir, "test-1.avd"))
self.info("starting emulator: " + subprocess.list2cmdline(args))
self.info("in dir: %s" % self.aospdir)
self.info("in env: %s" % pprint.pformat(self.emu_env()))
p = subprocess.Popen(args,
cwd=self.aospdir,
env=self.emu_env())
self.info("waiting for device to start")
self.adb_e(["wait-for-device"])
# Wait until the package manager is online as well.
self.info("waiting for package manager to respond")
while True:
time.sleep(10)
f = self.get_output_from_command([self.adb, "-e", "shell",
"pm", "path", "android"],
halt_on_failure=False)
if f.startswith("package:"):
time.sleep(10)
break
self.info("modifying 'su' on emulator")
self.adb_e(["shell", "mount", "-o", "remount,rw", "/dev/block/mtdblock0", "/system"])
self.adb_e(["push", os.path.join(self.workdir, "orangutan/su"), "/system/xbin"])
self.adb_e(["shell", "chmod", "6755", "/system/xbin/su"])
self.adb_e(["shell", "mount", "-o", "remount,ro", "/dev/block/mtdblock0", "/system"])
self.info("installing packages into emulator")
self.adb_e(["install", os.path.join(self.bindir, "Watcher.apk")])
self.adb_e(["install", os.path.join(self.bindir, "sutAgentAndroid.apk")])
self.info("starting watcher and SUTagent")
self.adb_e(["shell", "am", "start", "-W", "com.mozilla.watcher/.WatcherMain"])
self.adb_e(["shell", "am", "start", "-W", "com.mozilla.SUTAgentAndroid/.SUTAgentAndroid"])
self.info("copying customized emulator system image from /tmp/")
sys_image = None
d = "/proc/%d/fd" % p.pid
for i in os.listdir(d):
path = os.readlink(os.path.join(d, i))
if re.match("/tmp/android-.*/emulator-.*", path):
if sys_image != None:
self.fatal("multiple plausible emulator system images, "
"kill emulators and try again")
sys_image = path
if sys_image == None:
self.fatal("unable to find running emulator's system image")
tmp = os.path.join(self.workdir, "system.img")
self.copyfile(sys_image, tmp)
self.info("terminating emulator")
p.terminate()
p.poll()
self.info("moving customized system.img into %s/test-1.avd" % self.avddir)
self.move(tmp, os.path.join(self.avddir, "test-1.avd", "system.img"))
def adb_e(self, commands):
self.run_command([self.adb, "-e"] + commands,
cwd=self.workdir,
halt_on_failure=True)
def clone_customized_avd(self):
if self.navds < 2:
self.info("less than 2 AVDs requested, not cloning")
return
srcdir = os.path.join(self.avddir, "test-1.avd")
for i in ["test-%d" % n for n in range(2,self.navds+1)]:
self.info("making basic AVD " + i)
self.make_one_avd(i)
self.info("overwriting %s.avd images with customized images from test-1.avd" % i)
dstdir = os.path.join(self.avddir, i + ".avd")
for f in ["system.img", "userdata.img", "userdata-qemu.img"]:
self.copyfile(os.path.join(srcdir, f),
os.path.join(dstdir, f))
def bundle_avds(self):
# clear out the cached / auto-generated .ini files in test-1.avd
for i in ["emulator-user.ini", "hardware-qemu.ini"]:
p = os.path.join(self.avddir, "test-1.avd", i)
if os.path.exists(p):
os.remove(p)
self.info("rewriting AVD registry files to point to install target dir %s" %
self.config['install_android_dir'])
for i in ["test-%d" % n for n in range(1,self.navds+1)]:
avddir = os.path.join(self.config['install_android_dir'], "avd", i + ".avd")
self.write_registry_file(avddir, i)
filename = ("AVDs-%s-%s-build-%s-%s.tar.gz"
% (self.config['target_arch'],
self.tag,
datetime.date.today().isoformat(),
getpass.getuser()))
self.run_command(["tar", "-czf", filename, "-C", self.androiddir,
"devices.xml", "avd"],
cwd=self.workdir,
halt_on_failure=True)
def bundle_emulators(self):
filename = ("emulators-%s-%s-build-%s-%s.tar.gz"
% (self.config['target_arch'],
self.tag,
datetime.date.today().isoformat(),
getpass.getuser()))
emuarch = "x86"
if self.is_arm_target():
emuarch = "arm"
self.run_command(["tar", "-czf", filename, "-C", self.aosphostdir,
"bin/emulator", "bin/emulator-" + emuarch, "bin/adb"],
cwd=self.workdir,
halt_on_failure=True)
def apt_update(self):
self.run_command(['sudo', 'apt-get', 'update'],
halt_on_failure=True)
def apt_add_repo(self, repo):
self.run_command(['sudo', 'apt-add-repository', '-y', repo],
halt_on_failure=True)
def apt_get(self, pkgs):
self.run_command(['sudo', 'apt-get', 'install', '-y'] + pkgs,
halt_on_failure=True)
def is_arm_target(self):
return self.config['target_arch'].startswith('arm')
def is_armv7_target(self):
return self.config['target_arch'].startswith('armv7')
def ndk_bin_dir(self):
if self.is_arm_target():
return os.path.join(self.ndkdir, "toolchains/arm-linux-androideabi-4.6/prebuilt/linux-%s/bin" % self.config['host_arch'])
else:
return os.path.join(self.ndkdir, "toolchains/x86-4.6/prebuilt/linux-%s/bin" % self.config['host_arch'])
def ndk_sysroot(self):
arch = "x86"
if self.is_arm_target():
arch = "arm"
apilevel = int(self.apilevel)
while apilevel > 0:
p = os.path.join(self.ndkdir, "platforms", "android-" + str(apilevel), "arch-" + arch)
if os.path.exists(p):
return p
apilevel = int(apilevel) - 1
self.fatal("no NDK sysroot for API level %s or less" % self.apilevel)
def ndk_cross_prefix(self):
if self.is_arm_target():
return "arm-linux-androideabi-"
else:
return "i686-linux-android-"
def ndk_bin(self, b):
return os.path.join(self.ndk_bin_dir(), self.ndk_cross_prefix() + b)
def android_apilevel(self, tag):
pairs = [("1.6", "4"),
("2.0", "5"),
("2.0.1", "6"),
("2.1", "7"),
("2.2", "8"),
("2.3", "9"),
("2.3.3", "10"),
("gingerbread", "10"),
("3.0", "11"),
("3.1", "12"),
("3.2", "13"),
("4.0", "14"),
("4.0.3", "15"),
("4.1", "16"),
("4.2", "17"),
("4.3", "18")]
for (vers, api) in pairs:
if tag == vers or tag.startswith("android-" + vers):
self.info("android tag '%s', vers %s, uses API level %s" % (tag, vers, api))
return api
self.fatal("android tag '%s' doesn't map to a known API level" % tag)
def select_android_tag(self, vers):
tags = [
"android-1.6_r1.1",
"android-1.6_r1.2",
"android-1.6_r1.3",
"android-1.6_r1.4",
"android-1.6_r1.5",
"android-2.0_r1",
"android-2.0.1_r1",
"android-2.1_r1",
"android-2.1_r2",
"android-2.1_r2.1p",
"android-2.1_r2.1s",
"android-2.1_r2.1p2",
"android-2.2_r1",
"android-2.2_r1.1",
"android-2.2_r1.2",
"android-2.2_r1.3",
"android-2.2.1_r1",
"android-2.2.1_r2",
"android-2.2.2_r1",
"android-2.2.3_r1",
"android-2.2.3_r2",
"android-2.3_r1",
"android-2.3.1_r1",
"android-2.3.2_r1",
"android-2.3.3_r1",
"android-2.3.3_r1.1",
"android-2.3.4_r0.9",
"android-2.3.4_r1",
"android-2.3.5_r1",
"android-2.3.6_r0.9",
"android-2.3.6_r1",
"android-2.3.7_r1",
"android-4.0.1_r1",
"android-4.0.1_r1.1",
"android-4.0.1_r1.2",
"android-4.0.2_r1",
"android-4.0.3_r1",
"android-4.0.3_r1.1",
"android-4.0.4_r1",
"android-4.0.4_r1.1",
"android-4.0.4_r1.2",
"android-4.0.4_r2",
"android-4.0.4_r2.1",
"android-4.1.1_r1",
"android-4.1.1_r1.1",
"android-4.1.1_r2",
"android-4.1.1_r3",
"android-4.1.1_r4",
"android-4.1.1_r5",
"android-4.1.1_r6",
"android-4.1.1_r6.1",
"android-4.1.2_r1",
"android-4.1.2_r2",
"android-4.1.2_r2.1",
"android-4.2_r1",
"android-4.2.1_r1",
"android-4.2.1_r1.1",
"android-4.2.1_r1.2",
"android-4.2.2_r1",
"android-4.2.2_r1.1",
"android-4.2.2_r1.1",
"android-4.3_r0.9",
"android-4.3_r0.9.1",
"android-4.3_r1",
"android-4.3_r1.1",
"android-4.3_r2",
"android-4.3_r2.1",
"android-4.3_r2.2",
"android-4.3_r2.3",
"android-4.3_r3",
"android-4.3.1_r1",
]
codenames = {
"jb": "4.3",
"jellybean": "4.3",
"ics": "4.0",
"gb": "2.3",
"gingerbread": "2.3",
"froyo": "2.2",
}
# NB: Gingerbread is special: we don't infer to the last release branch, we infer
# to the _development_ branch 'gingerbread' because it's where the GL emulator
# was backported to.
if vers == 'gingerbread':
self.info("selecting development-branch 'gingerbread' for version '2.3'")
return 'gingerbread'
if vers.startswith('2.3'):
self.info("android version 2.3.x specified, when dev branch 'gingerbread' is most likely")
self.info("required to get a working fennec build; pass --android-tag if you are certain")
self.info("you want a non-'gingerbread' 2.3.x version")
self.fatal("passed 2.3.x for --android-version, failing conservatively")
if vers in codenames:
self.info("using android version '%s' for requested codename '%s'" % (codenames[vers], vers))
vers = codenames[vers]
for tag in reversed(tags):
if tag.startswith(vers) or tag.startswith("android-" + vers):
self.info("selecting tag '%s' for version '%s'" % (tag, vers))
return tag
self.fatal("requested android version '%s' doesn't match any tag" % vers)
def select_patches(self, tag):
if tag == 'gingerbread':
# FIXME: perhaps put this patch someplace more stable than bugzilla?
return [('development',
'https://bug910092.bugzilla.mozilla.org/attachment.cgi?id=8361456'),
('external/sqlite',
'https://bug910092.bugzilla.mozilla.org/attachment.cgi?id=8364687')]
return None
def _post_fatal(self, message=None, exit_code=None):
for i in ['emulator', 'emulator-arm', 'emulator-x86',
'emulator64', 'emulator64-arm', 'emulator64-x86']:
self._kill_processes(i)
# main
if __name__ == '__main__':
myScript = EmulatorBuild()
myScript.run_and_exit()