Created
December 25, 2016 11:12
-
-
Save e-oz/ff27a1d0e65bf21fb1c7943c068deea1 to your computer and use it in GitHub Desktop.
Rust installation old way - sudo curl -sSf https://static.rust-lang.org/rustup.sh | sh -s -- --channel=beta
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
# Copyright 2015 The Rust Project Developers. See the COPYRIGHT | |
# file at the top-level directory of this distribution and at | |
# http://rust-lang.org/COPYRIGHT. | |
# | |
# 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. | |
# # Coding conventions | |
# | |
# * globals are `like_this`. | |
# * locals are `_like_this`. | |
# * exported values are `LIKE_THIS`. | |
# * out-of-band return values are put into `RETVAL`. | |
# | |
# # Error handling | |
# | |
# Oh, my goodness, error handling. It's terrifying. | |
# | |
# This doesn't use -e because it makes it hard to control the | |
# presentation of and response to errors. | |
# | |
# `set -u` is on, which means undefined variables are errors. | |
# Generally when evaluating a variable that may not exist I'll | |
# write `${mystery_variable-}`, which results in "" if the name | |
# is undefined. | |
# | |
# Every command should be expected to return 0 on success, and | |
# non-zero on failure. In one case, for `download_and_check`, the | |
# error code needs to be interpreted more carefully because there are | |
# multiple successful return codes. Additional return values may be | |
# passed the `$RETVAL` global or further `RETVAL_FOO` globals as | |
# needed. | |
# | |
# Most commands are executed via wrappers that provide extra diagnostics | |
# and error handling: `run`, which prints the command on failure, and | |
# returns the error code, `ignore` which does the same, but is used | |
# to indicate the error code won't be handled, and `ensure`, which | |
# prints the command on failure, and also exits the process. | |
# | |
# Pass errors on: `run cmd arg1 arg2 || return 1`. `run` will run | |
# the command, printing it if it fails; the `|| return 1` passes the | |
# error on to the caller. `ensure cmd arg1 arg1`, runs the command, | |
# printing it if it fails, and terminating execution. | |
# | |
# Don't make typos. You just have to be better than that. | |
# | |
# This code is very careful never to create empty paths. Any time a | |
# new string that will be used as a path is produced, it is checked | |
# with `assert_nz`. Likewise, pretty much any time a string is | |
# constructed via command invocation it needs to be tested against | |
# the empty string. | |
# | |
# Temporary files must be carefully deleted on every error path. | |
set -u # Undefined variables are errors | |
main() { | |
assert_cmds | |
set_globals | |
handle_command_line_args "$@" | |
} | |
set_globals() { | |
# Environment sanity checks | |
assert_nz "$HOME" "\$HOME is undefined" | |
assert_nz "$0" "\$0 is undefined" | |
# Some constants | |
version=0.0.1 | |
metadata_version=1 | |
# Find the location of the distribution server | |
default_dist_server="https://static.rust-lang.org" | |
insecure_dist_server="http://static-rust-lang-org.s3-website-us-west-1.amazonaws.com" | |
dist_server="${RUSTUP_DIST_SERVER-$default_dist_server}" | |
gpg_available=false | |
# Check to see if GNUPG version 2 is installed, falling back to using version 1 by default | |
gpg_exe=gpg | |
if command -v gpg2 > /dev/null 2>&1; then | |
gpg_exe=gpg2 | |
fi | |
if command -v "$gpg_exe" > /dev/null 2>&1; then | |
gpg_available=true | |
fi | |
# The directory on the server containing the dist artifacts | |
rust_dist_dir=dist | |
default_channel="stable" | |
# Set up the rustup data dir | |
rustup_dir="${RUSTUP_HOME-$HOME/.rustup.sh}" | |
assert_nz "$rustup_dir" "rustup_dir" | |
# Install prefix can be set by the environment | |
default_prefix="${RUSTUP_PREFIX-/usr/local}" | |
default_save=false | |
if [ -n "${RUSTUP_SAVE-}" ]; then | |
default_save=true | |
fi | |
# Data locations | |
version_file="$rustup_dir/rustup-version" | |
temp_dir="$rustup_dir/tmp" | |
dl_dir="$rustup_dir/dl" | |
# Set up the GPG key | |
official_rust_gpg_key=" | |
-----BEGIN PGP PUBLIC KEY BLOCK----- | |
Version: GnuPG v1 | |
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 | |
Mve696B5tlHyc1KxjHR6w9GRsh4= | |
=5FXw | |
-----END PGP PUBLIC KEY BLOCK----- | |
" | |
if [ -n "${RUSTUP_GPG_KEY-}" ]; then | |
gpg_key=$(cat "$RUSTUP_GPG_KEY") | |
else | |
gpg_key="$official_rust_gpg_key" | |
fi | |
# This is just used by test.sh for testing sha256sum fallback to shasum | |
sha256sum_cmd="${__RUSTUP_MOCK_SHA256SUM-sha256sum}" | |
flag_verbose=false | |
flag_yes=false | |
if [ -n "${RUSTUP_VERBOSE-}" ]; then | |
flag_verbose=true | |
fi | |
} | |
# Ensuresthat ~/.rustup.sh exists and uses the correct format | |
initialize_metadata() { | |
local _disable_sudo="$1" | |
verbose_say "checking metadata version" | |
if [ "$rustup_dir" = "$HOME" ]; then | |
err "RUSTUP_HOME is the same as HOME. this cannot be correct. aborting" | |
fi | |
# This tries to guard against dumb values of RUSTUP_HOME like ~/ since | |
# rustup will delete the entire directory. | |
if [ -e "$rustup_dir" -a ! -e "$version_file" ]; then | |
say "rustup home dir exists at $rustup_dir but version file $version_file does not." | |
say "this may be old rustup metadata, in which case it can be deleted." | |
err "this is very suspicous. aborting." | |
fi | |
# Oh, my. We used to encourage people running this script as root, | |
# and that resulted in users' ~/.rustup.sh directories being owned by | |
# root (running `sudo sh` doesn't change $HOME apparently). Now | |
# that we're not running as root, we can't touch our ~/.rustup.sh | |
# directory. Try to fix that. | |
if [ -e "$version_file" ]; then | |
local _can_write=true | |
local _probe_file="$rustup_dir/write-probe" | |
ignore touch "$_probe_file" 2> /dev/null | |
if [ $? != 0 ]; then | |
_can_write=false | |
else | |
ensure rm "$_probe_file" | |
fi | |
if [ "$_can_write" = false ]; then | |
say "$rustup_dir is unwritable. it was likely created by a previous rustup run under sudo" | |
if [ "$_disable_sudo" = false ]; then | |
say "deleting it with sudo" | |
run sudo rm -R "$rustup_dir" | |
if [ $? != 0 ]; then | |
err "unable to delete unwritable $rustup_dir" | |
fi | |
else | |
say_err "not deleting it because of --disable-sudo" | |
err "delete $rustup_dir to continue. aborting" | |
fi | |
fi | |
fi | |
ensure mkdir -p "$rustup_dir" | |
rustup_dir="$(abs_path "$rustup_dir")" | |
assert_nz "$rustup_dir" "rustup_dir" | |
if [ ! -e "$version_file" ]; then | |
verbose_say "writing metadata version $metadata_version" | |
echo "$metadata_version" > "$version_file" | |
need_ok "failed to write metadata version" | |
else | |
local _current_version="$(ensure cat "$version_file")" | |
assert_nz "$_current_version" | |
verbose_say "got metadata version $_current_version" | |
if [ "$_current_version" != "$metadata_version" ]; then | |
# Wipe the out of date metadata. | |
say "metadata is out of date. deleting." | |
ensure rm -R "$rustup_dir" | |
ensure mkdir -p "$rustup_dir" | |
echo "$metadata_version" > "$version_file" | |
need_ok "failed to write metadata version" | |
fi | |
fi | |
} | |
handle_command_line_args() { | |
local _save="$default_save" | |
local _date="" | |
local _prefix="$default_prefix" | |
local _uninstall=false | |
local _channel="" | |
local _help=false | |
local _revision="" | |
local _spec="" | |
local _update_hash_file="" | |
local _disable_ldconfig=false | |
local _disable_sudo=false | |
local _extra_targets="" | |
local _add_target="" | |
local _list_targets=false | |
local _arg | |
for _arg in "$@"; do | |
case "${_arg%%=*}" in | |
--save ) | |
_save=true | |
;; | |
--uninstall ) | |
_uninstall=true | |
;; | |
-h | --help ) | |
_help=true | |
;; | |
--verbose) | |
# verbose is a global flag | |
flag_verbose=true | |
;; | |
--disable-ldconfig) | |
_disable_ldconfig=true | |
;; | |
--disable-sudo) | |
_disable_sudo=true | |
;; | |
-y | --yes) | |
# yes is a global flag | |
flag_yes=true | |
;; | |
--list-available-targets) | |
_list_targets=true | |
;; | |
--version) | |
echo "rustup.sh $version" | |
exit 0 | |
;; | |
--prefix) | |
if is_value_arg "$_arg" "prefix"; then | |
_prefix="$(get_value_arg "$_arg")" | |
fi | |
;; | |
--channel) | |
if is_value_arg "$_arg" "channel"; then | |
_channel="$(get_value_arg "$_arg")" | |
fi | |
;; | |
--date) | |
if is_value_arg "$_arg" "date"; then | |
_date="$(get_value_arg "$_arg")" | |
fi | |
;; | |
--revision) | |
if is_value_arg "$_arg" "revision"; then | |
_revision="$(get_value_arg "$_arg")" | |
fi | |
;; | |
--spec) | |
if is_value_arg "$_arg" "spec"; then | |
_spec="$(get_value_arg "$_arg")" | |
fi | |
;; | |
--update-hash-file) | |
if is_value_arg "$_arg" "update-hash-file"; then | |
# This option is used by multirust to short-circuit reinstalls | |
# when the channel has not been updated by examining a content | |
# hash in the update-hash-file | |
_update_hash_file="$(get_value_arg "$_arg")" | |
fi | |
;; | |
--with-target) | |
if is_value_arg "$_arg" "with-target"; then | |
local _next_extra_target="$(get_value_arg "$_arg")" | |
_extra_targets="$_extra_targets $_next_extra_target" | |
fi | |
;; | |
--add-target) | |
if is_value_arg "$_arg" "add-target"; then | |
_add_target="$(get_value_arg "$_arg")" | |
fi | |
;; | |
*) | |
echo "Unknown argument '$_arg', displaying usage:" | |
echo ${_arg%%=*} | |
_help=true | |
;; | |
esac | |
done | |
if [ "$_help" = true ]; then | |
print_help | |
exit 0 | |
fi | |
# Try to run `any` command with `sudo` to check we have enough rights | |
ensure maybe_sudo "$_disable_sudo" true | |
# Make sure either rust256sum or shasum exists | |
need_shasum_cmd | |
# Check that the various toolchain-specifying flags don't conflict | |
if [ -n "$_revision" ]; then | |
if [ -n "$_channel" ]; then | |
err "the --revision flag may not be combined with --channel" | |
fi | |
if [ -n "$_date" ]; then | |
err "the --revision flag may not be combined with --date" | |
fi | |
fi | |
if [ -n "$_spec" ]; then | |
if [ -n "$_channel" ]; then | |
err "the --spec flag may not be combined with --channel" | |
fi | |
if [ -n "$_revision" ]; then | |
err "the --spec flag may not be combined with --revision" | |
fi | |
fi | |
if [ -n "$_add_target" ]; then | |
if [ -n "$_channel" ]; then | |
err "the --add-target flag may not be combined with --channel" | |
fi | |
if [ -n "$_date" ]; then | |
err "the --add-target flag may not be combined with --date" | |
fi | |
if [ -n "$_spec" ]; then | |
err "the --add-target flag may not be combined with --spec" | |
fi | |
if [ -n "$_revision" ]; then | |
err "the --add-target flag may not be combined with --revision" | |
fi | |
fi | |
if [ "$_list_targets" = true ]; then | |
if [ -n "$_channel" ]; then | |
err "the --list-available-targets flag may not be combined with --channel" | |
fi | |
if [ -n "$_date" ]; then | |
err "the --list-available-targets flag may not be combined with --date" | |
fi | |
if [ -n "$_spec" ]; then | |
err "the --list-available-targets flag may not be combined with --spec" | |
fi | |
if [ -n "$_revision" ]; then | |
err "the --list-available-targets flag may not be combined with --revision" | |
fi | |
fi | |
if [ -z "$_channel" -a -z "$_revision" -a -z "$_spec" ]; then | |
_channel="$default_channel" | |
fi | |
# Toolchain can be either a channel, channel + date, or an explicit version | |
local _toolchain="" | |
if [ -n "$_channel" ]; then | |
validate_channel "$_channel" | |
_toolchain="$_channel" | |
if [ -n "$_date" ]; then | |
validate_date "$_date" | |
_toolchain="$_toolchain-$_date" | |
fi | |
elif [ -n "$_revision" ]; then | |
_toolchain="$_revision" | |
elif [ -n "$_spec" ]; then | |
_toolchain="$_spec" | |
fi | |
assert_nz "$_toolchain" "toolchain" | |
# --add-target is non-interactive | |
if [ -n "$_add_target" ]; then | |
flag_yes=true | |
fi | |
# --list-targets is non-interactive | |
if [ -n "$_list_targets" ]; then | |
flag_yes=true | |
fi | |
if [ "$flag_yes" = false ]; then | |
# Running in interactive mode, check that a tty exists | |
check_tty | |
# Print the welcome / warning message and wait for confirmation | |
print_welcome_message "$_prefix" "$_uninstall" "$_disable_sudo" | |
get_tty_confirmation | |
fi | |
# All work is done in the ~/.rustup.sh dir, which will be deleted | |
# afterward if the user doesn't pass --save. *If* ~/.rustup.sh | |
# already exists and they *did not* pass --save, we'll pretend | |
# they did anyway to avoid deleting their data. | |
local _preserve_rustup_dir="$_save" | |
if [ "$_save" = false -a -e "$version_file" ]; then | |
verbose_say "rustup home exists but not asked to save. saving anyway" | |
_preserve_rustup_dir=true | |
fi | |
# Make sure our data directory exists and is the right format | |
initialize_metadata "$_disable_sudo" | |
# OK, time to do the things | |
local _succeeded=true | |
if [ "$_list_targets" = true ]; then | |
list_targets "$_prefix" | |
if [ $? != 0 ]; then | |
_succeeded=false | |
fi | |
elif [ -n "$_add_target" ]; then | |
add_target_to_install "$_prefix" "$_add_target" "$_save" "$_disable_sudo" | |
if [ $? != 0 ]; then | |
_succeeded=false | |
fi | |
elif [ "$_uninstall" = false ]; then | |
install_toolchain_from_dist "$_toolchain" "$_prefix" "$_save" "$_update_hash_file" \ | |
"$_disable_ldconfig" "$_disable_sudo" "$_extra_targets" | |
if [ $? != 0 ]; then | |
_succeeded=false | |
fi | |
else | |
remove_toolchain "$_prefix" "$_disable_sudo" | |
if [ $? != 0 ]; then | |
_succeeded=false | |
fi | |
fi | |
# Remove the temporary directory. | |
# This will not happen if we hit certain hard errors earlier. | |
if [ "$_preserve_rustup_dir" = false ]; then | |
verbose_say "removing rustup home $rustup_dir" | |
ensure rm -R "$rustup_dir" | |
else | |
verbose_say "leaving rustup home $rustup_dir" | |
fi | |
if [ "$_succeeded" = false ]; then | |
exit 1 | |
fi | |
} | |
is_value_arg() { | |
local _arg="$1" | |
local _name="$2" | |
echo "$_arg" | grep -q -- "--$_name=" | |
return $? | |
} | |
get_value_arg() { | |
local _arg="$1" | |
echo "$_arg" | cut -f2 -d= | |
} | |
validate_channel() { | |
local _channel="$1" | |
case "$_channel" in | |
stable | beta | nightly ) | |
;; | |
* ) | |
err "channel must be either 'stable', 'beta', or 'nightly'" | |
;; | |
esac | |
} | |
validate_date() { | |
local _date="$1" | |
case "$_date" in | |
[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] ) | |
;; | |
* ) | |
err "date must be in YYYY-MM-DD format" | |
;; | |
esac | |
} | |
print_welcome_message() { | |
local _prefix="$1" | |
local _uninstall="$2" | |
local _disable_sudo="$3" | |
cat <<EOF | |
Welcome to Rust. | |
EOF | |
if [ "$_disable_sudo" = false ]; then | |
if [ "$(id -u)" = 0 ]; then | |
cat <<EOF | |
WARNING: This script appears to be running as root. While it will work | |
correctly, it is no longer necessary for rustup.sh to run as root. | |
EOF | |
fi | |
fi | |
if [ "$_uninstall" = false ]; then | |
cat <<EOF | |
This script will download the Rust compiler and its package manager, Cargo, and | |
install them to $_prefix. You may install elsewhere by running this script | |
with the --prefix=<path> option. | |
EOF | |
else | |
cat <<EOF | |
This script will uninstall the existing Rust installation at $_prefix. | |
EOF | |
fi | |
if [ "$_disable_sudo" = false ]; then | |
cat <<EOF | |
The installer will run under 'sudo' and may ask you for your password. If you do | |
not want the script to run 'sudo' then pass it the --disable-sudo flag. | |
EOF | |
fi | |
if [ "$_uninstall" = false ]; then | |
cat <<EOF | |
You may uninstall later by running $_prefix/lib/rustlib/uninstall.sh, | |
or by running this script again with the --uninstall flag. | |
EOF | |
fi | |
echo | |
} | |
# Updating toolchains | |
# Returns 0 on success, 1 on error | |
install_toolchain_from_dist() { | |
local _toolchain="$1" | |
local _prefix="$2" | |
local _save="$3" | |
local _update_hash_file="$4" | |
local _disable_ldconfig="$5" | |
local _disable_sudo="$6" | |
local _extra_targets="$7" | |
# FIXME: Right now installing rust over top of multirust will | |
# result in a broken multirust installation. | |
# This hack tries to avoid that by detecting if multirust is installed, | |
# but I'd rather fix this by having the installers understand negative | |
# dependencies. | |
local _potential_multirust_bin="$_prefix/bin/multirust" | |
if [ -e "$_potential_multirust_bin" ]; then | |
say_err "multirust appears to be installed at the destination, $_potential_multirust_bin" | |
say_err "installing rust over multirust will result in breakage." | |
local _potential_uninstaller="$_prefix/lib/rustlib/uninstall.sh" | |
if [ -e "$_potential_uninstaller" ]; then | |
say_err "consider uninstalling multirust first by running $_potential_uninstaller" | |
fi | |
err "aborting" | |
fi | |
if [ "$gpg_available" = true ]; then | |
# disabling https avoids rust#21293 | |
say "gpg available. signatures will be verified" | |
else | |
say "gpg not available. signatures will not be verified" | |
fi | |
get_architecture || return 1 | |
local _arch="$RETVAL" | |
assert_nz "$_arch" "arch" | |
# Inspect any existing installation for additional stds that must be upgraded | |
# and add them to the list | |
merge_existing_extra_targets "$_extra_targets" "$_arch" "$_prefix" || return 1 | |
_extra_targets="$RETVAL" | |
# We're going to fill in this variables by interrogating the channel | |
# metadata. There are two revisions of the channel metadata, v1 was | |
# a very simple list of binaries; v2 has richer information about | |
# the available packages. | |
# The URL of the main installer | |
local _remote_rust_installer | |
# A space-separated list of other things to install | |
local _extra_remote_installers | |
local _manifest_to_stash="" | |
# First, try to download a v2 manifest, before falling back to v1 codepaths. | |
download_rust_manifest_v2 "$_toolchain" | |
if [ $? = 0 ]; then | |
local _manifest="$RETVAL" | |
assert_nz "$_manifest" "manifest" | |
# We'll save the manifest in the install folder for future modifications | |
_manifest_to_stash="$_manifest" | |
validate_manifest_v2 "$_manifest" | |
if [ $? != 0 ]; then | |
say_err "failed to validate channel manifest for '$_toolchain'" | |
return 1 | |
fi | |
determine_remote_rust_installer_location_v2 "$_manifest" | |
if [ $? != 0 ]; then | |
say_err "unable to find installer url in manifest" | |
return 1 | |
fi | |
_remote_rust_installer="$RETVAL" | |
if [ "$_extra_targets" != "" ]; then | |
# The user has asked for additional standard libraries. | |
# Figure out where they are. | |
determine_remote_std_locations_v2 "$_manifest" "$_extra_targets" || return 1 | |
_extra_remote_installers="$RETVAL" | |
else | |
_extra_remote_installers="" | |
fi | |
else | |
verbose_say "unable to find v2 manifest. trying v1" | |
if [ "$_extra_targets" != "" ]; then | |
say_err "v1 manifests don't support --with-target" | |
return 1 | |
else | |
_extra_remote_installers="" | |
fi | |
determine_remote_rust_installer_location "$_toolchain" || return 1 | |
_remote_rust_installer="$RETVAL" | |
fi | |
assert_nz "$_remote_rust_installer" "remote rust installer" | |
verbose_say "remote rust installer location: $_remote_rust_installer" | |
say "downloading toolchain for '$_toolchain'" | |
# Download and install rust package | |
download_and_check "$_remote_rust_installer" false "$_update_hash_file" | |
# Hey! I need to check $? twice here, so it has to be | |
# assigned to a named variable, otherwise the second | |
# check against $? will not be what I expect. | |
local _retval=$? | |
if [ "$_retval" = 20 ]; then | |
say "'$_toolchain' is already up to date" | |
# Successful short-circuit using the update-hash | |
return 0 | |
fi | |
if [ "$_retval" != 0 ]; then | |
return 1 | |
fi | |
local _rust_installer_file="$RETVAL" | |
local _rust_installer_cache="$RETVAL_CACHE" | |
local _rust_update_hash="$RETVAL_UPDATE_HASH" | |
assert_nz "$_rust_installer_file" "rust_installer_file" | |
assert_nz "$_rust_installer_cache" "rust_installer_cache" | |
assert_nz "$_rust_update_hash" "rust_update_hash" | |
say "installing toolchain for '$_toolchain'" | |
install_toolchain "$_rust_installer_file" "$_prefix" \ | |
"$_disable_ldconfig" "$_disable_sudo" "$_rust_installer_cache" "$_save" | |
if [ $? != 0 ]; then | |
say_err "failed to install toolchain" | |
return 1 | |
fi | |
# Download and install extra packages | |
# NB: Splitting $_extra_remote_installers on space by not quoting | |
local _extra_remote_installer | |
for _extra_remote_installer in $_extra_remote_installers; do | |
install_extra_component "$_prefix" "$_extra_remote_installer" "$_disable_sudo" "$_save" | |
done | |
# Write the update hash of the rust toolchain to file so that, | |
# when invoked by multirust, the toolchain won't be reinstalled. | |
if [ -n "$_update_hash_file" ]; then | |
echo "$_rust_update_hash" > "$_update_hash_file" | |
if [ $? != 0 ]; then | |
say_err "failed to write update hash to file" | |
return 1 | |
fi | |
fi | |
# Install the manifest for future updates | |
if [ "$_manifest_to_stash" != "" ]; then | |
# Fix for rust-lang/rust#32154. Somehow rustup.sh managed | |
# until today to exist without escaping ~ in prefix. Probably | |
# because it's only ultimately used by the install script, | |
# which is called via sh. This command here though will fail | |
# if prefix contains ~ so run it through `sh` to escape it. | |
local _prefix="$(sh -c "printf '%s' $_prefix")" | |
local _manifest_stash="$_prefix/lib/rustlib/channel-manifest.toml" | |
ensure printf "%s" "$_manifest_to_stash" | \ | |
ensure maybe_sudo "$_disable_sudo" sh -c "cat > \"$_manifest_stash\"" | |
fi | |
} | |
merge_existing_extra_targets() { | |
local _extra_targets="$1" | |
local _primary_arch="$2" | |
local _prefix="$3" | |
local _components_file="$_prefix/lib/rustlib/components" | |
if [ ! -e "$_components_file" ]; then | |
RETVAL="$_extra_targets" | |
return 0 | |
fi | |
local _component | |
while read _component in; do | |
case "$_component" in | |
rust-std-*) | |
# Extract the triple | |
local _arch="$(ensure printf "%s" "$_component" | ensure sed "s/rust-std-//")" | |
assert_nz "$_arch", "arch" | |
# See if we've already got it | |
ignore printf "%s" "$_extra_targets" | grep -q "$_arch" | |
if [ $? = 0 ]; then | |
verbose_say "already installing extra std component: $_arch" | |
else | |
# See if it's the primary target | |
ignore printf "%s" "$_primary_arch" | grep -q "$_arch" | |
if [ $? = 0 ]; then | |
verbose_say "already installing extra std component: $_arch" | |
else | |
verbose_say "found extra std component: $_arch" | |
_extra_targets="$_extra_targets $_arch" | |
fi | |
fi | |
;; | |
*) | |
;; | |
esac | |
done < "$_components_file" | |
RETVAL="$_extra_targets" | |
return 0 | |
} | |
install_toolchain() { | |
local _installer_file="$1" | |
local _prefix="$2" | |
local _disable_ldconfig="$3" | |
local _disable_sudo="$4" | |
local _installer_cache="$5" | |
local _save="$6" | |
# Create a temp directory to put the downloaded toolchain | |
make_temp_dir | |
local _workdir="$RETVAL" | |
assert_nz "$_workdir" "workdir" | |
verbose_say "install work dir: $_workdir" | |
local _failing=false | |
install_toolchain_with_workdir "$_installer_file" "$_prefix" \ | |
"$_disable_ldconfig" "$_disable_sudo" "$_workdir" | |
if [ $? != 0 ]; then | |
_failing=true | |
fi | |
local _retval=$? | |
run rm -R "$_workdir" | |
if [ $? != 0 ]; then | |
say_err "couldn't delete workdir" | |
_failing=true | |
fi | |
# Throw away the cache if not --save | |
if [ "$_save" = false ]; then | |
verbose_say "discarding cache '$_installer_cache'" | |
run rm -R "$_installer_cache" | |
if [ $? != 0 ]; then | |
say_err "couldn't delete cache dir" | |
_failing=true | |
fi | |
fi | |
if [ "$_failing" = true ]; then | |
return 1 | |
fi | |
} | |
install_toolchain_with_workdir() { | |
local _installer="$1" | |
local _prefix="$2" | |
local _disable_ldconfig="$3" | |
local _disable_sudo="$4" | |
local _workdir="$5" | |
local _installer_dir="$_workdir/$(basename "$_installer" | sed s/.tar.gz$//)" | |
# Extract the toolchain | |
say "extracting installer" | |
run tar xzf "$_installer" -C "$_workdir" | |
if [ $? != 0 ]; then | |
verbose_say "failed to extract installer" | |
return 1 | |
fi | |
# Install the toolchain | |
local _toolchain_dir="$_prefix" | |
verbose_say "installing toolchain to '$_toolchain_dir'" | |
if [ "$_disable_ldconfig" = false ]; then | |
maybe_sudo "$_disable_sudo" sh "$_installer_dir/install.sh" --prefix="$_toolchain_dir" | |
else | |
maybe_sudo "$_disable_sudo" sh "$_installer_dir/install.sh" --prefix="$_toolchain_dir" --disable-ldconfig | |
fi | |
if [ $? != 0 ]; then | |
verbose_say "failed to install toolchain" | |
return 1 | |
fi | |
} | |
remove_toolchain() { | |
local _prefix="$1" | |
local _disable_sudo="$2" | |
local _uninstall_script="$_prefix/lib/rustlib/uninstall.sh" | |
if [ -e "$_uninstall_script" ]; then | |
verbose_say "uninstalling from '$_uninstall_script'" | |
maybe_sudo "$_disable_sudo" sh "$_uninstall_script" | |
if [ $? != 0 ]; then | |
say_err "failed to remove toolchain" | |
return 1; | |
fi | |
say "toolchain '$_toolchain' uninstalled" | |
else | |
say "no toolchain installed at '$_prefix'" | |
fi | |
} | |
add_target_to_install() { | |
local _prefix="$1" | |
local _target="$2" | |
local _save="$3" | |
local _disable_sudo="$4" | |
local _manifest_file="$_prefix/lib/rustlib/channel-manifest.toml" | |
if [ ! -e "$_manifest_file" ]; then | |
say_err "no channel manifest at '$_manifest_file'" | |
return 1 | |
fi | |
local _manifest="$(cat "$_manifest_file")" | |
determine_remote_std_locations_v2 "$_manifest" "$_target" || return 1 | |
local _url="$RETVAL" | |
# NB: No quotes around $url - it's a space-separated list with one element. Removing | |
# the quotes to get rid of an extra space | |
install_extra_component "$_prefix" $_url "$_disable_sudo" "$_save" | |
} | |
list_targets() { | |
local _prefix="$1" | |
local _manifest_file="$_prefix/lib/rustlib/channel-manifest.toml" | |
if [ ! -e "$_manifest_file" ]; then | |
say_err "no channel manifest at '$_manifest_file'" | |
return 1 | |
fi | |
local _manifest="$(cat "$_manifest_file")" | |
toml_find_package_triples "$_manifest" rust-std | |
if [ $? != 0 ]; then | |
say_err "error searching manifest for targets" | |
return 1 | |
fi | |
local _all_stds="$RETVAL" | |
# NB: Not quoting to split on space | |
local _std | |
for _std in $_all_stds; do | |
printf "%s\n" "$_std" | |
done | |
} | |
install_extra_component() { | |
local _prefix="$1" | |
local _url="$2" | |
local _disable_sudo="$3" | |
local _save="$4" | |
say "downloading extra component from $_url" | |
download_and_check "$_url" false "" | |
# Don't need to check for the second success value since | |
# we didn't pass an update hash file to download_and_check | |
if [ $? != 0 ]; then | |
return 1 | |
fi | |
local _extra_installer_file="$RETVAL" | |
local _extra_installer_cache="$RETVAL_CACHE" | |
assert_nz "$_extra_installer_file" "extra_installer_file" | |
assert_nz "$_extra_installer_cache" "extra_installer_cache" | |
say "installing extra component from $_extra_installer_file" | |
install_toolchain "$_extra_installer_file" "$_prefix" \ | |
"$_disable_ldconfig" "$_disable_sudo" "$_extra_installer_cache" "$_save" | |
if [ $? != 0 ]; then | |
say_err "failed to install component" | |
return 1 | |
fi | |
} | |
# Manifest v2 interface | |
# Returns 0 on success. | |
# Returns the manifest as a string in RETVAL | |
download_rust_manifest_v2() { | |
local _toolchain="$1" | |
verbose_say "dist_server: $dist_server" | |
case "$_toolchain" in | |
nightly | beta | stable ) | |
local _remote_rust_manifest="$dist_server/$rust_dist_dir/channel-rust-$_toolchain.toml" | |
;; | |
nightly-* | beta-* | stable-* ) | |
extract_channel_and_date_from_toolchain "$_toolchain" || return 1 | |
local _channel="$RETVAL_CHANNEL" | |
local _date="$RETVAL_DATE" | |
assert_nz "$_channel" "channel" | |
assert_nz "$_date" "date" | |
local _remote_rust_manifest="$dist_server/$rust_dist_dir/$_date/channel-rust-$_channel.toml" | |
;; | |
*) | |
verbose_say "interpreting toolchain spec as explicit version" | |
local _remote_rust_manifest="$dist_server/$rust_dist_dir/channel-rust-$_toolchain.toml" | |
;; | |
esac | |
download_manifest "$_toolchain" "rust" "$_remote_rust_manifest" || return 1 | |
local _manifest_file="$RETVAL" | |
local _manifest_cache="$RETVAL_CACHE" | |
local _manifest="$(cat "$_manifest_file")" | |
if [ $? != 0 ]; then | |
say_err "unable to load manifest from disk" | |
run rm -R "$_manifest_cache" | |
return 1 | |
fi | |
run rm -R "$_manifest_cache" || return 1 | |
RETVAL="$_manifest" | |
return 0 | |
} | |
validate_manifest_v2() { | |
local _manifest="$1" | |
toml_find_manifest_version "$_manifest" | |
if [ $? != 0 ]; then | |
say_err "unable to find manifest version" | |
return 1 | |
fi | |
local _manifest_version="$RETVAL" | |
assert_nz "$_manifest_version" "manifest_version" | |
case "$_manifest_version" in | |
2 ) | |
;; | |
* ) | |
say_err "channel manifest has unknown version: $_manifest_version" | |
return 1 | |
;; | |
esac | |
} | |
determine_remote_rust_installer_location_v2() { | |
local _manifest="$1" | |
get_architecture || return 1 | |
local _arch="$RETVAL" | |
assert_nz "$_arch" "arch" | |
toml_find_package_url "$_manifest" rust "$_arch" | |
if [ $? != 0 ]; then | |
say_err "unable to find rust package url in manifest" | |
return 1 | |
fi | |
local _url="$RETVAL" | |
RETVAL="$_url" | |
} | |
determine_remote_std_locations_v2() { | |
local _manifest="$1" | |
local _targets="$2" | |
# A space-separated list of URLs of std installers | |
local _urls="" | |
# Replace commas with spaces | |
_targets="$(printf "%s" "$_targets" | sed "s/,/ /g")" | |
local _target | |
# NB: Purposefully not quoting $_targets to split on space | |
for _target in $_targets; do | |
toml_find_package_url "$_manifest" rust-std "$_target" | |
if [ $? != 0 ]; then | |
say_err "unable to find package url for std, for $_target" | |
return 1 | |
fi | |
_urls="$_urls $RETVAL" | |
done | |
RETVAL="$_urls" | |
} | |
# Manifest v2 toml parsing | |
toml_find_package_url() { | |
local _manifest="$1" | |
local _package="$2" | |
local _arch="$3" | |
verbose_say "looking for pkg.$_package.target.$_arch in manifest" | |
make_temp_dir | |
local _workdir="$RETVAL" | |
assert_nz "$_workdir" "workdir" | |
# Put the manifest in a temp file so it can be read back in | |
# Note the \n. This is needed for `read` to read the last line. | |
# I was surprised. | |
local _tmpfile="$_workdir/manifest" | |
ensure printf "%s\n" "$_manifest" > "$_tmpfile" | |
local _found_package=false | |
local _found_url=false | |
local _url="" | |
local _line | |
while read _line; do | |
case "$_line" in | |
# First look for the package header | |
*"[pkg.$_package.target.$_arch]"*) | |
verbose_say "found $_package.$_arch section in manifest" | |
if [ "$_found_package" = true ]; then err "found package twice"; fi | |
_found_package=true | |
;; | |
# Then find the url | |
*url*=*) | |
if [ "$_found_package" = true -a "$_found_url" = false ]; then | |
_url="$(ensure printf "%s" "$_line" | ensure sed 's/.*url.*\"\(.*\)\".*/\1/')" | |
assert_nz "$_url" "url is empty!" | |
verbose_say "url: $_url" | |
_found_url=true | |
fi | |
;; | |
esac | |
done < "$_tmpfile" | |
ensure rm -R "$_workdir" | |
if [ "$_url" = "" ]; then | |
return 1 | |
fi | |
RETVAL="$_url" | |
} | |
toml_find_package_triples() { | |
local _manifest="$1" | |
local _package="$2" | |
make_temp_dir | |
local _workdir="$RETVAL" | |
assert_nz "$_workdir" "workdir" | |
local _tmpfile="$_workdir/manifest" | |
ensure printf "%s\n" "$_manifest" > "$_tmpfile" | |
local _triples="" | |
local _line | |
while read _line; do | |
case "$_line" in | |
*"[pkg.$_package.target".*"]"*) | |
verbose_say "found $_package in manifest" | |
local _triple="$(ensure printf "%s" "$_line" | ensure sed "s/.*pkg\.$_package\.target\.\(.*\)]/\1/")" | |
verbose_say "triple: $_triple" | |
_triples="$_triples $_triple" | |
;; | |
esac | |
done < "$_tmpfile" | |
ensure rm -R "$_workdir" | |
RETVAL="$_triples" | |
} | |
toml_find_manifest_version() { | |
local _manifest="$1" | |
make_temp_dir | |
local _workdir="$RETVAL" | |
assert_nz "$_workdir" "workdir" | |
local _tmpfile="$_workdir/manifest" | |
ensure printf "%s\n" "$_manifest" > "$_tmpfile" | |
local _manifest_version="" | |
local _line | |
while read _line; do | |
case "$_line" in | |
*manifest-version*=*) | |
_manifest_version="$(ensure printf "%s" "$_line" | ensure sed 's/.*manifest-version.*\"\(.*\)\".*/\1/')" | |
assert_nz "$_manifest_version" "manifest_version is empty!" | |
verbose_say "manifest-version: $_manifest_version" | |
;; | |
esac | |
done < "$_tmpfile" | |
ensure rm -R "$_workdir" | |
if [ "$_manifest_version" = "" ]; then | |
return 1 | |
fi | |
RETVAL="$_manifest_version" | |
} | |
# Manifest v1 interface | |
determine_remote_rust_installer_location() { | |
local _toolchain="$1" | |
verbose_say "determining remote rust installer for '$_toolchain'" | |
case "$_toolchain" in | |
nightly | beta | stable | nightly-* | beta-* | stable-* ) | |
download_rust_manifest "$_toolchain" || return 1 | |
local _manifest_file="$RETVAL" | |
assert_nz "$_manifest_file" "manifest file" | |
local _manifest_cache="$RETVAL_CACHE" | |
assert_nz "$_manifest_cache" "manifest cache" | |
get_remote_installer_location_from_manifest "$_toolchain" "$_manifest_file" rust "$rust_dist_dir" || return 1 | |
RETVAL="$RETVAL" | |
verbose_say "deleting cache dir $_manifest_cache" | |
run rm -R "$_manifest_cache" || return 1 | |
;; | |
* ) | |
verbose_say "interpreting toolchain spec as explicit version" | |
get_architecture || return 1 | |
local _arch="$RETVAL" | |
assert_nz "$_arch" "arch" | |
local _file_name="rust-$_toolchain-$_arch.tar.gz" | |
RETVAL="$dist_server/$rust_dist_dir/$_file_name" | |
;; | |
esac | |
} | |
# Returns 0 on success. | |
# Returns the manifest file in RETVAL and its cache dir in RETVAL_CACHE. | |
download_rust_manifest() { | |
local _toolchain="$1" | |
case "$_toolchain" in | |
nightly | beta | stable ) | |
local _remote_rust_manifest="$dist_server/$rust_dist_dir/channel-rust-$_toolchain" | |
;; | |
nightly-* | beta-* | stable-* ) | |
extract_channel_and_date_from_toolchain "$_toolchain" || return 1 | |
local _channel="$RETVAL_CHANNEL" | |
local _date="$RETVAL_DATE" | |
assert_nz "$_channel" "channel" | |
assert_nz "$_date" "date" | |
local _remote_rust_manifest="$dist_server/$rust_dist_dir/$_date/channel-rust-$_channel" | |
;; | |
*) | |
err "unrecognized toolchain spec: $_toolchain" | |
;; | |
esac | |
download_manifest "$_toolchain" "rust" "$_remote_rust_manifest" || return 1 | |
RETVAL="$RETVAL" | |
RETVAL_CACHE="$RETVAL_CACHE" | |
} | |
download_manifest() { | |
local _toolchain="$1" | |
local _name="$2" | |
local _remote_manifest="$3" | |
verbose_say "remote $_name manifest: $_remote_manifest" | |
say "downloading manifest for '$_toolchain'" | |
# It's not possible for $? = 20 here, because the update_hash_file | |
# param is empty | |
download_and_check "$_remote_manifest" true "" || return 1 | |
RETVAL="$RETVAL" | |
RETVAL_CACHE="$RETVAL_CACHE" | |
} | |
get_remote_installer_location_from_manifest() { | |
local _toolchain="$1" | |
local _manifest_file="$2" | |
local _package_name="$3" | |
local _dist_dir="$4" | |
if [ ! -e "$_manifest_file" ]; then | |
err "manifest file '$_manifest_file' does not exist" | |
fi | |
get_architecture | |
local _arch="$RETVAL" | |
assert_nz "$_arch" "arch" | |
local _line | |
while read _line; do | |
# This regex checks for the version in addition to the package name because there | |
# are package names that are substrings of other packages, 'rust-docs' vs. 'rust'. | |
echo "$_line" | egrep "^$_package_name-(nightly|beta|alpha|[0-9]).*$_arch\.tar\.gz" > /dev/null | |
if [ $? = 0 ]; then | |
case "$_toolchain" in | |
nightly | beta | stable ) | |
RETVAL="$dist_server/$_dist_dir/$_line" | |
;; | |
nightly-* | beta-* | stable-* ) | |
extract_channel_and_date_from_toolchain "$_toolchain" || return 1 | |
local _channel="$RETVAL_CHANNEL" | |
local _date="$RETVAL_DATE" | |
assert_nz "$_channel" "channel" | |
assert_nz "$_date" "date" | |
RETVAL="$dist_server/$_dist_dir/$_date/$_line" | |
;; | |
*) | |
err "unrecognized toolchain spec: $_toolchain" | |
;; | |
esac | |
return | |
fi | |
done < "$_manifest_file" | |
err "couldn't find remote installer for '$_arch' in manifest" | |
} | |
extract_channel_and_date_from_toolchain() { | |
local _toolchain="$1" | |
case "$_toolchain" in | |
nightly-20[0-9][0-9]-[0-9][0-9]-[0-9][0-9] | \ | |
beta-20[0-9][0-9]-[0-9][0-9]-[0-9][0-9] | \ | |
stable-20[0-9][0-9]-[0-9][0-9]-[0-9][0-9] ) | |
local _channel="$(ensure echo "$_toolchain" | ensure cut -d- -f1)" | |
assert_nz "$_channel" "channel" | |
local _date="$(ensure echo "$_toolchain" | ensure cut -d- -f2,3,4)" | |
assert_nz "$_date" "date" | |
RETVAL_CHANNEL="$_channel" | |
RETVAL_DATE="$_date" | |
;; | |
*) | |
err "unrecognized toolchain spec: $_toolchain" | |
;; | |
esac | |
} | |
# Tools | |
# FIXME: Temp names based on pid need to worry about pid recycling | |
make_temp_name() { | |
local _pid="$$" | |
assert_nz "$_pid" "pid" | |
local _tmp_number="${NEXT_TMP_NUMBER-0}" | |
local _tmp_name="tmp-$_pid-$_tmp_number" | |
NEXT_TMP_NUMBER=$((_tmp_number + 1)) | |
need_ok "failed to create temp number" | |
assert_nz "$NEXT_TMP_NUMBER" "NEXT_TMP_NUMBER" | |
RETVAL="$_tmp_name" | |
} | |
make_temp_dir() { | |
ensure mkdir -p "$temp_dir" | |
ensure make_temp_name | |
local _tmp_name="$temp_dir/$RETVAL" | |
ensure mkdir -p "$_tmp_name" | |
RETVAL="$_tmp_name" | |
} | |
# Returns 0 on success, like sha256sum | |
check_sums() { | |
local _sumfile="$1" | |
# Hackily edit the sha256 file to workaround a bug in the bots' generation of sums | |
make_temp_dir | |
local _workdir="$RETVAL" | |
assert_nz "$_workdir" "workdir" | |
sed s/tmp\\/dist\\/.*\\/final\\/// "$_sumfile" > "$_workdir/tmpsums" | |
need_ok "failed to generate temporary checksums" | |
local _sumfile_dirname="$(dirname "$_sumfile")" | |
assert_nz "$_sumfile_dirname" "sumfile_dirname" | |
if command -v "$sha256sum_cmd" > /dev/null 2>&1; then | |
(run cd "$_sumfile_dirname" && run "$sha256sum_cmd" -c "$_workdir/tmpsums" > /dev/null) | |
elif command -v shasum > /dev/null 2>&1; then | |
(run cd "$_sumfile_dirname" && run shasum -c -a 256 "$_workdir/tmpsums" > /dev/null) | |
else | |
err "need either sha256sum or shasum" | |
fi | |
local _sum_retval=$? | |
run rm -R "$_workdir" || return 1 | |
return $_sum_retval | |
} | |
# Outputs 40-char sum to stdout | |
create_sum() { | |
local _input="$1" | |
local _sum="none" | |
if command -v "$sha256sum_cmd" > /dev/null 2>&1; then | |
_sum="$(run "$sha256sum_cmd" "$_input" | run head -c 40)" | |
elif command -v shasum > /dev/null 2>&1; then | |
_sum="$(run shasum -a 256 "$_input" | run head -c 40)" | |
else | |
err "need either sha256sum or shasum" | |
fi | |
local _sum_retval=$? | |
assert_nz "$_sum" "sum" | |
ensure printf "$_sum" | |
return $_sum_retval | |
} | |
need_shasum_cmd() { | |
if ! command -v "$sha256sum_cmd" > /dev/null 2>&1; then | |
if ! command -v shasum > /dev/null 2>&1; then | |
err "need either sha256sum or shasum" | |
else | |
verbose_say "sha256sum not available. falling back to shasum" | |
fi | |
fi | |
} | |
get_architecture() { | |
verbose_say "detecting architecture" | |
local _ostype="$(uname -s)" | |
local _cputype="$(uname -m)" | |
verbose_say "uname -s reports: $_ostype" | |
verbose_say "uname -m reports: $_cputype" | |
if [ "$_ostype" = Darwin -a "$_cputype" = i386 ]; then | |
# Darwin `uname -s` lies | |
if sysctl hw.optional.x86_64 | grep -q ': 1'; then | |
local _cputype=x86_64 | |
fi | |
fi | |
case "$_ostype" in | |
Linux) | |
local _ostype=unknown-linux-gnu | |
;; | |
FreeBSD) | |
local _ostype=unknown-freebsd | |
;; | |
DragonFly) | |
local _ostype=unknown-dragonfly | |
;; | |
Darwin) | |
local _ostype=apple-darwin | |
;; | |
MINGW* | MSYS* | CYGWIN*) | |
local _ostype=pc-windows-gnu | |
;; | |
*) | |
err "unrecognized OS type: $_ostype" | |
;; | |
esac | |
case "$_cputype" in | |
i386 | i486 | i686 | i786 | x86) | |
local _cputype=i686 | |
;; | |
xscale | arm) | |
local _cputype=arm | |
;; | |
armv6l) | |
local _cputype=arm | |
local _ostype="${_ostype}eabihf" | |
;; | |
armv7l) | |
local _cputype=armv7 | |
local _ostype="${_ostype}eabihf" | |
;; | |
x86_64 | x86-64 | x64 | amd64) | |
local _cputype=x86_64 | |
;; | |
*) | |
err "unknown CPU type: $_cputype" | |
esac | |
# Detect 64-bit linux with 32-bit userland | |
if [ $_ostype = unknown-linux-gnu -a $_cputype = x86_64 ]; then | |
# $SHELL does not exist in standard 'sh', so probably only exists | |
# if configure is running in an interactive bash shell. /usr/bin/env | |
# exists *everywhere*. | |
local _bin_to_probe="${SHELL-bogus_shell}" | |
if [ ! -e "$_bin_to_probe" -a -e "/usr/bin/env" ]; then | |
_bin_to_probe="/usr/bin/env" | |
fi | |
# $SHELL may be not a binary | |
if [ -e "$_bin_to_probe" ]; then | |
file -L "$_bin_to_probe" | grep -q "text" | |
if [ $? = 0 ]; then | |
_bin_to_probe="/usr/bin/env" | |
fi | |
fi | |
if [ -e "$_bin_to_probe" ]; then | |
file -L "$_bin_to_probe" | grep -q "x86[_-]64" | |
if [ $? != 0 ]; then | |
local _cputype=i686 | |
fi | |
fi | |
fi | |
local _arch="$_cputype-$_ostype" | |
verbose_say "architecture is $_arch" | |
RETVAL="$_arch" | |
} | |
check_sig() { | |
local _sig_file="$1" | |
local _quiet="$2" | |
if ! command -v "$gpg_exe" > /dev/null 2>&1; then | |
return | |
fi | |
make_temp_dir | |
local _workdir="$RETVAL" | |
assert_nz "$_workdir" "workdir" | |
verbose_say "sig work dir: $_workdir" | |
echo "$gpg_key" > "$_workdir/key.asc" | |
need_ok "failed to serialize gpg key" | |
# Convert the armored key to .gpg format so it works with --keyring | |
verbose_say "converting armored key to gpg" | |
run "$gpg_exe" --no-permission-warning --dearmor "$_workdir/key.asc" | |
if [ $? != 0 ]; then | |
ignore rm -R "$_workdir" | |
return 1 | |
fi | |
verbose_say "verifying signature '$_sig_file'" | |
local _output="$("$gpg_exe" --no-permission-warning --keyring "$_workdir/key.asc.gpg" --verify "$_sig_file" 2>&1)" | |
if [ $? != 0 ]; then | |
ignore echo "$_output" | |
say_err "signature verification failed" | |
ignore rm -R "$_workdir" | |
return 1 | |
fi | |
if [ "$_quiet" = false -o "$flag_verbose" = true ]; then | |
ensure echo "$_output" | |
fi | |
run rm -R "$_workdir" || return 1 | |
} | |
# Downloads a remote file, its checksum, and signature and verifies them. | |
# Returns 0 on success. Returns the path to the downloaded file in RETVAL, | |
# and the path to it's directory in the cache in RETVAL_CACHE. | |
# | |
# The caller can decide to remove it from the cache by deleting RETVAL_CACHE. | |
# | |
# A return code of *20* indicates a successful short circuit from the | |
# update hash. | |
download_and_check() { | |
local _remote_name="$1" | |
local _quiet="$2" | |
local _update_hash_file="$3" | |
local _remote_basename="$(basename "$_remote_name")" | |
make_temp_dir | |
local _workdir="$RETVAL" | |
assert_nz "$_workdir" "workdir" | |
verbose_say "download work dir: $_workdir" | |
download_checksum_for "$_remote_name" "$_workdir/$_remote_basename" | |
if [ $? != 0 ]; then | |
ignore rm -R "$_workdir" | |
return 1 | |
fi | |
# This is the unique name of the cache, based on the content hash | |
local _cache_name="$(create_sum "$_workdir/$_remote_basename.sha256" | head -c 20)" | |
need_ok "failed to name cache name from checksum" | |
assert_nz "$_cache_name" "cache_name" | |
# If the user already has this rev then don't redownload it | |
if [ -n "$_update_hash_file" ]; then | |
# NB: May fail if file does not exist | |
local _update_hash="$(cat "$_update_hash_file" 2> /dev/null)" | |
verbose_say "provided update hash: $_update_hash" | |
verbose_say "new update hash: $_cache_name" | |
if [ "$_cache_name" = "$_update_hash" ]; then | |
run rm -R "$_workdir" || return 1 | |
# NB: Return code 20 is successful here! | |
return 20 | |
fi | |
fi | |
# Create a cache directory under dl_dir for this download, based off the content hash | |
local _cache_dir="$dl_dir/$_cache_name" | |
verbose_say "cache dir: $_cache_dir" | |
run mkdir -p "$_cache_dir" | |
if [ $? != 0 ]; then | |
say_err "failed to create download directory" | |
ignore rm -R "$_workdir" | |
return 1 | |
fi | |
# Move the checksum into the cache. -f because the file may | |
# already exist from previous download. | |
verbose_say "moving '$_workdir/$_remote_basename.sha256' to '$_cache_dir/$_remote_basename.sha256'" | |
run mv -f "$_workdir/$_remote_basename.sha256" "$_cache_dir/$_remote_basename.sha256" | |
if [ $? != 0 ]; then | |
say_err "failed to move checksum into download cache" | |
ignore rm -R "$_workdir" | |
ignore rm -R "$_cache_dir" | |
return 1 | |
fi | |
# Done with the workdir | |
run rm -R "$_workdir" | |
if [ $? != 0 ]; then | |
say_err "couldn't delete workdir '$_workdir'" | |
ignore rm -R "$_cache_dir" | |
return 1 | |
fi | |
download_file_and_sig "$_remote_name" "$_cache_dir" "$_quiet" | |
if [ $? != 0 ]; then | |
# Leave the cache dir to resume the download later | |
return 1 | |
fi | |
check_file_and_sig "$_cache_dir/$_remote_basename" "$_quiet" | |
if [ $? != 0 ]; then | |
# Whatever's in the cache doesn't add up. Delete it. | |
ignore rm -R "$_cache_dir" | |
return 1 | |
fi | |
RETVAL="$_cache_dir/$_remote_basename" | |
RETVAL_CACHE="$_cache_dir" | |
RETVAL_UPDATE_HASH="$_cache_name" | |
} | |
download_checksum_for() { | |
local _remote_name="$1" | |
local _local_name="$2" | |
local _remote_sums="$_remote_name.sha256" | |
local _local_sums="$_local_name.sha256" | |
local _remote_basename="$(basename "$_remote_name")" | |
local _remote_sums_basename="$_remote_basename.sha256" | |
assert_nz "$_remote_basename" "remote basename" | |
make_temp_dir | |
local _workdir="$RETVAL" | |
assert_nz "$_workdir" "workdir" | |
verbose_say "download work dir: $_workdir" | |
verbose_say "downloading '$_remote_sums' to '$_workdir'" | |
(run cd "$_workdir" && run curl -s -f -O "$_remote_sums") | |
if [ $? != 0 ]; then | |
say_err "couldn't download checksum file '$_remote_sums'" | |
ignore rm -R "$_workdir" | |
return 1 | |
fi | |
verbose_say "moving '$_workdir/$_remote_sums_basename' to '$_local_sums'" | |
run mv -f "$_workdir/$_remote_sums_basename" "$_local_sums" | |
if [ $? != 0 ]; then | |
say_err "couldn't move '$_workdir/$_remote_sums_basename' to '$_local_sums'" | |
ignore rm -R "$_workdir" | |
return 1 | |
fi | |
run rm -R "$_workdir" | |
if [ $? != 0 ]; then | |
say_err "couldn't delete workdir '$_workdir'" | |
return 1 | |
fi | |
} | |
download_file_and_sig() { | |
local _remote_name="$1" | |
local _local_dirname="$2" | |
local _quiet="$3" | |
local _remote_basename="$(basename "$_remote_name")" | |
assert_nz "$_remote_basename" "remote basename" | |
local _local_name="$_local_dirname/$_remote_basename" | |
local _remote_sig="$_remote_name.asc" | |
local _local_sig="$_local_name.asc" | |
# curl -C does not seem to work when the file already exists at 100%, | |
# so just delete it and redownload. | |
if [ -e "$_local_sig" ]; then | |
run rm "$_local_sig" | |
if [ $? != 0 ]; then | |
say_err "failed to delete existing local signature for '$_remote_name'" | |
return 1 | |
fi | |
fi | |
verbose_say "downloading '$_remote_sig' to '$_local_sig'" | |
(run cd "$_local_dirname" && run curl -s -C - -f -O "$_remote_sig") | |
if [ $? != 0 ]; then | |
say_err "couldn't download signature file '$_remote_sig'" | |
return 1 | |
fi | |
# Again, because curl -C doesn't like a complete file, short circuit | |
# curl by checking the sum. | |
local _local_sums_file="$_local_dirname/$_remote_basename.sha256" | |
# Throwing away error text since this error is expected. | |
check_sums "$_local_sums_file" > /dev/null 2>&1 | |
if [ $? = 0 ]; then | |
verbose_say "checksum good. download already complete" | |
return 0 | |
fi | |
verbose_say "downloading '$_remote_name' to '$_local_name'" | |
# Invoke curl in a way that will resume if necessary | |
if [ "$_quiet" = false ]; then | |
(run cd "$_local_dirname" && run curl -# -C - -f -O "$_remote_name") | |
else | |
(run cd "$_local_dirname" && run curl -s -C - -f -O "$_remote_name") | |
fi | |
if [ $? != 0 ]; then | |
say_err "couldn't download '$_remote_name'" | |
return 1 | |
fi | |
} | |
check_file_and_sig() { | |
local _local_name="$1" | |
local _quiet="$2" | |
local _local_sums="$_local_name.sha256" | |
local _local_sig="$_local_name.asc" | |
verbose_say "verifying checksums for '$_local_name'" | |
check_sums "$_local_sums" | |
if [ $? != 0 ]; then | |
say_err "checksum failed for '$_local_name'" | |
return 1 | |
fi | |
check_sig "$_local_sig" "$_quiet" | |
if [ $? != 0 ]; then | |
say_err "signature failed for '$_local_name'" | |
return 1 | |
fi | |
} | |
# Verifies that the terminal can be opened or exits | |
check_tty() { | |
# FIXME: This isn't sufficient since it just checks that tty | |
# exists, not that it can be read | |
if [ ! -e /dev/tty ]; then | |
err "running in interactive mode (without -y), but cannot open /dev/tty" | |
fi | |
} | |
# Waits for a y/n response and exits if n | |
get_tty_confirmation() { | |
local _yn="" | |
read -p "Continue? (y/N) " _yn < /dev/tty | |
need_ok "failed to read from /dev/tty" | |
echo | |
if [ "$_yn" != "y" -a "$_yn" != "Y" -a "$_yn" != "yes" ]; then | |
say "cancelling" | |
exit 0 | |
fi | |
} | |
maybe_sudo() { | |
local _disable_sudo="$1" | |
shift | |
get_architecture || return 1 | |
local _arch="$RETVAL" | |
assert_nz "$_arch" "arch" | |
local _is_windows=false | |
case "$_arch" in | |
*windows*) | |
_is_windows=true | |
;; | |
esac | |
if [ "$_disable_sudo" = false -a "$_is_windows" = false ]; then | |
run sudo "$@" | |
else | |
run "$@" | |
fi | |
} | |
# Help | |
print_help() { | |
echo ' | |
Usage: rustup.sh [--verbose] | |
Options: | |
--channel=(stable|beta|nightly) Install from channel (default stable) | |
--date=<YYYY-MM-DD> Install from archives | |
--revision=<version-number> Install a specific release | |
--spec=<toolchain-spec> Install from toolchain spec | |
--prefix=<path> Install to a specific location (default /usr/local) | |
--uninstall Uninstall instead of install | |
--with-target=<triple> Also install the standard library for the given target | |
--add-target=<triple> Updates an existing installation with a new target | |
--list-available-targets Lists targets available to an existing installation | |
--disable-ldconfig Do not run ldconfig on Linux | |
--disable-sudo Do not run installer under sudo | |
--save Save downloads for future reuse | |
--yes, -y Disable the interactive mode | |
--help, -h Display usage information | |
' | |
} | |
# Standard utilities | |
say() { | |
echo "rustup: $1" | |
} | |
say_err() { | |
say "$1" >&2 | |
} | |
verbose_say() { | |
if [ "$flag_verbose" = true ]; then | |
say "$1" | |
fi | |
} | |
err() { | |
say "$1" >&2 | |
exit 1 | |
} | |
need_cmd() { | |
if ! command -v "$1" > /dev/null 2>&1 | |
then err "need '$1' (command not found)" | |
fi | |
} | |
need_ok() { | |
if [ $? != 0 ]; then err "$1"; fi | |
} | |
assert_nz() { | |
if [ -z "$1" ]; then err "assert_nz $2"; fi | |
} | |
# Run a command that should never fail. If the command fails execution | |
# will immediately terminate with an error showing the failing | |
# command. | |
ensure() { | |
"$@" | |
need_ok "command failed: $*" | |
} | |
# This is just for indicating that commands' results are being | |
# intentionally ignored. Usually, because it's being executed | |
# as part of error handling. | |
ignore() { | |
run "$@" | |
} | |
# Runs a command and prints it to stderr if it fails. | |
run() { | |
"$@" | |
local _retval=$? | |
if [ $_retval != 0 ]; then | |
say_err "command failed: $*" | |
fi | |
return $_retval | |
} | |
# Prints the absolute path of a directory to stdout | |
abs_path() { | |
local _path="$1" | |
# Unset CDPATH because it causes havok: it makes the destination unpredictable | |
# and triggers 'cd' to print the path to stdout. Route `cd`'s output to /dev/null | |
# for good measure. | |
(unset CDPATH && cd "$_path" > /dev/null && pwd) | |
} | |
assert_cmds() { | |
need_cmd dirname | |
need_cmd basename | |
need_cmd mkdir | |
need_cmd cat | |
need_cmd curl | |
need_cmd mktemp | |
need_cmd rm | |
need_cmd egrep | |
need_cmd grep | |
need_cmd file | |
need_cmd uname | |
need_cmd tar | |
need_cmd sed | |
need_cmd sh | |
need_cmd mv | |
need_cmd awk | |
need_cmd cut | |
need_cmd sort | |
need_cmd date | |
need_cmd head | |
need_cmd printf | |
need_cmd touch | |
need_cmd id | |
} | |
main "$@" | |
# vim: set noet ts=8 sts=4 sw=4: |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment