summaryrefslogtreecommitdiffstats
path: root/taskcluster/scripts/builder
diff options
context:
space:
mode:
Diffstat (limited to 'taskcluster/scripts/builder')
-rwxr-xr-xtaskcluster/scripts/builder/build-haz-linux.sh89
-rwxr-xr-xtaskcluster/scripts/builder/build-l10n.sh98
-rwxr-xr-xtaskcluster/scripts/builder/build-linux.sh122
-rwxr-xr-xtaskcluster/scripts/builder/build-sm-mozjs-crate.sh18
-rwxr-xr-xtaskcluster/scripts/builder/build-sm-package.sh28
-rwxr-xr-xtaskcluster/scripts/builder/build-sm.sh20
-rwxr-xr-xtaskcluster/scripts/builder/desktop-setup.sh24
-rwxr-xr-xtaskcluster/scripts/builder/get-objdir.py20
-rwxr-xr-xtaskcluster/scripts/builder/hazard-analysis.sh149
-rwxr-xr-xtaskcluster/scripts/builder/install-packages.sh13
-rw-r--r--taskcluster/scripts/builder/setup-ccache.sh9
-rwxr-xr-xtaskcluster/scripts/builder/sm-tooltool-config.sh50
12 files changed, 640 insertions, 0 deletions
diff --git a/taskcluster/scripts/builder/build-haz-linux.sh b/taskcluster/scripts/builder/build-haz-linux.sh
new file mode 100755
index 000000000..1d5ef52ba
--- /dev/null
+++ b/taskcluster/scripts/builder/build-haz-linux.sh
@@ -0,0 +1,89 @@
+#!/bin/bash -ex
+
+function usage() {
+ echo "Usage: $0 [--project <shell|browser>] <workspace-dir> flags..."
+ echo "flags are treated the same way as a commit message would be"
+ echo "(as in, they are scanned for directives just like a try: ... line)"
+}
+
+PROJECT=shell
+WORKSPACE=
+DO_TOOLTOOL=1
+while [[ $# -gt 0 ]]; do
+ if [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]]; then
+ usage
+ exit 0
+ elif [[ "$1" == "--project" ]]; then
+ shift
+ PROJECT="$1"
+ shift
+ elif [[ "$1" == "--no-tooltool" ]]; then
+ shift
+ DO_TOOLTOOL=
+ elif [[ -z "$WORKSPACE" ]]; then
+ WORKSPACE=$( cd "$1" && pwd )
+ shift
+ break
+ fi
+done
+
+SCRIPT_FLAGS="$@"
+
+# Ensure all the scripts in this dir are on the path....
+DIRNAME=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
+PATH=$DIRNAME:$PATH
+
+# Use GECKO_BASE_REPOSITORY as a signal for whether we are running in automation.
+export AUTOMATION=${GECKO_BASE_REPOSITORY:+1}
+
+: ${GECKO_DIR:=$WORKSPACE/gecko}
+: ${TOOLTOOL_MANIFEST:=browser/config/tooltool-manifests/linux64/hazard.manifest}
+: ${TOOLTOOL_CACHE:=$WORKSPACE/tt-cache}
+
+if ! [ -d $GECKO_DIR ]; then
+ echo "GECKO_DIR must be set to a directory containing a gecko source checkout" >&2
+ exit 1
+fi
+GECKO_DIR=$( cd "$GECKO_DIR" && pwd )
+
+# Directory to populate with tooltool-installed tools
+export TOOLTOOL_DIR="$WORKSPACE"
+
+# Directory to hold the (useless) object files generated by the analysis.
+export MOZ_OBJDIR="$WORKSPACE/obj-analyzed"
+mkdir -p "$MOZ_OBJDIR"
+
+if [ -n "$DO_TOOLTOOL" ]; then
+ ( cd $TOOLTOOL_DIR; python $GECKO_DIR/testing/docker/recipes/tooltool.py --url https://api.pub.build.mozilla.org/tooltool/ -m $GECKO_DIR/$TOOLTOOL_MANIFEST fetch -c $TOOLTOOL_CACHE )
+fi
+
+export NO_MERCURIAL_SETUP_CHECK=1
+
+if [[ "$PROJECT" = "browser" ]]; then (
+ cd "$WORKSPACE"
+ set "$WORKSPACE"
+ . setup-ccache.sh
+ # Mozbuild config:
+ export MOZBUILD_STATE_PATH=$WORKSPACE/mozbuild/
+ # Create .mozbuild so mach doesn't complain about this
+ mkdir -p $MOZBUILD_STATE_PATH
+) fi
+. hazard-analysis.sh
+
+build_js_shell
+
+# Artifacts folder is outside of the cache.
+mkdir -p $HOME/artifacts/ || true
+
+function onexit () {
+ grab_artifacts "$WORKSPACE/analysis" "$HOME/artifacts"
+}
+
+trap onexit EXIT
+
+configure_analysis "$WORKSPACE/analysis"
+run_analysis "$WORKSPACE/analysis" "$PROJECT"
+
+check_hazards "$WORKSPACE/analysis"
+
+################################### script end ###################################
diff --git a/taskcluster/scripts/builder/build-l10n.sh b/taskcluster/scripts/builder/build-l10n.sh
new file mode 100755
index 000000000..be16955a5
--- /dev/null
+++ b/taskcluster/scripts/builder/build-l10n.sh
@@ -0,0 +1,98 @@
+#! /bin/bash -vex
+
+set -x -e
+
+echo "running as" $(id)
+
+. /home/worker/scripts/xvfb.sh
+
+####
+# Taskcluster friendly wrapper for performing fx desktop l10n repacks via mozharness.
+# Based on ./build-linux.sh
+####
+
+# Inputs, with defaults
+
+: MOZHARNESS_SCRIPT ${MOZHARNESS_SCRIPT}
+: MOZHARNESS_CONFIG ${MOZHARNESS_CONFIG}
+: MOZHARNESS_ACTIONS ${MOZHARNESS_ACTIONS}
+: MOZHARNESS_OPTIONS ${MOZHARNESS_OPTIONS}
+
+: TOOLTOOL_CACHE ${TOOLTOOL_CACHE:=/home/worker/tooltool-cache}
+
+: NEED_XVFB ${NEED_XVFB:=false}
+
+: WORKSPACE ${WORKSPACE:=/home/worker/workspace}
+
+set -v
+
+fail() {
+ echo # make sure error message is on a new line
+ echo "[build-l10n.sh:error]" "${@}"
+ exit 1
+}
+
+export MOZ_CRASHREPORTER_NO_REPORT=1
+export MOZ_OBJDIR=obj-firefox
+export TINDERBOX_OUTPUT=1
+
+# Ensure that in tree libraries can be found
+export LIBRARY_PATH=$LIBRARY_PATH:$WORKSPACE/src/obj-firefox:$WORKSPACE/src/gcc/lib64
+
+# test required parameters are supplied
+if [[ -z ${MOZHARNESS_SCRIPT} ]]; then fail "MOZHARNESS_SCRIPT is not set"; fi
+if [[ -z ${MOZHARNESS_CONFIG} ]]; then fail "MOZHARNESS_CONFIG is not set"; fi
+
+cleanup() {
+ local rv=$?
+ cleanup_xvfb
+ exit $rv
+}
+trap cleanup EXIT INT
+
+# run XVfb in the background, if necessary
+if $NEED_XVFB; then
+ start_xvfb '1024x768x24' 2
+fi
+
+# set up mozharness configuration, via command line, env, etc.
+
+# $TOOLTOOL_CACHE bypasses mozharness completely and is read by tooltool_wrapper.sh to set the
+# cache. However, only some mozharness scripts use tooltool_wrapper.sh, so this may not be
+# entirely effective.
+export TOOLTOOL_CACHE
+
+# support multiple, space delimited, config files
+config_cmds=""
+for cfg in $MOZHARNESS_CONFIG; do
+ config_cmds="${config_cmds} --config ${cfg}"
+done
+
+# if MOZHARNESS_ACTIONS is given, only run those actions (completely overriding default_actions
+# in the mozharness configuration)
+if [ -n "$MOZHARNESS_ACTIONS" ]; then
+ actions=""
+ for action in $MOZHARNESS_ACTIONS; do
+ actions="$actions --$action"
+ done
+fi
+
+# if MOZHARNESS_OPTIONS is given, append them to mozharness command line run
+# e.g. enable-pgo
+if [ -n "$MOZHARNESS_OPTIONS" ]; then
+ options=""
+ for option in $MOZHARNESS_OPTIONS; do
+ options="$options --$option"
+ done
+fi
+
+cd /home/worker
+
+python2.7 $WORKSPACE/build/src/testing/${MOZHARNESS_SCRIPT} \
+ --disable-mock \
+ --revision ${GECKO_HEAD_REV} \
+ $actions \
+ $options \
+ ${config_cmds} \
+ --log-level=debug \
+ --work-dir=$WORKSPACE/build \
diff --git a/taskcluster/scripts/builder/build-linux.sh b/taskcluster/scripts/builder/build-linux.sh
new file mode 100755
index 000000000..8885abdec
--- /dev/null
+++ b/taskcluster/scripts/builder/build-linux.sh
@@ -0,0 +1,122 @@
+#! /bin/bash -vex
+
+set -x -e
+
+echo "running as" $(id)
+
+. /home/worker/scripts/xvfb.sh
+
+####
+# Taskcluster friendly wrapper for performing fx desktop builds via mozharness.
+####
+
+# Inputs, with defaults
+
+: MOZHARNESS_SCRIPT ${MOZHARNESS_SCRIPT}
+: MOZHARNESS_CONFIG ${MOZHARNESS_CONFIG}
+: MOZHARNESS_ACTIONS ${MOZHARNESS_ACTIONS}
+: MOZHARNESS_OPTIONS ${MOZHARNESS_OPTIONS}
+
+: TOOLTOOL_CACHE ${TOOLTOOL_CACHE:=/home/worker/tooltool-cache}
+
+: NEED_XVFB ${NEED_XVFB:=false}
+
+: MH_CUSTOM_BUILD_VARIANT_CFG ${MH_CUSTOM_BUILD_VARIANT_CFG}
+: MH_BRANCH ${MH_BRANCH:=mozilla-central}
+: MH_BUILD_POOL ${MH_BUILD_POOL:=staging}
+: MOZ_SCM_LEVEL ${MOZ_SCM_LEVEL:=1}
+
+: WORKSPACE ${WORKSPACE:=/home/worker/workspace}
+
+set -v
+
+fail() {
+ echo # make sure error message is on a new line
+ echo "[build-linux.sh:error]" "${@}"
+ exit 1
+}
+
+export MOZ_CRASHREPORTER_NO_REPORT=1
+export MOZ_OBJDIR=obj-firefox
+export TINDERBOX_OUTPUT=1
+
+# use "simple" package names so that they can be hard-coded in the task's
+# extras.locations
+export MOZ_SIMPLE_PACKAGE_NAME=target
+
+# Do not try to upload symbols (see https://bugzilla.mozilla.org/show_bug.cgi?id=1164615)
+export MOZ_AUTOMATION_UPLOAD_SYMBOLS=0
+
+# Ensure that in tree libraries can be found
+export LIBRARY_PATH=$LIBRARY_PATH:$WORKSPACE/src/obj-firefox:$WORKSPACE/src/gcc/lib64
+
+# test required parameters are supplied
+if [[ -z ${MOZHARNESS_SCRIPT} ]]; then fail "MOZHARNESS_SCRIPT is not set"; fi
+if [[ -z ${MOZHARNESS_CONFIG} ]]; then fail "MOZHARNESS_CONFIG is not set"; fi
+
+cleanup() {
+ local rv=$?
+ cleanup_xvfb
+ exit $rv
+}
+trap cleanup EXIT INT
+
+# run XVfb in the background, if necessary
+if $NEED_XVFB; then
+ start_xvfb '1024x768x24' 2
+fi
+
+# set up mozharness configuration, via command line, env, etc.
+
+debug_flag=""
+if [ 0$DEBUG -ne 0 ]; then
+ debug_flag='--debug'
+fi
+
+custom_build_variant_cfg_flag=""
+if [ -n "${MH_CUSTOM_BUILD_VARIANT_CFG}" ]; then
+ custom_build_variant_cfg_flag="--custom-build-variant-cfg=${MH_CUSTOM_BUILD_VARIANT_CFG}"
+fi
+
+# $TOOLTOOL_CACHE bypasses mozharness completely and is read by tooltool_wrapper.sh to set the
+# cache. However, only some mozharness scripts use tooltool_wrapper.sh, so this may not be
+# entirely effective.
+export TOOLTOOL_CACHE
+
+# support multiple, space delimited, config files
+config_cmds=""
+for cfg in $MOZHARNESS_CONFIG; do
+ config_cmds="${config_cmds} --config ${cfg}"
+done
+
+# if MOZHARNESS_ACTIONS is given, only run those actions (completely overriding default_actions
+# in the mozharness configuration)
+if [ -n "$MOZHARNESS_ACTIONS" ]; then
+ actions=""
+ for action in $MOZHARNESS_ACTIONS; do
+ actions="$actions --$action"
+ done
+fi
+
+# if MOZHARNESS_OPTIONS is given, append them to mozharness command line run
+# e.g. enable-pgo
+if [ -n "$MOZHARNESS_OPTIONS" ]; then
+ options=""
+ for option in $MOZHARNESS_OPTIONS; do
+ options="$options --$option"
+ done
+fi
+
+cd /home/worker
+
+python2.7 $WORKSPACE/build/src/testing/${MOZHARNESS_SCRIPT} ${config_cmds} \
+ $debug_flag \
+ $custom_build_variant_cfg_flag \
+ --disable-mock \
+ $actions \
+ $options \
+ --log-level=debug \
+ --scm-level=$MOZ_SCM_LEVEL \
+ --work-dir=$WORKSPACE/build \
+ --branch=${MH_BRANCH} \
+ --build-pool=${MH_BUILD_POOL}
diff --git a/taskcluster/scripts/builder/build-sm-mozjs-crate.sh b/taskcluster/scripts/builder/build-sm-mozjs-crate.sh
new file mode 100755
index 000000000..09c353084
--- /dev/null
+++ b/taskcluster/scripts/builder/build-sm-mozjs-crate.sh
@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+
+set -xe
+
+source $(dirname $0)/sm-tooltool-config.sh
+
+# Ensure that we have a .config/cargo that points us to our vendored crates
+# rather than to crates.io.
+cd "$SRCDIR/.cargo"
+sed -e "s|@top_srcdir@|$SRCDIR|" < config.in | tee config
+
+cd "$SRCDIR/js/src"
+
+export PATH="$PATH:$TOOLTOOL_CHECKOUT/cargo/bin:$TOOLTOOL_CHECKOUT/rustc/bin"
+export RUST_BACKTRACE=1
+
+cargo build --verbose --frozen --features debugmozjs
+cargo build --verbose --frozen
diff --git a/taskcluster/scripts/builder/build-sm-package.sh b/taskcluster/scripts/builder/build-sm-package.sh
new file mode 100755
index 000000000..6bb819f26
--- /dev/null
+++ b/taskcluster/scripts/builder/build-sm-package.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+set -xe
+
+source $(dirname $0)/sm-tooltool-config.sh
+
+mkdir -p $UPLOAD_DIR
+
+# Package up the sources into the release tarball.
+AUTOMATION=1 DIST=$UPLOAD_DIR $SRCDIR/js/src/make-source-package.sh
+
+# Extract the tarball into a new directory in the workspace.
+
+PACKAGE_DIR=$WORK/sm-package
+mkdir -p $PACKAGE_DIR
+pushd $PACKAGE_DIR
+
+tar -xjvf $UPLOAD_DIR/mozjs-*.tar.bz2
+
+: ${PYTHON:=python2.7}
+
+# Build the freshly extracted, packaged SpiderMonkey.
+pushd ./mozjs-*/js/src
+AUTOMATION=1 $PYTHON ./devtools/automation/autospider.py --skip-tests=checks $SPIDERMONKEY_VARIANT
+popd
+
+# Copy artifacts for upload by TaskCluster
+cp -rL ./mozjs-*/obj-spider/dist/bin/{js,jsapi-tests,js-gdb.py,libmozjs*} $UPLOAD_DIR
diff --git a/taskcluster/scripts/builder/build-sm.sh b/taskcluster/scripts/builder/build-sm.sh
new file mode 100755
index 000000000..d61a7a81c
--- /dev/null
+++ b/taskcluster/scripts/builder/build-sm.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+set -x
+
+source $(dirname $0)/sm-tooltool-config.sh
+
+: ${PYTHON:=python2.7}
+
+# Run the script
+export MOZ_UPLOAD_DIR="$UPLOAD_DIR"
+AUTOMATION=1 $PYTHON $SRCDIR/js/src/devtools/automation/autospider.py $SPIDERMONKEY_VARIANT
+BUILD_STATUS=$?
+
+# Ensure upload dir exists
+mkdir -p $UPLOAD_DIR
+
+# Copy artifacts for upload by TaskCluster
+cp -rL $SRCDIR/obj-spider/dist/bin/{js,jsapi-tests,js-gdb.py} $UPLOAD_DIR
+
+exit $BUILD_STATUS
diff --git a/taskcluster/scripts/builder/desktop-setup.sh b/taskcluster/scripts/builder/desktop-setup.sh
new file mode 100755
index 000000000..4b74a1201
--- /dev/null
+++ b/taskcluster/scripts/builder/desktop-setup.sh
@@ -0,0 +1,24 @@
+#!/bin/bash -ex
+
+test $MOZCONFIG # mozconfig is required...
+test -d $1 # workspace must exist at this point...
+WORKSPACE=$( cd "$1" && pwd )
+
+. setup-ccache.sh
+
+# Gecko source:
+export GECKO_DIR=$WORKSPACE/gecko
+# Gaia source:
+export GAIA_DIR=$WORKSPACE/gaia
+# Mozbuild config:
+export MOZBUILD_STATE_PATH=$WORKSPACE/mozbuild/
+
+# Create .mozbuild so mach doesn't complain about this
+mkdir -p $MOZBUILD_STATE_PATH
+
+### Install package dependencies
+install-packages.sh ${TOOLTOOL_DIR:-$GECKO_DIR}
+
+# Ensure object-folder exists
+export MOZ_OBJDIR=$WORKSPACE/object-folder/
+mkdir -p $MOZ_OBJDIR
diff --git a/taskcluster/scripts/builder/get-objdir.py b/taskcluster/scripts/builder/get-objdir.py
new file mode 100755
index 000000000..132e20d4f
--- /dev/null
+++ b/taskcluster/scripts/builder/get-objdir.py
@@ -0,0 +1,20 @@
+#!/usr/bin/env python2.7
+
+from __future__ import print_function
+import sys
+import os
+import json
+import subprocess
+from StringIO import StringIO
+
+gecko_dir = sys.argv[1]
+os.chdir(gecko_dir)
+
+result = subprocess.check_output(["./mach", "environment", "--format", "json"])
+environment = json.load(StringIO(result))
+
+topobjdir = environment["mozconfig"]["topobjdir"]
+if topobjdir is None:
+ topobjdir = sys.argv[2]
+
+print(topobjdir)
diff --git a/taskcluster/scripts/builder/hazard-analysis.sh b/taskcluster/scripts/builder/hazard-analysis.sh
new file mode 100755
index 000000000..d3e574742
--- /dev/null
+++ b/taskcluster/scripts/builder/hazard-analysis.sh
@@ -0,0 +1,149 @@
+#!/bin/bash -ex
+
+[ -n "$WORKSPACE" ]
+[ -n "$MOZ_OBJDIR" ]
+[ -n "$GECKO_DIR" ]
+
+HAZARD_SHELL_OBJDIR=$WORKSPACE/obj-haz-shell
+JS_SRCDIR=$GECKO_DIR/js/src
+ANALYSIS_SRCDIR=$JS_SRCDIR/devtools/rootAnalysis
+
+export CC="$TOOLTOOL_DIR/gcc/bin/gcc"
+export CXX="$TOOLTOOL_DIR/gcc/bin/g++"
+
+PYTHON=python2.7
+if ! which $PYTHON; then
+ PYTHON=python
+fi
+
+
+function check_commit_msg () {
+ ( set +e;
+ if [[ -n "$AUTOMATION" ]]; then
+ hg --cwd "$GECKO_DIR" log -r. --template '{desc}\n' | grep -F -q -- "$1"
+ else
+ echo -- "$SCRIPT_FLAGS" | grep -F -q -- "$1"
+ fi
+ )
+}
+
+if check_commit_msg "--dep"; then
+ HAZ_DEP=1
+fi
+
+function build_js_shell () {
+ # Must unset MOZ_OBJDIR and MOZCONFIG here to prevent the build system from
+ # inferring that the analysis output directory is the current objdir. We
+ # need a separate objdir here to build the opt JS shell to use to run the
+ # analysis.
+ (
+ unset MOZ_OBJDIR
+ unset MOZCONFIG
+ ( cd $JS_SRCDIR; autoconf-2.13 )
+ if [[ -z "$HAZ_DEP" ]]; then
+ [ -d $HAZARD_SHELL_OBJDIR ] && rm -rf $HAZARD_SHELL_OBJDIR
+ fi
+ mkdir -p $HAZARD_SHELL_OBJDIR || true
+ cd $HAZARD_SHELL_OBJDIR
+ $JS_SRCDIR/configure --enable-optimize --disable-debug --enable-ctypes --enable-nspr-build --without-intl-api --with-ccache
+ make -j4
+ ) # Restore MOZ_OBJDIR and MOZCONFIG
+}
+
+function configure_analysis () {
+ local analysis_dir
+ analysis_dir="$1"
+
+ if [[ -z "$HAZ_DEP" ]]; then
+ [ -d "$analysis_dir" ] && rm -rf "$analysis_dir"
+ fi
+
+ mkdir -p "$analysis_dir" || true
+ (
+ cd "$analysis_dir"
+ cat > defaults.py <<EOF
+js = "$HAZARD_SHELL_OBJDIR/dist/bin/js"
+analysis_scriptdir = "$ANALYSIS_SRCDIR"
+objdir = "$MOZ_OBJDIR"
+source = "$GECKO_DIR"
+sixgill = "$TOOLTOOL_DIR/sixgill/usr/libexec/sixgill"
+sixgill_bin = "$TOOLTOOL_DIR/sixgill/usr/bin"
+EOF
+
+ cat > run-analysis.sh <<EOF
+#!/bin/sh
+if [ \$# -eq 0 ]; then
+ set gcTypes
+fi
+export ANALYSIS_SCRIPTDIR="$ANALYSIS_SRCDIR"
+exec "$ANALYSIS_SRCDIR/analyze.py" "\$@"
+EOF
+ chmod +x run-analysis.sh
+ )
+}
+
+function run_analysis () {
+ local analysis_dir
+ analysis_dir="$1"
+ local build_type
+ build_type="$2"
+
+ if [[ -z "$HAZ_DEP" ]]; then
+ [ -d $MOZ_OBJDIR ] && rm -rf $MOZ_OBJDIR
+ fi
+
+ (
+ cd "$analysis_dir"
+ $PYTHON "$ANALYSIS_SRCDIR/analyze.py" --buildcommand="$GECKO_DIR/testing/mozharness/scripts/spidermonkey/build.${build_type}"
+ )
+}
+
+function grab_artifacts () {
+ local analysis_dir
+ analysis_dir="$1"
+ local artifacts
+ artifacts="$2"
+
+ (
+ cd "$analysis_dir"
+ ls -lah
+
+ # Do not error out if no files found
+ shopt -s nullglob
+ set +e
+ for f in *.txt *.lst; do
+ gzip -9 -c "$f" > "${artifacts}/$f.gz"
+ done
+
+ # Check whether the user requested .xdb file upload in the top commit comment
+ if check_commit_msg "--upload-xdbs"; then
+ HAZ_UPLOAD_XDBS=1
+ fi
+
+ if [ -n "$HAZ_UPLOAD_XDBS" ]; then
+ for f in *.xdb; do
+ bzip2 -c "$f" > "${artifacts}/$f.bz2"
+ done
+ fi
+ )
+}
+
+function check_hazards () {
+ (
+ set +e
+ NUM_HAZARDS=$(grep -c 'Function.*has unrooted.*live across GC call' "$1"/rootingHazards.txt)
+ NUM_UNSAFE=$(grep -c '^Function.*takes unsafe address of unrooted' "$1"/refs.txt)
+ NUM_UNNECESSARY=$(grep -c '^Function.* has unnecessary root' "$1"/unnecessary.txt)
+
+ set +x
+ echo "TinderboxPrint: rooting hazards<br/>$NUM_HAZARDS"
+ echo "TinderboxPrint: unsafe references to unrooted GC pointers<br/>$NUM_UNSAFE"
+ echo "TinderboxPrint: unnecessary roots<br/>$NUM_UNNECESSARY"
+
+ if [ $NUM_HAZARDS -gt 0 ]; then
+ echo "TEST-UNEXPECTED-FAIL $NUM_HAZARDS hazards detected" >&2
+ echo "TinderboxPrint: documentation<br/><a href='https://wiki.mozilla.org/Javascript:Hazard_Builds'>static rooting hazard analysis failures</a>, visit \"Inspect Task\" link for hazard details"
+ exit 1
+ fi
+ )
+}
diff --git a/taskcluster/scripts/builder/install-packages.sh b/taskcluster/scripts/builder/install-packages.sh
new file mode 100755
index 000000000..2f5cdf489
--- /dev/null
+++ b/taskcluster/scripts/builder/install-packages.sh
@@ -0,0 +1,13 @@
+#!/bin/bash -vex
+
+gecko_dir=$1
+test -d $gecko_dir
+test -n "$TOOLTOOL_CACHE"
+test -n "$TOOLTOOL_MANIFEST"
+test -n "$TOOLTOOL_REPO"
+test -n "$TOOLTOOL_REV"
+
+tc-vcs checkout $gecko_dir/tooltool $TOOLTOOL_REPO $TOOLTOOL_REPO $TOOLTOOL_REV
+
+(cd $gecko_dir; python $gecko_dir/tooltool/tooltool.py --url https://api.pub.build.mozilla.org/tooltool/ -m $gecko_dir/$TOOLTOOL_MANIFEST fetch -c $TOOLTOOL_CACHE)
+
diff --git a/taskcluster/scripts/builder/setup-ccache.sh b/taskcluster/scripts/builder/setup-ccache.sh
new file mode 100644
index 000000000..3c03b2640
--- /dev/null
+++ b/taskcluster/scripts/builder/setup-ccache.sh
@@ -0,0 +1,9 @@
+#! /bin/bash -ex
+
+test -d $1 # workspace must exist at this point...
+WORKSPACE=$( cd "$1" && pwd )
+
+export CCACHE_DIR=$WORKSPACE/ccache
+
+ccache -M 12G
+ccache -s
diff --git a/taskcluster/scripts/builder/sm-tooltool-config.sh b/taskcluster/scripts/builder/sm-tooltool-config.sh
new file mode 100755
index 000000000..b6a062858
--- /dev/null
+++ b/taskcluster/scripts/builder/sm-tooltool-config.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+
+set -xe
+
+: ${TOOLTOOL_SERVER:=https://api.pub.build.mozilla.org/tooltool/}
+: ${SPIDERMONKEY_VARIANT:=plain}
+: ${UPLOAD_DIR:=$HOME/artifacts/}
+: ${WORK:=$HOME/workspace}
+: ${SRCDIR:=$WORK/build/src}
+
+mkdir -p $WORK
+cd $WORK
+
+# Need to install things from tooltool. Figure out what platform to use.
+
+case $(uname -m) in
+ i686 | arm )
+ BITS=32
+ ;;
+ *)
+ BITS=64
+ ;;
+esac
+
+case "$OSTYPE" in
+ darwin*)
+ PLATFORM_OS=macosx
+ ;;
+ linux-gnu)
+ PLATFORM_OS=linux
+ ;;
+ msys)
+ PLATFORM_OS=win
+ ;;
+ *)
+ echo "Unrecognized OSTYPE '$OSTYPE'" >&2
+ PLATFORM_OS=linux
+ ;;
+esac
+
+# Install everything needed for the browser on this platform. Not all of it is
+# necessary for the JS shell, but it's less duplication to share tooltool
+# manifests.
+BROWSER_PLATFORM=$PLATFORM_OS$BITS
+: ${TOOLTOOL_MANIFEST:=browser/config/tooltool-manifests/$BROWSER_PLATFORM/releng.manifest}
+
+: ${TOOLTOOL_CHECKOUT:=$WORK}
+export TOOLTOOL_CHECKOUT
+
+(cd $TOOLTOOL_CHECKOUT && python ${SRCDIR}/testing/docker/recipes/tooltool.py --url $TOOLTOOL_SERVER -m $SRCDIR/$TOOLTOOL_MANIFEST fetch ${TOOLTOOL_CACHE:+ -c $TOOLTOOL_CACHE})