blob: 18b9ce961d1da1e380630b862f5375a0c54b3c44 [file] [log] [blame]
#!/usr/bin/env python
#
# Copyright 2018 - The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the', help="License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an', help="AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import, division, print_function
import argparse
import json
import logging
import multiprocessing
import os
import platform
import site
import subprocess
import sys
from distutils.spawn import find_executable
from Queue import Queue
from threading import Thread, currentThread
from server_config import ServerConfig
from time_formatter import TimeFormatter
AOSP_ROOT = os.path.abspath(
os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "..", ".."))
TOOLS = os.path.join(AOSP_ROOT, "tools")
PYTHON_EXE = sys.executable or "python"
def _reader(pipe, queue):
try:
with pipe:
for line in iter(pipe.readline, b""):
queue.put((pipe, line[:-1]))
finally:
queue.put(None)
def log_std_out(proc):
"""Logs the output of the given process."""
q = Queue()
Thread(target=_reader, args=[proc.stdout, q]).start()
Thread(target=_reader, args=[proc.stderr, q]).start()
for _ in range(2):
for _, line in iter(q.get, None):
logging.info(line)
def run(cmd, env, log_prefix, cwd=AOSP_ROOT):
currentThread().setName(log_prefix)
cmd_env = os.environ.copy()
cmd_env.update(env)
is_windows = (platform.system() == "Windows")
logging.info("=" * 140)
logging.info(json.dumps(cmd_env, sort_keys=True))
logging.info("%s $> %s", cwd, " ".join(cmd))
logging.info("=" * 140)
proc = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
shell=is_windows, # Make sure windows propagates ENV vars properly.
cwd=cwd,
env=cmd_env)
log_std_out(proc)
proc.wait()
if proc.returncode != 0:
raise Exception("Failed to run %s - %s" %
(" ".join(cmd), proc.returncode))
def install_deps():
# It is possible that the USER_SITE dir has never been created on freshly minted
# windows build bots. Since python's setuptools doesn't create it for us, we do it
# if needed.
if not os.path.exists(site.USER_SITE):
os.makedirs(site.USER_SITE)
run([PYTHON_EXE, "setup.py", "develop", "--user"
], {}, "dep", os.path.join(AOSP_ROOT, "external", "qemu", "android", "build", "python"))
def is_presubmit(build_id):
return build_id.startswith("P")
def config_logging():
ch = logging.StreamHandler()
ch.setFormatter(TimeFormatter("%(asctime)s %(threadName)s | %(message)s"))
logging.root = logging.getLogger('build')
logging.root.setLevel(logging.INFO)
logging.root.addHandler(ch)
currentThread().setName('inf')
def main(argv):
config_logging()
# We don't want to be too aggressive with concurrency.
test_cpu_count = int(multiprocessing.cpu_count() / 4)
# The build bots tend to be overloaded, so we want to restrict
# cpu usage to prevent strange timeout issues we have seen in the past.
# We can increment this once we are building on our own controlled macs
if platform.system() == 'Darwin':
test_cpu_count = 2
parser = argparse.ArgumentParser(
description="Configures the android emulator cmake project so it can be build"
)
parser.add_argument(
"--out_dir", type=str, required=True, help="The ouput directory")
parser.add_argument(
"--dist_dir", type=str, required=True, help="The destination directory")
parser.add_argument(
"--build-id",
type=str,
default=[],
required=True,
dest="build_id",
help="The emulator build number")
parser.add_argument(
"--test_jobs",
type=int,
default=test_cpu_count,
dest="test_jobs",
help="Specifies the number of tests to run simultaneously")
parser.add_argument(
"--target",
type=str,
default=platform.system(),
help="The build target, defaults to current os")
parser.add_argument(
"--qtwebengine",
action='store_true',
help="Build emulator with QtWebEngine libraries")
parser.add_argument(
"--gfxstream",
action='store_true',
help="Build gfxstream libraries")
parser.add_argument(
"--crosvm",
action='store_true',
help="Build crosvm")
args = parser.parse_args()
version = "{0[0]}.{0[1]}.{0[2]}".format(sys.version_info)
logging.info("Building with %s on %s - %s, Python: %s",
PYTHON_EXE,
platform.system(),
platform.uname(),
version)
target = platform.system().lower()
if args.target:
target = args.target.lower()
if not os.path.isabs(args.out_dir):
args.out_dir = os.path.join(AOSP_ROOT, args.out_dir)
# Make sure we have all the build dependencies
install_deps()
# This how we are going to launch the python build script
launcher = [PYTHON_EXE,
os.path.join(AOSP_ROOT, "external", "qemu", "android", "build", "python",
"cmake.py")
]
gfxstream_arg = "--gfxstream"
crosvm_arg = "--crosvm"
# Standard arguments for both debug & production.
if args.qtwebengine:
qtwebengine_arg = "--qtwebengine"
else:
qtwebengine_arg = "--noqtwebengine"
cmd = [
qtwebengine_arg, "--noshowprefixforinfo", "--out", args.out_dir,
"--sdk_build_number", args.build_id, "--target", target, "--dist",
args.dist_dir, "--test_jobs", str(args.test_jobs)
]
prod = ["--crash", "prod"]
debug = ["--config", "debug"]
if args.gfxstream:
cmd.append(gfxstream_arg)
if args.crosvm:
cmd.append(crosvm_arg)
# Kick of builds for 2 targets. (debug/release)
with ServerConfig(is_presubmit(args.build_id)) as cfg:
run(launcher + cmd + prod, cfg.get_env(), 'rel')
if not args.gfxstream and not args.crosvm:
run(launcher + cmd + debug, cfg.get_env(), 'dbg')
logging.info("Build completed!")
if __name__ == "__main__":
main(sys.argv)