Skip to content

Instantly share code, notes, and snippets.

@e-oz
Created December 25, 2016 11:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save e-oz/ff27a1d0e65bf21fb1c7943c068deea1 to your computer and use it in GitHub Desktop.
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
#!/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
mQINBFJEwMkBEADlPACa2K7reD4x5zd8afKx75QYKmxqZwywRbgeICeD4bKiQoJZ
dUjmn1LgrGaXuBMKXJQhyA34e/1YZel/8et+HPE5XpljBfNYXWbVocE1UMUTnFU9
CKXa4AhJ33f7we2/QmNRMUifw5adPwGMg4D8cDKXk02NdnqQlmFByv0vSaArR5kn
gZKnLY6o0zZ9Buyy761Im/ShXqv4ATUgYiFc48z33G4j+BDmn0ryGr1aFdP58tHp
gjWtLZs0iWeFNRDYDje6ODyu/MjOyuAWb2pYDH47Xu7XedMZzenH2TLM9yt/hyOV
xReDPhvoGkaO8xqHioJMoPQi1gBjuBeewmFyTSPS4deASukhCFOcTsw/enzJagiS
ZAq6Imehduke+peAL1z4PuRmzDPO2LPhVS7CDXtuKAYqUV2YakTq8MZUempVhw5n
LqVaJ5/XiyOcv405PnkT25eIVVVghxAgyz6bOU/UMjGQYlkUxI7YZ9tdreLlFyPR
OUL30E8q/aCd4PGJV24yJ1uit+yS8xjyUiMKm4J7oMP2XdBN98TUfLGw7SKeAxyU
92BHlxg7yyPfI4TglsCzoSgEIV6xoGOVRRCYlGzSjUfz0bCMCclhTQRBkegKcjB3
sMTyG3SPZbjTlCqrFHy13e6hGl37Nhs8/MvXUysq2cluEISn5bivTKEeeQARAQAB
tERSdXN0IExhbmd1YWdlIChUYWcgYW5kIFJlbGVhc2UgU2lnbmluZyBLZXkpIDxy
dXN0LWtleUBydXN0LWxhbmcub3JnPokCOAQTAQIAIgUCUkTAyQIbAwYLCQgHAwIG
FQgCCQoLBBYCAwECHgECF4AACgkQhauW5vob5f5fYQ//b1DWK1NSGx5nZ3zYZeHJ
9mwGCftIaA2IRghAGrNf4Y8DaPqR+w1OdIegWn8kCoGfPfGAVW5XXJg+Oxk6QIaD
2hJojBUrq1DALeCZVewzTVw6BN4DGuUexsc53a8DcY2Yk5WE3ll6UKq/YPiWiPNX
9r8FE2MJwMABB6mWZLqJeg4RCrriBiCG26NZxGE7RTtPHyppoVxWKAFDiWyNdJ+3
UnjldWrT9xFqjqfXWw9Bhz8/EoaGeSSbMIAQDkQQpp1SWpljpgqvctZlc5fHhsG6
lmzW5RM4NG8OKvq3UrBihvgzwrIfoEDKpXbk3DXqaSs1o81NH5ftVWWbJp/ywM9Q
uMC6n0YWiMZMQ1cFBy7tukpMkd+VPbPkiSwBhPkfZIzUAWd74nanN5SKBtcnymgJ
+OJcxfZLiUkXRj0aUT1GLA9/7wnikhJI+RvwRfHBgrssXBKNPOfXGWajtIAmZc2t
kR1E8zjBVLId7r5M8g52HKk+J+y5fVgJY91nxG0zf782JjtYuz9+knQd55JLFJCO
hhbv3uRvhvkqgauHagR5X9vCMtcvqDseK7LXrRaOdOUDrK/Zg/abi5d+NIyZfEt/
ObFsv3idAIe/zpU6xa1nYNe3+Ixlb6mlZm3WCWGxWe+GvNW/kq36jZ/v/8pYMyVO
p/kJqnf9y4dbufuYBg+RLqC5Ag0EUkTAyQEQANxy2tTSeRspfrpBk9+ju+KZ3zc4
umaIsEa5DxJ2zIKHywVAR67Um0K1YRG07/F5+tD9TIRkdx2pcmpjmSQzqdk3zqa9
2Zzeijjz2RNyBY8qYmyE08IncjTsFFB8OnvdXcsAgjCFmI1BKnePxrABL/2k8X18
aysPb0beWqQVsi5FsSpAHu6k1kaLKc+130x6Hf/YJAjeo+S7HeU5NeOz3zD+h5bA
Q25qMiVHX3FwH7rFKZtFFog9Ogjzi0TkDKKxoeFKyADfIdteJWFjOlCI9KoIhfXq
Et9JMnxApGqsJElJtfQjIdhMN4Lnep2WkudHAfwJ/412fe7wiW0rcBMvr/BlBGRY
vM4sTgN058EwIuY9Qmc8RK4gbBf6GsfGNJjWozJ5XmXElmkQCAvbQFoAfi5TGfVb
77QQrhrQlSpfIYrvfpvjYoqj618SbU6uBhzh758gLllmMB8LOhxWtq9eyn1rMWyR
KL1fEkfvvMc78zP+Px6yDMa6UIez8jZXQ87Zou9EriLbzF4QfIYAqR9LUSMnLk6K
o61tSFmFEDobC3tc1jkSg4zZe/wxskn96KOlmnxgMGO0vJ7ASrynoxEnQE8k3WwA
+/YJDwboIR7zDwTy3Jw3mn1FgnH+c7Rb9h9geOzxKYINBFz5Hd0MKx7kZ1U6WobW
KiYYxcCmoEeguSPHABEBAAGJAh8EGAECAAkFAlJEwMkCGwwACgkQhauW5vob5f7f
FA//Ra+itJF4NsEyyhx4xYDOPq4uj0VWVjLdabDvFjQtbBLwIyh2bm8uO3AY4r/r
rM5WWQ8oIXQ2vvXpAQO9g8iNlFez6OLzbfdSG80AG74pQqVVVyCQxD7FanB/KGge
tAoOstFxaCAg4nxFlarMctFqOOXCFkylWl504JVIOvgbbbyj6I7qCUmbmqazBSMU
K8c/Nz+FNu2Uf/lYWOeGogRSBgS0CVBcbmPUpnDHLxZWNXDWQOCxbhA1Uf58hcyu
036kkiWHh2OGgJqlo2WIraPXx1cGw1Ey+U6exbtrZfE5kM9pZzRG7ZY83CXpYWMp
kyVXNWmf9JcIWWBrXvJmMi0FDvtgg3Pt1tnoxqdilk6yhieFc8LqBn6CZgFUBk0t
NSaWk3PsN0N6Ut8VXY6sai7MJ0Gih1gE1xadWj2zfZ9sLGyt2jZ6wK++U881YeXA
ryaGKJ8sIs182hwQb4qN7eiUHzLtIh8oVBHo8Q4BJSat88E5/gOD6IQIpxc42iRL
T+oNZw1hdwNyPOT1GMkkn86l3o7klwmQUWCPm6vl1aHp3omo+GHC63PpNFO5RncJ
Ilo3aBKKmoE5lDSMGE8KFso5awTo9z9QnVPkRsk6qeBYit9xE3x3S+iwjcSg0nie
aAkc0N00nc9V9jfPvt4z/5A5vjHh+NhFwH5h2vBJVPdsz6m5Ag0EVI9keAEQAL3R
oVsHncJTmjHfBOV4JJsvCum4DuJDZ/rDdxauGcjMUWZaG338ZehnDqG1Yn/ys7zE
aKYUmqyT+XP+M2IAQRTyxwlU1RsDlemQfWrESfZQCCmbnFScL0E7cBzy4xvtInQe
UaFgJZ1BmxbzQrx+eBBdOTDv7RLnNVygRmMzmkDhxO1IGEu1+3ETIg/DxFE7VQY0
It/Ywz+nHu1o4Hemc/GdKxu9hcYvcRVc/Xhueq/zcIM96l0m+CFbs0HMKCj8dgMe
Ng6pbbDjNM+cV+5BgpRdIpE2l9W7ImpbLihqcZt47J6oWt/RDRVoKOzRxjhULVyV
2VP9ESr48HnbvxcpvUAEDCQUhsGpur4EKHFJ9AmQ4zf91gWLrDc6QmlACn9o9ARU
fOV5aFsZI9ni1MJEInJTP37stz/uDECRie4LTL4O6P4Dkto8ROM2wzZq5CiRNfnT
PP7ARfxlCkpg+gpLYRlxGUvRn6EeYwDtiMQJUQPfpGHSvThUlgDEsDrpp4SQSmdA
CB+rvaRqCawWKoXs0In/9wylGorRUupeqGC0I0/rh+f5mayFvORzwy/4KK4QIEV9
aYTXTvSRl35MevfXU1Cumlaqle6SDkLr3ZnFQgJBqap0Y+Nmmz2HfO/pohsbtHPX
92SN3dKqaoSBvzNGY5WT3CsqxDtik37kR3f9/DHpABEBAAGJBD4EGAECAAkFAlSP
ZHgCGwICKQkQhauW5vob5f7BXSAEGQECAAYFAlSPZHgACgkQXLSpNHs7CdwemA/+
KFoGuFqU0uKT9qblN4ugRyil5itmTRVffl4tm5OoWkW8uDnu7Ue3vzdzy+9NV8X2
wRG835qjXijWP++AGuxgW6LB9nV5OWiKMCHOWnUjJQ6pNQMAgSN69QzkFXVF/q5f
bkma9TgSbwjrVMyPzLSRwq7HsT3V02Qfr4cyq39QeILGy/NHW5z6LZnBy3BaVSd0
lGjCEc3yfH5OaB79na4W86WCV5n4IT7cojFM+LdL6P46RgmEtWSG3/CDjnJl6BLR
WqatRNBWLIMKMpn+YvOOL9TwuP1xbqWr1vZ66wksm53NIDcWhptpp0KEuzbU0/Dt
OltBhcX8tOmO36LrSadX9rwckSETCVYklmpAHNxPml011YNDThtBidvsicw1vZwR
HsXn+txlL6RAIRN+J/Rw3uOiJAqN9Qgedpx2q+E15t8MiTg/FXtB9SysnskFT/BH
z0USNKJUY0btZBw3eXWzUnZf59D8VW1M/9JwznCHAx0c9wy/gRDiwt9w4RoXryJD
VAwZg8rwByjldoiThUJhkCYvJ0R3xH3kPnPlGXDW49E9R8C2umRC3cYOL4U9dOQ1
5hSlYydF5urFGCLIvodtE9q80uhpyt8L/5jj9tbwZWv6JLnfBquZSnCGqFZRfXlb
Jphk9+CBQWwiZSRLZRzqQ4ffl4xyLuolx01PMaatkQbRaw/+JpgRNlurKQ0PsTrO
8tztO/tpBBj/huc2DGkSwEWvkfWElS5RLDKdoMVs/j5CLYUJzZVikUJRm7m7b+OA
P3W1nbDhuID+XV1CSBmGifQwpoPTys21stTIGLgznJrIfE5moFviOLqD/LrcYlsq
CQg0yleu7SjOs//8dM3mC2FyLaE/dCZ8l2DCLhHw0+ynyRAvSK6aGCmZz6jMjmYF
MXgiy7zESksMnVFMulIJJhR3eB0wx2GitibjY/ZhQ7tD3i0yy9ILR07dFz4pgkVM
afxpVR7fmrMZ0t+yENd+9qzyAZs0ksxORoc2ze90SCx2jwEX/3K+m4I0hP2H/w5W
gqdvuRLiqf+4BGW4zqWkLLlNIe/okt0r82SwHtDN0Ui1asmZTGj6sm8SXtwx+5cE
38MttWqjDiibQOSthRVcETByRYM8KcjYSUCi4PoBc3NpDONkFbZm6XofR/f5mTcl
2jDw6fIeVc4Hd1jBGajNzEqtneqqbdAkPQaLsuD2TMkQfTDJfE/IljwjrhDa9Mi+
odtnMWq8vlwOZZ24/8/BNK5qXuCYL67O7AJB4ZQ6BT+g4z96iRLbupzu/XJyXkQF
rOY/Ghegvn7fDrnt2KC9MpgeFBXzUp+k5rzUdF8jbCx5apVjA1sWXB9Kh3L+DUwF
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