[tbb-dev] Tor Browser for Android Build Script

Matthew Finkel matthew.finkel at gmail.com
Mon Aug 27 16:26:57 UTC 2018


Hi everyone,

I wrote a build script for TBA-alpha, while integration into
tor-browser-build is still being worked on. It works well on Debian
Stretch and Ubuntu Bionic (the only two systems on which I tested it).

It creates a (configurable) build directory where it clones or downloads
every component and then builds them as needed - checking file hashes,
commits hashes, and tags when relevant. I don't expect many of you will
care too much about this, but you can give it a try if you want.

One suggestion I have is creating a new user for this (I create a user
named tba_builder), simply so there is a clean build environment. The
script, by default, runs `git clean` within the git repos before
configuring them, so if you don't want your working directory destroyed
you should comment those lines in the script before running it.

When I run it, I don't provide any command line arguments:

  tba_builder at localhost:~$ time ./build_tba_alpha

and it creates a directory called tba_build_dir, changes directory into
that directory, and then sets HOME=`pwd`. Unfortunately, some of the
Adnroid build system continue writing in /home/tba_builder instead of
/home/tba_builder/tba_build_dir, but it wasn't worth spending time
troubleshooting that.

Almost all of the values are configurable, simply look at the variables
at the top of the script and you can override them by setting
environment variable before executing the script.

Let me know if you have any questions.

- Matt
-------------- next part --------------
#!/bin/bash

#set -x
# Build Tor Browser for Android - Alpha
#
# This build script creates a Tor Browser for Android (TBA) package

if [ -z "${TORBROWSER_GIT_URL}" ]; then
    TORBROWSER_GIT_URL=https://git.torproject.org/tor-browser.git
fi
if [ -z "${TORBROWSER_GIT_TAG}" ]; then
    TORBROWSER_GIT_TAG=tor-browser-mobile-60.1.0esr-1.0-build2
fi
if [ -z "${TORBROWSER_GIT_COMMIT_HASH}" ]; then
    TORBROWSER_GIT_COMMIT_HASH=a0620db9e7cd08e3d67a42d0c5b1067d5b3ed355
fi
if [ -z "${TORBROWSER_GIT_REPO_DIR}" ]; then
    TORBROWSER_GIT_REPO_DIR=tor-browser
fi
if [ -z "${TORBUTTON_GIT_URL}" ]; then
    TORBUTTON_GIT_URL=https://git.torproject.org/torbutton.git
fi
if [ -z "${TORBUTTON_GIT_TAG}" ]; then
    TORBUTTON_GIT_TAG=2.0.4
fi
if [ -z "${TORBUTTON_GIT_COMMIT_HASH}" ]; then
    TORBUTTON_GIT_COMMIT_HASH=dc772e2c9f17cb0cf5d006f24c35430fadaf1aae
fi
if [ -z "${TORBUTTON_GIT_REPO_DIR}" ]; then
    TORBUTTON_GIT_REPO_DIR=torbutton
fi
if [ -z "${HTTPS_EVERYWHERE_GIT_URL}" ]; then
    HTTPS_EVERYWHERE_GIT_URL=https://git.torproject.org/https-everywhere.git
fi
if [ -z "${HTTPS_EVERYWHERE_GIT_TAG}" ]; then
    HTTPS_EVERYWHERE_GIT_TAG=2018.8.22
fi
if [ -z "${HTTPS_EVERYWHERE_GIT_COMMIT_HASH}" ]; then
    HTTPS_EVERYWHERE_GIT_COMMIT_HASH=161a08daedaff6c4da2b653900f3977755453563
fi
if [ -z "${HTTPS_EVERYWHERE_GIT_REPO_DIR}" ]; then
    HTTPS_EVERYWHERE_GIT_REPO_DIR=https-everywhere
fi
if [ -z "${HTTPS_EVERYWHERE_URL}" ]; then
    HTTPS_EVERYWHERE_URL=https://www.eff.org/files/https-everywhere-latest.xpi
fi
if [ -z "${HTTPS_EVERYWHERE_HASH}" ]; then
    HTTPS_EVERYWHERE_HASH=b37b8ddc871e539e97075b7ae9555c076003a51b389776bc2a4729e3f13690ce
fi
if [ -z "${HTTPS_EVERYWHERE_FILE}" ]; then
    HTTPS_EVERYWHERE_FILE=https-everywhere-eff at eff.org.xpi
fi
if [ -z "${NOSCRIPT_URL}" ]; then
    NOSCRIPT_URL=https://addons.cdn.mozilla.net/user-media/addons/722/noscript_security_suite-10.1.8.16-an+fx.xpi
fi
if [ -z "${NOSCRIPT_HASH}" ]; then
    NOSCRIPT_HASH=fdc539412a61e6109b9fd331c90bd73faa787eefe1ee8d06b57dc3d906c541cd
fi
if [ -z "${NOSCRIPT_FILE}" ]; then
    NOSCRIPT_FILE={73a6fe31-595d-460b-a920-fcc0f8843232}.xpi
fi
if [ -z "${TBA_BUILD_DIR}" ]; then
    TBA_BUILD_DIR=tba_build_dir
fi
if [ -z "${MOZ_FORCE_BOOTSTRAP}" ]; then
    MOZ_FORCE_BOOTSTRAP=
fi


TORSOCKS=

has_git_tag() {
    GIT_TAG="$1"

    git_log=`git log -1 ${GIT_TAG}`
    if [ ! $? = 0 ]; then
        echo "Git tag '${GIT_TAG}' does not exist"
        return 1
    fi
    return 0
}

has_git_hash() {
    GIT_TAG="$1"
    GIT_COMMIT_HASH="$2"

    commit_hash=`git log -1 --pretty=format:%H ${GIT_TAG}`
    if [ ! $? = 0 ]; then
        echo "Git tag '${GIT_TAG}' does not exist"
        return 1;
    fi
    if [ "${commit_hash}" = "${GIT_COMMIT_HASH}" ]; then
        echo "Git commit hash '${GIT_COMMIT_HASH}' is good!"
        return 0;
    fi
    echo "Git commit hash '${GIT_COMMIT_HASH}' on tag '${GIT_TAG}' does not exist"
    return 1;
}

has_git_tag_and_hash() {
    GIT_REPO="$1"
    GIT_REPO_GIT_TAG="${GIT_REPO}_GIT_TAG"
    GIT_REPO_GIT_COMMIT_HASH="${GIT_REPO}_GIT_COMMIT_HASH"

    # Sorry, these are disgusting.
    eval "GIT_TAG=\${${GIT_REPO_GIT_TAG}}"
    eval "EXPECTED_HASH=\${${GIT_REPO_GIT_COMMIT_HASH}}"

    has_git_tag "${GIT_TAG}"
    if [ ! $? = 0 ]; then
        return 1
    fi
    has_git_hash "${GIT_TAG}" "${EXPECTED_HASH}"
    if [ ! $? = 0 ]; then
        return 1
    fi
}

clone() {
    GIT_REPO="$1"
    REPO_DIR="$2"
    GIT_REPO_GIT_URL="${GIT_REPO}_GIT_URL" 
    
    # Sorry, this are disgusting.
    eval "GIT_REPO_URL=\${${GIT_REPO_GIT_URL}}"

    ${TORSOCKS}git clone "${GIT_REPO_URL}" "${REPO_DIR}"
    if [ ! $? = 0 ]; then
        echo "Git clone '${GIT_REPO_URL}' failed"
        exit 1
    fi
    pushd "${REPO_DIR}"
    has_git_tag_and_hash "${GIT_REPO}"
    popd
    return $?
}

fetch() {
    GIT_REPO="$1"
    REPO_DIR="$2"
    success=0

    pushd "${REPO_DIR}"
    has_git_tag_and_hash "${GIT_REPO}"
    if [ $? = 1 ]; then
        ${TORSOCKS}git fetch
        has_git_tag_and_hash "${GIT_REPO}"
        success=$?
    fi
    popd
    return "${success}"
}

fetch_or_fail() {
    GIT_REPO="$1"
    GIT_REPO_GIT_REPO_DIR="${GIT_REPO}_GIT_REPO_DIR"

    # Sorry, these are disgusting.
    eval "REPO_DIR=\${${GIT_REPO_GIT_REPO_DIR}}"

    if [ -d "${REPO_DIR}" -a -d "${REPO_DIR}/.git" ]; then
        fetch "${GIT_REPO}" "${REPO_DIR}"
    else
        clone "${GIT_REPO}" "${REPO_DIR}"
    fi
    if [ ! $? = 0 ]; then
        echo "Failing. ${GIT_REPO} in ${REPO_DIR} is in unknown state."
        exit 1
    fi
}

check_sha256sum() {
    FILEPATH="$1"
    EXPECTED_HASH="$2"

    sha256sum "${FILEPATH}" | grep "^${EXPECTED_HASH}  ${FILEPATH}$"
    return $?
}

DOWNLOAD_MESSAGE=
download() {
    XPI_URL="$1"
    OUTPUT_FILE="$2"
    CMD=

    if [ -f "${OUTPUT_FILE}" ]; then
        echo "${OUTPUT_FILE}" already exists. Skipping download.
        return 0
    fi
    if [ -n "`which curl`" ]; then
        CMD=curl
    else 
        CMD="wget -O -"
    fi
    DOWNLOAD_MESSAGE=`${TORSOCKS}${CMD} "${XPI_URL}" > "${OUTPUT_FILE}"`
    return $?
}
   
download_or_fail() {
    XPI_NAME="$1"
    XPI_NAME_URL="${XPI_NAME}_URL"
    XPI_NAME_FILE="${XPI_NAME}_FILE"
    XPI_FILE_HASH="${XPI_NAME}_HASH"

    # Sorry, these are disgusting.
    eval "XPI_URL=\${${XPI_NAME_URL}}"
    eval "XPI_FILE=\${${XPI_NAME_FILE}}"
    eval "XPI_HASH=\${${XPI_FILE_HASH}}"

    download "${XPI_URL}" "${XPI_FILE}"
    if [ ! $? = 0 ]; then
        echo "Downloading ${XPI_NAME} from \'${XPI_URL}\' failed:"
        echo "${DOWNLOAD_MESSAGE}"
        exit 1
    fi

    check_sha256sum "${XPI_FILE}" "${XPI_HASH}"
    if [ ! $? = 0 ]; then
        echo "${XPI_NAME} from '${XPI_URL}' failed checksum"
        echo Expected: "${XPI_HASH}"
        echo Have: `sha256sum "${XPI_FILE}" | cut -c -65`
        exit 1
    fi

    # Clear $DOWNLOAD_MESSAGE, in case it was set during the last download
    unset DOWNLOAD_MESSAGE
}

download_extensions() {
    download_or_fail NOSCRIPT
    download_or_fail HTTPS_EVERYWHERE
}

fetch_extensions() {
    #for repo in TORBROWSER TORBUTTON HTTPS_EVERYWHERE;
    for repo in TORBROWSER TORBUTTON;
    do
        fetch_or_fail $repo
    done
}

set_torsocks() {
    if [ -n "`which torsocks`" ]; then
        echo "torsocks found. Connections will go over tor."
        TORSOCKS="torsocks "
    else
        echo "torsocks not found. Connections will not go over tor."
    fi
} 

enter_tba_build_dir() {
    if [ ! -d "${TBA_BUILD_DIR}" ]; then
        mkdir "${TBA_BUILD_DIR}"
        if [ ! -d "${TBA_BUILD_DIR}" ]; then
            echo "Making tba_build_dir failed. Failing."
            exit -1
        fi
    fi
    cd "${TBA_BUILD_DIR}"
    return $?
}

checkout_torbrowser_tag() {
    pushd tor-browser
    git checkout "${TORBROWSER_GIT_TAG}"
    popd
}

mach_artifact() {
    pushd .mozbuild
    ../tor-browser/mach artifact toolchain --from-build linux64-clang
    save=$?
    if [ ! $? = 0 ]; then
        echo "mach bootstrap artifact failed."
    fi
    popd
    return $save
}

mach_bootstrap() {
    if [ -n "${MOZ_FORCE_BOOTSTRAP}" ]; then
        # Fallthrough
        echo "Forcing bootstrap"
    elif [ -d .mozbuild -a -d .mozbuild/android-sdk-linux -a -d .mozbuild/android-ndk-r15c ]; then
        echo "Good, it looks like we're bootstrapped."
        return 0
    else
        echo "Okay, let's run bootstrap"
    fi
    pushd tor-browser
    ./mach bootstrap --no-interactive --application-choice mobile_android
    save=$?
    if [ ! $? = 0 ]; then
        echo "mach bootstrap failed."
	popd
	return $save
    fi

    popd

    . `pwd`/.cargo/env
    rustup target add armv7-linux-androideabi
    save=$?
    if [ ! $? = 0 ]; then
        echo "rustup failed."
    elif [ -n "${MOZ_NO_LOOP_BOOTSTRAP}" ]; then
	    echo "Bootstrapping complete"
    else
	    # Let's run it again, so we can finishin bootstrapping rust
	    export MOZ_FORCE_BOOTSTRAP=1
	    export MOZ_NO_LOOP_BOOTSTRAP=1
	    mach_bootstrap
            if [ ! $? = 0 ]; then
                return $?
            fi
	    mach_artifact
	    save=$?
    fi
    return $save
}

mach_configure() {
    pushd tor-browser
    git clean -fdx
    ./mach configure --with-tor-browser-version=8.0 --with-distribution-id=org.torproject --enable-update-channel=alpha --enable-bundled-fonts
    save=$?
    popd
    return $save
}

build_torbutton() {
    pushd torbutton
    git clean -fdx
    ./makexpi.sh
    save=$?
    popd
    return ${save}
}

copy_torbutton() {
    EXTENSIONS_DIR="$1"
    cp "torbutton/pkg/torbutton-${TORBUTTON_GIT_TAG}.xpi" "${EXTENSIONS_DIR}"/torbutton at torproject.org.xpi
    return $?
}

build_https_everywhere() {
    XPI_NAME="$1"
    pushd https-everywhere
    #git checkout "${HTTPS_EVERYWHERE_GIT_TAG}"
    #git clean -fdx
    #./install-dev-dependencies.sh --no-prompt
    #./make.sh "${HTTPS_EVERYWHERE_GIT_TAG}"
    save=0
    if [ ! -f "pkg/${XPI_NAME}" ]; then
        echo "https-everywhere has many dependencies. Please build it yourself (or download it and place it in `pwd`/pkg/${XPI_NAME})"
        save=1
    fi
    popd
    return ${save}
}

copy_https_everywhere() {
    #XPI_NAME="$1"
    #EXTENSIONS_DIR="$2"
    #cp "https-everywhere/pkg/${XPI_NAME}" "${EXTENSIONS_DIR}"/https-everywhere-eff at eff.org.xpi
    EXTENSIONS_DIR="$1"
    cp "${HTTPS_EVERYWHERE_FILE}" "${EXTENSIONS_DIR}"
    return $?
}

copy_noscript() {
    EXTENSIONS_DIR="$1"
    cp "${NOSCRIPT_FILE}" "${EXTENSIONS_DIR}"
    return $?
}

build_tba() {
    enter_tba_build_dir

    if [ ! $? = 0 ]; then
        echo "Entering the build directory failed. Exiting."
        exit 1
    fi

    set_torsocks
    download_extensions
    fetch_extensions

    echo "We have all the components. Now let's build!"
    echo "First, let's checkout the tor-browser tag."
    checkout_torbrowser_tag

    echo "Second, let's confirm firefox is bootstrapped."

    OLD_HOME="${HOME}"
    export HOME=`pwd`

    ANDROID_NDK_HOME="${HOME}"/.mozbuild/android-ndk-r15c
    ANDROID_SDK_HOME="${HOME}"/.mozbuild/android-sdk-linux
    ANDROID_SDK_ROOT="${ANDROID_SDK_HOME}"
    ANDROID_HOME="${ANDROID_SDK_ROOT}"

    NDK_BASE="${ANDROID_NDK_HOME}"
    SDK_BASE="${ANDROID_SDK_HOME}"

    TB_BUILD_WITH_DISTRIBUTION=1
    MOZCONFIG=${HOME}/tor-browser/.mozconfig-android

    # From `perl projects/firefox/get-moz-build-date 2018 60.1.0`
    MOZ_BUILD_DATE=20180204020101

    export ANDROID_SDK_ROOT ANDROID_HOME ANDROID_NDK_HOME SDK_BASE NDK_BASE MOZCONFIG MOZ_BUILD_DATE
    echo "Sorry, we're resetting PATH"
    export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:${SDK_BASE}/platform-tools"

    mach_bootstrap
    if [ ! $? = 0 ]; then
        echo "Bootstrap failed."
        exit 1
    fi

    echo "Third, let's change the project name."
    sed --in-place 's/^ANDROID_PACKAGE_NAME=org.torproject.torbrowser.*$/ANDROID_PACKAGE_NAME=org.torproject.torbrowser_alpha/' \
        tor-browser/mobile/android/branding/torbrowser/configure.sh

    echo "Fourth, let's configure it."
    mach_configure
    if [ ! $? = 0 ]; then
        echo "Configure failed."
        exit 1
    fi

    EXTENSIONS_DIR=tor-browser/mobile/android/torbrowser/assets/distribution/extensions/
    mkdir -p "${EXTENSIONS_DIR}"

    echo "Fifth, Build and copy torbutton."
    build_torbutton
    if [ ! $? = 0 ]; then
        echo "Building torbutton failed."
        exit 1
    fi
    copy_torbutton "${EXTENSIONS_DIR}"
    if [ ! $? = 0 ]; then
        echo "Copying torbutton failed."
        exit 1
    fi

    echo "Six, Copy https-everywhere."
    #XPI_NAME=https-everywhere-${HTTPS_EVERYWHERE_GIT_TAG}-eff.xpi
    #build_https_everywhere "${XPI_NAME}"
    #if [ ! $? = 0 ]; then
    #    echo "Building https-everywhere failed."
    #    exit 1
    #fi
    #copy_https_everywhere "${XPI_NAME}" "${EXTENSIONS_DIR}"
    copy_https_everywhere "${EXTENSIONS_DIR}"
    if [ ! $? = 0 ]; then
        echo "Copying https-everywhere failed."
        exit 1
    fi

    echo "Seven, Copy NoScript."
    copy_noscript "${EXTENSIONS_DIR}"
    if [ ! $? = 0 ]; then
        echo "Copying noscript failed."
        exit 1
    fi

    echo "Eight, Build and package!"
    # These don't respect $HOME, but it doesn't seem like it matters
    if [ -d ../.gradle ]; then
        mv ../.gradle ./
    fi
    if [ -d ../.android ]; then
        mv ../.android ./
    fi
    pushd tor-browser
    time ./mach build && time ./mach package
    popd
}

build_tba
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://lists.torproject.org/pipermail/tbb-dev/attachments/20180827/4b3270c6/attachment.sig>


More information about the tbb-dev mailing list