| #!/bin/bash |
| # Copyright 2016 Google Inc. All Rights Reserved. |
| # This script downloads, compiles, and installs various versions of gcc. |
| |
| script_path="$(readlink -f ${BASH_SOURCE[0]})" |
| script_dir="$(dirname "${script_path}")" |
| script_name="$(basename "${script_path}")" |
| dirty_build= |
| force_build= |
| local_build= |
| verbose= |
| |
| arm_git_url="https://nest-open-source.googlesource.com/stadia-controller/gcc-arm-none-eabi" |
| |
| function wget() { |
| 1>&2 command wget --no-show-progress -c "$@" |
| } |
| |
| function log() { |
| 1>&2 echo "${script_name}: $*" |
| } |
| |
| function is_verbose() { |
| [[ "${verbose}" == "true" ]] |
| } |
| |
| function is_dirty_build() { |
| [[ "${dirty_build}" == "true" ]] |
| } |
| |
| function is_forced_build() { |
| [[ "${force_build}" == "true" ]] |
| } |
| |
| function is_local_build() { |
| [[ "${local_build}" == "true" ]] |
| } |
| |
| function log_verbose() { |
| if is_verbose; then |
| log "$@" |
| fi |
| } |
| |
| exit_commands=() |
| function run_exit_commands() { |
| log_verbose "Cleaning up." |
| for command_id in "${exit_commands[@]}"; do |
| typeset -n command_line=${command_id} |
| ( |
| if is_verbose; then |
| set -x |
| fi |
| "${command_line[@]}" |
| ) |
| eval "${command_id}"'=""' |
| done |
| } |
| |
| function add_exit_command() { |
| local index="${#exit_commands[@]}" |
| local command_id="__exit_command_$index" |
| eval "${command_id}"'=( "$@" )' |
| exit_commands+=("${command_id}") |
| } |
| |
| function make_clean_dir() { |
| local dir="$1" |
| if [[ -d "${dir}" ]]; then |
| log "Removing ${dir}" |
| rm -rf "${dir}" || return |
| fi |
| log_verbose "Creating ${dir}" |
| mkdir -p "${dir}" |
| } |
| |
| function build_gcc() { |
| local toolchain_name="gcc" |
| |
| local version="${1:-7.3.0}" |
| shift |
| local arch="${1:-x86_64}" |
| shift |
| local vendor="${1:-unknown}" |
| shift |
| local os="${1:-linux-gnu}" |
| shift |
| |
| local get_source_command="${1:-default_get_source}" |
| shift |
| local get_dependencies_command="${1:-default_get_dependencies}" |
| shift |
| local configure_gcc_command="${1:-default_configure_gcc}" |
| shift |
| local make_gcc_command="${1:-default_make_gcc}" |
| shift |
| local install_gcc_command="${1:-default_install_gcc}" |
| shift |
| |
| local build_duration="3 hours" |
| |
| local triplet="${arch}-${vendor}-${os}" |
| if [[ "${vendor}" == "unknown" ]]; then |
| triplet="${arch}-${os}" |
| fi |
| |
| local target_prefix="" |
| local target_arguments=() |
| if [[ "${triplet}" != "x86_64-linux"* ]]; then |
| target_prefix="${triplet}-" |
| target_arguments=("--target=${triplet}") |
| fi |
| |
| local toolchain_full_name="${toolchain_name}-${triplet}-${version}" |
| |
| local canonical_path="${HOME}/.cache/toolchains" |
| local base_path="${TOOLCHAINS_DIR:=${canonical_path}}" |
| local toolchain_path="${base_path}/${toolchain_full_name}" |
| local toolchain_binary="${toolchain_path}/bin/${target_prefix}g++" |
| local build_date="$(command date '+%Y.%m.%d-%H%M%S')" |
| local logfile="${base_path}/log-${toolchain_full_name}-${build_date}.txt" |
| |
| if [[ -x "${toolchain_binary}" ]] && "${toolchain_binary}" --version &> /dev/null; then |
| # The toolchain binary already exists. |
| if is_forced_build; then |
| log "The force flag was specified." |
| log "We will remove the existing toolchain during the install phase." |
| log "Toolchain path to remove: ${toolchain_path}" |
| else |
| log "${toolchain_full_name} already available." |
| return 0 |
| fi |
| elif [[ -d "${toolchain_path}" ]]; then |
| cat <<EOF 1>&2 |
| WARNING: ${toolchain_full_name} directory |
| |
| ${toolchain_path} |
| |
| already exists, but it does not contain a valid binary. Perhaps a previous build |
| was interrupted or failed? |
| |
| We will remove this directory during the install phase. |
| |
| EOF |
| fi |
| |
| cat <<EOF 1>&2 |
| Downloading and building ${toolchain_full_name} into: |
| |
| ${toolchain_path} |
| |
| Log file can be found at: |
| |
| ${logfile} |
| |
| This may take up to ${build_duration}. |
| |
| EOF |
| |
| local work_dir="$(mktemp -d -t "${toolchain_name}-XXXXXX")" |
| if ! is_dirty_build; then |
| add_exit_command rm -rf "${work_dir}" |
| fi |
| local gcc_build_dir="${work_dir}/gcc-build" |
| local gcc_install_dir="${toolchain_path}" |
| |
| mkdir -p "$(dirname "${gcc_install_dir}")" |
| |
| ( |
| set -x |
| |
| log "Fetching ${toolchain_full_name} source code" |
| cd "${work_dir}" |
| local gcc_source_dir="${work_dir}/$("${get_source_command}" "${version}")" |
| if [[ ! -d "${gcc_source_dir}" ]]; then |
| log "Unable to get source with ${get_source_command}." |
| return 1 |
| fi |
| |
| log "Fetching ${toolchain_full_name} dependencies" |
| cd "${gcc_source_dir}" |
| "${get_dependencies_command}" || return |
| |
| log "Configuring ${toolchain_full_name} for installation into ${gcc_install_dir}" |
| |
| mkdir -p "${gcc_build_dir}" |
| cd "${gcc_build_dir}" |
| "${configure_gcc_command}" \ |
| "${gcc_source_dir}" \ |
| "${gcc_install_dir}" \ |
| "${target_arguments[@]}" || return |
| |
| # Build and install gcc to toolchain directory. |
| log "Building ${toolchain_full_name}" |
| "${make_gcc_command}" "${gcc_source_dir}" "${gcc_install_dir}" || return |
| make_clean_dir "${toolchain_path}" |
| |
| log "Installing ${toolchain_full_name}" |
| "${install_gcc_command}" "${gcc_source_dir}" "${gcc_install_dir}" |
| |
| if [[ ! -f "${toolchain_binary}" ]]; then |
| log "No resulting binary found, assuming build failure." |
| rm -rf "${toolchain_path}" |
| return 1 |
| fi |
| |
| "${toolchain_binary}" --version |
| ) |& tee "${logfile}" |
| } |
| |
| function default_get_source() { |
| local version="$1" |
| local gcc_version="gcc-${version}" |
| local tar_file="${gcc_version}.tar" |
| local mirror_url="http://ftpmirror.gnu.org/gcc/${gcc_version}" |
| local archive_extensions=("xz" "bz2" "gz") |
| for extension in "${archive_extensions[@]}"; do |
| local file="${tar_file}.${extension}" |
| wget "${mirror_url}/${file}" |
| |
| if ! $?; then |
| break |
| fi |
| done |
| |
| if [[ ! -f "${file}" ]]; then |
| log "Unable to get source." |
| return 1 |
| fi |
| |
| wget "${mirror_url}/${file}.sig" || return |
| wget "http://ftp.gnu.org/gnu/gnu-keyring.gpg" || return |
| |
| local signature_invalid="$(gpg --verify --no-default-keyring --keyring ./gnu-keyring.gpg "${file}.sig")" |
| if [[ -n "${signature_invalid}" ]]; then |
| log "Invalid signature, aborting." |
| return 1 |
| fi |
| tar -xf "${file}" || return |
| echo -n "${file%.tar*}" |
| } |
| |
| function default_get_dependencies() { |
| if [[ -x ./contrib/download_prerequisites ]]; then |
| ./contrib/download_prerequisites |
| fi |
| } |
| |
| function default_configure_gcc() { |
| local gcc_source_dir="$1" |
| shift |
| local gcc_install_dir="$1" |
| shift |
| |
| "${gcc_source_dir}/configure" \ |
| --prefix="${gcc_install_dir}" \ |
| --enable-languages=c,c++ \ |
| "$@" |
| } |
| |
| function default_make_gcc() { |
| local gcc_source_dir="$1" |
| shift |
| local gcc_install_dir="$1" |
| shift |
| |
| make -j"$(nproc)" |
| } |
| |
| function default_install_gcc() { |
| local gcc_source_dir="$1" |
| shift |
| local gcc_install_dir="$1" |
| shift |
| |
| make install-strip |
| } |
| |
| function arm_get_source() { |
| if is_local_build; then |
| 1>&2 cp -R "${script_dir}" "gcc-arm-none-eabi" |
| echo -n "gcc-arm-none-eabi" |
| fi |
| |
| 1>&2 git clone "${arm_git_url}" || return |
| echo -n "gcc-arm-none-eabi" |
| } |
| |
| function no_op() { |
| return # NO-OP |
| } |
| |
| function arm_make_gcc() { |
| local gcc_source_dir="$1" |
| shift |
| local gcc_install_dir="$1" |
| shift |
| |
| ( |
| cd "${gcc_source_dir}" || return |
| ./build-prerequisites.sh --skip_steps=mingw32 || return |
| ./build-toolchain.sh \ |
| --skip_steps=manual,gdb-with-python,mingw32,mingw32-gdb-with-python \ |
| --threads=posix |
| ) |
| } |
| |
| function arm_install_gcc() { |
| local gcc_source_dir="$1" |
| shift |
| local gcc_install_dir="$1" |
| shift |
| |
| local package_dir="${gcc_source_dir}/pkg" |
| |
| ( |
| if [[ ! -d "${package_dir}" ]]; then |
| log "No package directory: ${package_dir}" |
| fi |
| |
| cd "${package_dir}" |
| |
| shopt -s nullglob |
| local archive=(gcc-*-linux.tar.*) |
| if (( ${#archive[@]} < 1 )); then |
| log "No toolchain archive found in: ${package_dir}" |
| return 1 |
| fi |
| |
| local archive_dir="${archive%-linux.tar*}" |
| tar xf "${archive}" || return |
| local archive_files=("${archive_dir}"/*) |
| if (( ${#archive_files[@]} < 1 )); then |
| log "${archive} didn't extract to ${archive_dir}" |
| return 1 |
| fi |
| |
| cp -R "${archive_files[@]}" "${gcc_install_dir}/" |
| ) |
| } |
| |
| function usage() { |
| cat <<EOF 1>&2 |
| usage: ${script_name} [-fhv] |
| |
| where: |
| -d Dirty. Do not clean up the work directory. |
| -f Force a rebuild even if toolchain exists. |
| -h Show this help. |
| -l Build using the source in the script directory. |
| -v Increase verbosity. |
| |
| EOF |
| } |
| |
| function main() { |
| trap run_exit_commands EXIT |
| |
| OPTIND=1 |
| while getopts "dfhlv?" opt; do |
| case "$opt" in |
| h|\?) |
| usage |
| return 0 |
| ;; |
| d) |
| dirty_build=true |
| ;; |
| f) |
| force_build=true |
| ;; |
| l) |
| local_build=true |
| ;; |
| v) |
| verbose=true |
| ;; |
| esac |
| done |
| |
| shift $((OPTIND-1)) |
| |
| [[ "$1" == "--" ]] && shift |
| |
| build_gcc 7.2.1 arm none eabi arm_get_source no_op no_op arm_make_gcc \ |
| arm_install_gcc |
| } |
| |
| main "$@" |