Files
tubestation/servo/python/servo/build_commands.py
Lars Bergstrom fac39e45d9 servo: Merge #8366 - Add CCACHE infra and turn it on in travis (from larsbergstrom:ccache); r=frewsxcv
r? @Manishearth

This lets devs configure their use of CCACHE with their .servobuild file, as usual. For build environments, they can either have a .servobuild file or set the CCACHE env var to point at the ccache binary to use.

It also adds support for ccache to our travis builds. Buildbot will come in a separate commit to the saltfs repo.

It is expected that the various cargo makefiles will look at this variable and do the "right thing" to tell their native build to instead use ccache. e.g., https://github.com/servo/mozjs/pull/62

Source-Repo: https://github.com/servo/servo
Source-Revision: d16ba51b4722a84f69976ca8679af672495248c8
2015-11-07 06:31:54 +05:00

365 lines
12 KiB
Python

# Copyright 2013 The Servo Project Developers. See the COPYRIGHT
# file at the top-level directory of this distribution.
#
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
# option. This file may not be copied, modified, or distributed
# except according to those terms.
from __future__ import print_function, unicode_literals
import os
import os.path as path
import subprocess
import sys
from time import time
from mach.decorators import (
CommandArgument,
CommandProvider,
Command,
)
from servo.command_base import CommandBase, cd
def is_headless_build():
return int(os.getenv('SERVO_HEADLESS', 0)) == 1
def notify_linux(title, text):
try:
import dbus
bus = dbus.SessionBus()
notify_obj = bus.get_object("org.freedesktop.Notifications", "/org/freedesktop/Notifications")
method = notify_obj.get_dbus_method("Notify", "org.freedesktop.Notifications")
method(title, 0, "", text, "", [], {"transient": True}, -1)
except:
raise Exception("Optional Python module 'dbus' is not installed.")
def notify_win(title, text):
from ctypes import Structure, windll, POINTER, sizeof
from ctypes.wintypes import DWORD, HANDLE, WINFUNCTYPE, BOOL, UINT
class FLASHWINDOW(Structure):
_fields_ = [("cbSize", UINT),
("hwnd", HANDLE),
("dwFlags", DWORD),
("uCount", UINT),
("dwTimeout", DWORD)]
FlashWindowExProto = WINFUNCTYPE(BOOL, POINTER(FLASHWINDOW))
FlashWindowEx = FlashWindowExProto(("FlashWindowEx", windll.user32))
FLASHW_CAPTION = 0x01
FLASHW_TRAY = 0x02
FLASHW_TIMERNOFG = 0x0C
params = FLASHWINDOW(sizeof(FLASHWINDOW),
windll.kernel32.GetConsoleWindow(),
FLASHW_CAPTION | FLASHW_TRAY | FLASHW_TIMERNOFG, 3, 0)
FlashWindowEx(params)
def notify_darwin(title, text):
try:
import Foundation
bundleDict = Foundation.NSBundle.mainBundle().infoDictionary()
bundleIdentifier = 'CFBundleIdentifier'
if bundleIdentifier not in bundleDict:
bundleDict[bundleIdentifier] = 'mach'
note = Foundation.NSUserNotification.alloc().init()
note.setTitle_(title)
note.setInformativeText_(text)
now = Foundation.NSDate.dateWithTimeInterval_sinceDate_(0, Foundation.NSDate.date())
note.setDeliveryDate_(now)
centre = Foundation.NSUserNotificationCenter.defaultUserNotificationCenter()
centre.scheduleNotification_(note)
except ImportError:
raise Exception("Optional Python module 'pyobjc' is not installed.")
def notify_build_done(elapsed):
"""Generate desktop notification when build is complete and the
elapsed build time was longer than 30 seconds."""
if elapsed > 30:
notify("Servo build", "Completed in %0.2fs" % elapsed)
def notify(title, text):
"""Generate a desktop notification using appropriate means on
supported platforms Linux, Windows, and Mac OS. On unsupported
platforms, this function acts as a no-op."""
platforms = {
"linux": notify_linux,
"linux2": notify_linux,
"win": notify_win,
"darwin": notify_darwin
}
func = platforms.get(sys.platform)
if func is not None:
try:
func(title, text)
except Exception as e:
extra = getattr(e, "message", "")
print("[Warning] Could not generate notification! %s" % extra, file=sys.stderr)
def call(*args, **kwargs):
"""Wrap `subprocess.call`, printing the command if verbose=True."""
verbose = kwargs.pop('verbose', False)
if verbose:
print(' '.join(args[0]))
return subprocess.call(*args, **kwargs)
@CommandProvider
class MachCommands(CommandBase):
@Command('build',
description='Build Servo',
category='build')
@CommandArgument('--target', '-t',
default=None,
help='Cross compile for given target platform')
@CommandArgument('--release', '-r',
action='store_true',
help='Build in release mode')
@CommandArgument('--dev', '-d',
action='store_true',
help='Build in development mode')
@CommandArgument('--jobs', '-j',
default=None,
help='Number of jobs to run in parallel')
@CommandArgument('--features',
default=None,
help='Space-separated list of features to also build',
nargs='+')
@CommandArgument('--android',
default=None,
action='store_true',
help='Build for Android')
@CommandArgument('--debug-mozjs',
default=None,
action='store_true',
help='Enable debug assertions in mozjs')
@CommandArgument('--verbose', '-v',
action='store_true',
help='Print verbose output')
@CommandArgument('params', nargs='...',
help="Command-line arguments to be passed through to Cargo")
def build(self, target=None, release=False, dev=False, jobs=None,
features=None, android=None, verbose=False, debug_mozjs=False, params=None):
if android is None:
android = self.config["build"]["android"]
features = features or []
opts = params or []
base_path = self.get_target_dir()
release_path = path.join(base_path, "release", "servo")
dev_path = path.join(base_path, "debug", "servo")
release_exists = path.exists(release_path)
dev_exists = path.exists(dev_path)
if not (release or dev):
if self.config["build"]["mode"] == "dev":
dev = True
elif self.config["build"]["mode"] == "release":
release = True
elif release_exists and not dev_exists:
release = True
elif dev_exists and not release_exists:
dev = True
else:
print("Please specify either --dev (-d) for a development")
print(" build, or --release (-r) for an optimized build.")
sys.exit(1)
if release and dev:
print("Please specify either --dev or --release.")
sys.exit(1)
self.ensure_bootstrapped()
if release:
opts += ["--release"]
if target:
opts += ["--target", target]
if jobs is not None:
opts += ["-j", jobs]
if verbose:
opts += ["-v"]
if android:
opts += ["--target", "arm-linux-androideabi"]
if debug_mozjs or self.config["build"]["debug-mozjs"]:
features += ["script/debugmozjs"]
if is_headless_build():
opts += ["--no-default-features"]
features += ["headless"]
if android:
features += ["android_glue"]
if features:
opts += ["--features", "%s" % ' '.join(features)]
build_start = time()
env = self.build_env()
if android:
# Build OpenSSL for android
make_cmd = ["make"]
if jobs is not None:
make_cmd += ["-j" + jobs]
with cd(self.android_support_dir()):
status = call(
make_cmd + ["-f", "openssl.makefile"],
env=self.build_env(),
verbose=verbose)
if status:
return status
openssl_dir = path.join(self.android_support_dir(), "openssl-1.0.1k")
env['OPENSSL_LIB_DIR'] = openssl_dir
env['OPENSSL_INCLUDE_DIR'] = path.join(openssl_dir, "include")
env['OPENSSL_STATIC'] = 'TRUE'
if not (self.config["build"]["ccache"] == ""):
env['CCACHE'] = self.config["build"]["ccache"]
status = call(
["cargo", "build"] + opts,
env=env, cwd=self.servo_crate(), verbose=verbose)
elapsed = time() - build_start
# Generate Desktop Notification if elapsed-time > some threshold value
notify_build_done(elapsed)
print("Build completed in %0.2fs" % elapsed)
return status
@Command('build-cef',
description='Build the Chromium Embedding Framework library',
category='build')
@CommandArgument('--jobs', '-j',
default=None,
help='Number of jobs to run in parallel')
@CommandArgument('--verbose', '-v',
action='store_true',
help='Print verbose output')
@CommandArgument('--release', '-r',
action='store_true',
help='Build in release mode')
def build_cef(self, jobs=None, verbose=False, release=False):
self.ensure_bootstrapped()
ret = None
opts = []
if jobs is not None:
opts += ["-j", jobs]
if verbose:
opts += ["-v"]
if release:
opts += ["--release"]
build_start = time()
with cd(path.join("ports", "cef")):
ret = call(["cargo", "build"] + opts,
env=self.build_env(), verbose=verbose)
elapsed = time() - build_start
# Generate Desktop Notification if elapsed-time > some threshold value
notify_build_done(elapsed)
print("CEF build completed in %0.2fs" % elapsed)
return ret
@Command('build-gonk',
description='Build the Gonk port',
category='build')
@CommandArgument('--jobs', '-j',
default=None,
help='Number of jobs to run in parallel')
@CommandArgument('--verbose', '-v',
action='store_true',
help='Print verbose output')
@CommandArgument('--release', '-r',
action='store_true',
help='Build in release mode')
def build_gonk(self, jobs=None, verbose=False, release=False):
self.ensure_bootstrapped()
ret = None
opts = []
if jobs is not None:
opts += ["-j", jobs]
if verbose:
opts += ["-v"]
if release:
opts += ["--release"]
opts += ["--target", "arm-linux-androideabi"]
env = self.build_env(gonk=True)
build_start = time()
with cd(path.join("ports", "gonk")):
ret = call(["cargo", "build"] + opts, env=env, verbose=verbose)
elapsed = time() - build_start
# Generate Desktop Notification if elapsed-time > some threshold value
notify_build_done(elapsed)
print("Gonk build completed in %0.2fs" % elapsed)
return ret
@Command('build-tests',
description='Build the Servo test suites',
category='build')
@CommandArgument('--jobs', '-j',
default=None,
help='Number of jobs to run in parallel')
@CommandArgument('--release', default=False, action="store_true",
help="Build tests with release mode")
def build_tests(self, jobs=None, verbose=False, release=False):
self.ensure_bootstrapped()
args = ["cargo", "test", "--no-run"]
if is_headless_build():
args += ["--no-default-features", "--features", "headless"]
if release:
args += ["--release"]
return call(
args,
env=self.build_env(), cwd=self.servo_crate(), verbose=verbose)
@Command('clean',
description='Clean the build directory.',
category='build')
@CommandArgument('--manifest-path',
default=None,
help='Path to the manifest to the package to clean')
@CommandArgument('--verbose', '-v',
action='store_true',
help='Print verbose output')
@CommandArgument('params', nargs='...',
help="Command-line arguments to be passed through to Cargo")
def clean(self, manifest_path, params, verbose=False):
self.ensure_bootstrapped()
opts = []
if manifest_path:
opts += ["--manifest-path", manifest_path]
if verbose:
opts += ["-v"]
opts += params
return call(["cargo", "clean"] + opts,
env=self.build_env(), cwd=self.servo_crate(), verbose=verbose)