Permalink
Cannot retrieve contributors at this time
Fetching contributors…
| #!/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: |