2 # shellcheck shell=dash
4 # This is just a little script that can be downloaded from the internet to
5 # install rustup. It just does platform detection, downloads the installer
8 # It runs on Unix shells like {a,ba,da,k,z}sh. It uses the common `local`
9 # extension. Note: Most shells limit `local` to 1 var per line, contra bash.
11 if [ "$KSH_VERSION" = 'Version JM 93t+ 2010-03-05' ]; then
12 # The version of ksh93 that ships with many illumos systems does not
13 # support the "local" extension. Print a message rather than fail in
14 # subtle ways later on:
15 echo 'rustup does not work with this ksh93 version; please try bash!' >&2
22 # If RUSTUP_UPDATE_ROOT is unset or empty, default it.
23 RUSTUP_UPDATE_ROOT="${RUSTUP_UPDATE_ROOT:-https://static.rust-lang.org/rustup}"
25 #XXX: If you change anything here, please make the same changes in setup_mode.rs
28 rustup-init 1.25.2 (8c4dad73d 2023-02-01)
29 The installer for rustup
32 rustup-init [FLAGS] [OPTIONS]
35 -v, --verbose Enable verbose output
36 -q, --quiet Disable progress output
37 -y Disable confirmation prompt.
38 --no-modify-path Don't configure the PATH environment variable
39 -h, --help Prints help information
40 -V, --version Prints version information
43 --default-host <default-host> Choose a default host triple
44 --default-toolchain <default-toolchain> Choose a default toolchain to install
45 --default-toolchain none Do not install any toolchains
46 --profile [minimal|default|complete] Choose a profile
47 -c, --component <components>... Component name to also install
48 -t, --target <targets>... Target name to also install
61 get_architecture || return 1
63 assert_nz "$_arch" "arch"
72 local _url="${RUSTUP_UPDATE_ROOT}/dist/${_arch}/rustup-init${_ext}"
75 _dir="$(ensure mktemp -d)"
76 local _file="${_dir}/rustup-init${_ext}"
78 local _ansi_escapes_are_valid=false
80 if [ "${TERM+set}" = 'set' ]; then
82 xterm*|rxvt*|urxvt*|linux*|vt*)
83 _ansi_escapes_are_valid=true
89 # check if we have to use /dev/tty to prompt the user
99 if [ "${arg%%--*}" = "" ]; then
100 # Long option (other than --help);
101 # don't attempt to interpret it.
104 while getopts :hy sub_arg "$arg"; do
111 # user wants to skip the prompt --
112 # we don't need /dev/tty
123 if $_ansi_escapes_are_valid; then
124 printf "\33[1minfo:\33[0m downloading installer\n" 1>&2
126 printf '%s\n' 'info: downloading installer' 1>&2
129 ensure mkdir -p "$_dir"
130 ensure downloader "$_url" "$_file" "$_arch"
131 ensure chmod u+x "$_file"
132 if [ ! -x "$_file" ]; then
133 printf '%s\n' "Cannot execute $_file (likely because of mounting /tmp as noexec)." 1>&2
134 printf '%s\n' "Please copy the file to a location where you can execute binaries and run ./rustup-init${_ext}." 1>&2
138 if [ "$need_tty" = "yes" ] && [ ! -t 0 ]; then
139 # The installer is going to want to ask for confirmation by
140 # reading stdin. This script was piped into `sh` though and
141 # doesn't have stdin to pass to its children. Instead we're going
142 # to explicitly connect /dev/tty to the installer's stdin.
144 err "Unable to run interactively. Run with -y to accept defaults, --help for additional options"
147 ignore "$_file" "$@" < /dev/tty
161 # Check for /proc by looking for the /proc/self/exe link
162 # This is only run on Linux
163 if ! test -L /proc/self/exe ; then
164 err "fatal: Unable to find /proc/self/exe. Is /proc mounted? Installation cannot proceed without /proc."
170 # Architecture detection without dependencies beyond coreutils.
171 # ELF files start out "\x7fELF", and the following byte is
172 # 0x01 for 32-bit and
174 # The printf builtin on some shells like dash only supports octal
175 # escape sequences, so we use those.
176 local _current_exe_head
177 _current_exe_head=$(head -c 5 /proc/self/exe )
178 if [ "$_current_exe_head" = "$(printf '\177ELF\001')" ]; then
180 elif [ "$_current_exe_head" = "$(printf '\177ELF\002')" ]; then
183 err "unknown platform bitness"
187 is_host_amd64_elf() {
190 # ELF e_machine detection without dependencies beyond coreutils.
191 # Two-byte field at offset 0x12 indicates the CPU,
192 # but we're interested in it being 0x3E to indicate amd64, or not that.
193 local _current_exe_machine
194 _current_exe_machine=$(head -c 19 /proc/self/exe | tail -c 1)
195 [ "$_current_exe_machine" = "$(printf '\076')" ]
203 # detect endianness without od/hexdump, like get_bitness() does.
207 local _current_exe_endianness
208 _current_exe_endianness="$(head -c 6 /proc/self/exe | tail -c 1)"
209 if [ "$_current_exe_endianness" = "$(printf '\001')" ]; then
210 echo "${cputype}${suffix_el}"
211 elif [ "$_current_exe_endianness" = "$(printf '\002')" ]; then
212 echo "${cputype}${suffix_eb}"
214 err "unknown platform endianness"
219 local _ostype _cputype _bitness _arch _clibtype
220 _ostype="$(uname -s)"
221 _cputype="$(uname -m)"
224 if [ "$_ostype" = Linux ]; then
225 if [ "$(uname -o)" = Android ]; then
228 if ldd --version 2>&1 | grep -q 'musl'; then
233 if [ "$_ostype" = Darwin ] && [ "$_cputype" = i386 ]; then
234 # Darwin `uname -m` lies
235 if sysctl hw.optional.x86_64 | grep -q ': 1'; then
240 if [ "$_ostype" = SunOS ]; then
241 # Both Solaris and illumos presently announce as "SunOS" in "uname -s"
242 # so use "uname -o" to disambiguate. We use the full path to the
243 # system uname in case the user has coreutils uname first in PATH,
244 # which has historically sometimes printed the wrong value here.
245 if [ "$(/usr/bin/uname -o)" = illumos ]; then
249 # illumos systems have multi-arch userlands, and "uname -m" reports the
250 # machine hardware name; e.g., "i86pc" on both 32- and 64-bit x86
251 # systems. Check for the native (widest) instruction set on the
253 if [ "$_cputype" = i86pc ]; then
254 _cputype="$(isainfo -n)"
261 _ostype=linux-android
266 _ostype=unknown-linux-$_clibtype
267 _bitness=$(get_bitness)
271 _ostype=unknown-freebsd
275 _ostype=unknown-netbsd
279 _ostype=unknown-dragonfly
287 _ostype=unknown-illumos
290 MINGW* | MSYS* | CYGWIN* | Windows_NT)
291 _ostype=pc-windows-gnu
295 err "unrecognized OS type: $_ostype"
302 i386 | i486 | i686 | i786 | x86)
308 if [ "$_ostype" = "linux-android" ]; then
309 _ostype=linux-androideabi
315 if [ "$_ostype" = "linux-android" ]; then
316 _ostype=linux-androideabi
318 _ostype="${_ostype}eabihf"
324 if [ "$_ostype" = "linux-android" ]; then
325 _ostype=linux-androideabi
327 _ostype="${_ostype}eabihf"
335 x86_64 | x86-64 | x64 | amd64)
340 _cputype=$(get_endianness mips '' el)
344 if [ "$_bitness" -eq 64 ]; then
345 # only n64 ABI is supported for now
346 _ostype="${_ostype}abi64"
347 _cputype=$(get_endianness mips64 '' el)
370 err "unknown CPU type: $_cputype"
374 # Detect 64-bit linux with 32-bit userland
375 if [ "${_ostype}" = unknown-linux-gnu ] && [ "${_bitness}" -eq 32 ]; then
378 if [ -n "${RUSTUP_CPUTYPE:-}" ]; then
379 _cputype="$RUSTUP_CPUTYPE"
381 # 32-bit executable for amd64 = x32
382 if is_host_amd64_elf; then {
383 echo "This host is running an x32 userland; as it stands, x32 support is poor," 1>&2
384 echo "and there isn't a native toolchain -- you will have to install" 1>&2
385 echo "multiarch compatibility with i686 and/or amd64, then select one" 1>&2
386 echo "by re-running this script with the RUSTUP_CPUTYPE environment variable" 1>&2
387 echo "set to i686 or x86_64, respectively." 1>&2
389 echo "You will be able to add an x32 target after installation by running" 1>&2
390 echo " rustup target add x86_64-unknown-linux-gnux32" 1>&2
398 _cputype=$(get_endianness mips '' el)
405 if [ "$_ostype" = "linux-android" ]; then
406 _ostype=linux-androideabi
408 _ostype="${_ostype}eabihf"
412 err "riscv64 with 32-bit userland unsupported"
417 # Detect armv7 but without the CPU features Rust needs in that build,
418 # and fall back to arm.
419 # See https://github.com/rust-lang/rustup.rs/issues/587.
420 if [ "$_ostype" = "unknown-linux-gnueabihf" ] && [ "$_cputype" = armv7 ]; then
421 if ensure grep '^Features' /proc/cpuinfo | grep -q -v neon; then
422 # At least one processor does not have NEON.
427 _arch="${_cputype}-${_ostype}"
433 printf 'rustup: %s\n' "$1"
442 if ! check_cmd "$1"; then
443 err "need '$1' (command not found)"
448 command -v "$1" > /dev/null 2>&1
452 if [ -z "$1" ]; then err "assert_nz $2"; fi
455 # Run a command that should never fail. If the command fails execution
456 # will immediately terminate with an error showing the failing
459 if ! "$@"; then err "command failed: $*"; fi
462 # This is just for indicating that commands' results are being
463 # intentionally ignored. Usually, because it's being executed
464 # as part of error handling.
469 # This wraps curl or wget. Try curl first, if not installed,
477 if check_cmd curl; then
479 elif check_cmd wget; then
482 _dld='curl or wget' # to be used in error message of need_cmd
485 if [ "$1" = --check ]; then
487 elif [ "$_dld" = curl ]; then
488 check_curl_for_retry_support
490 get_ciphersuites_for_curl
491 _ciphersuites="$RETVAL"
492 if [ -n "$_ciphersuites" ]; then
493 _err=$(curl $_retry --proto '=https' --tlsv1.2 --ciphers "$_ciphersuites" --silent --show-error --fail --location "$1" --output "$2" 2>&1)
496 echo "Warning: Not enforcing strong cipher suites for TLS, this is potentially less secure"
497 if ! check_help_for "$3" curl --proto --tlsv1.2; then
498 echo "Warning: Not enforcing TLS v1.2, this is potentially less secure"
499 _err=$(curl $_retry --silent --show-error --fail --location "$1" --output "$2" 2>&1)
502 _err=$(curl $_retry --proto '=https' --tlsv1.2 --silent --show-error --fail --location "$1" --output "$2" 2>&1)
506 if [ -n "$_err" ]; then
508 if echo "$_err" | grep -q 404$; then
509 err "installer for platform '$3' not found, this may be unsupported"
513 elif [ "$_dld" = wget ]; then
514 if [ "$(wget -V 2>&1|head -2|tail -1|cut -f1 -d" ")" = "BusyBox" ]; then
515 echo "Warning: using the BusyBox version of wget. Not enforcing strong cipher suites for TLS or TLS v1.2, this is potentially less secure"
516 _err=$(wget "$1" -O "$2" 2>&1)
519 get_ciphersuites_for_wget
520 _ciphersuites="$RETVAL"
521 if [ -n "$_ciphersuites" ]; then
522 _err=$(wget --https-only --secure-protocol=TLSv1_2 --ciphers "$_ciphersuites" "$1" -O "$2" 2>&1)
525 echo "Warning: Not enforcing strong cipher suites for TLS, this is potentially less secure"
526 if ! check_help_for "$3" wget --https-only --secure-protocol; then
527 echo "Warning: Not enforcing TLS v1.2, this is potentially less secure"
528 _err=$(wget "$1" -O "$2" 2>&1)
531 _err=$(wget --https-only --secure-protocol=TLSv1_2 "$1" -O "$2" 2>&1)
536 if [ -n "$_err" ]; then
538 if echo "$_err" | grep -q ' 404 Not Found$'; then
539 err "installer for platform '$3' not found, this may be unsupported"
544 err "Unknown downloader" # should not reach here
558 if "$_cmd" --help | grep -q 'For all options use the manual or "--help all".'; then
567 if check_cmd sw_vers; then
568 case $(sw_vers -productVersion) in
570 # If we're running on macOS, older than 10.13, then we always
571 # fail to find these options to force fallback
572 if [ "$(sw_vers -productVersion | cut -d. -f2)" -lt 13 ]; then
574 echo "Warning: Detected macOS platform older than 10.13"
579 # We assume Big Sur will be OK for now
582 # Unknown product version, warn and continue
583 echo "Warning: Detected unknown macOS major version: $(sw_vers -productVersion)"
584 echo "Warning TLS capabilities detection may fail"
593 if ! "$_cmd" --help "$_category" | grep -q -- "$_arg"; then
598 true # not strictly needed
601 # Check if curl supports the --retry flag, then pass it to the curl invocation.
602 check_curl_for_retry_support() {
603 local _retry_supported=""
604 # "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc.
605 if check_help_for "notspecified" "curl" "--retry"; then
606 _retry_supported="--retry 3"
609 RETVAL="$_retry_supported"
613 # Return cipher suite string specified by user, otherwise return strong TLS 1.2-1.3 cipher suites
614 # if support by local tools is detected. Detection currently supports these curl backends:
615 # GnuTLS and OpenSSL (possibly also LibreSSL and BoringSSL). Return value can be empty.
616 get_ciphersuites_for_curl() {
617 if [ -n "${RUSTUP_TLS_CIPHERSUITES-}" ]; then
618 # user specified custom cipher suites, assume they know what they're doing
619 RETVAL="$RUSTUP_TLS_CIPHERSUITES"
623 local _openssl_syntax="no"
624 local _gnutls_syntax="no"
625 local _backend_supported="yes"
626 if curl -V | grep -q ' OpenSSL/'; then
627 _openssl_syntax="yes"
628 elif curl -V | grep -iq ' LibreSSL/'; then
629 _openssl_syntax="yes"
630 elif curl -V | grep -iq ' BoringSSL/'; then
631 _openssl_syntax="yes"
632 elif curl -V | grep -iq ' GnuTLS/'; then
635 _backend_supported="no"
638 local _args_supported="no"
639 if [ "$_backend_supported" = "yes" ]; then
640 # "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc.
641 if check_help_for "notspecified" "curl" "--tlsv1.2" "--ciphers" "--proto"; then
642 _args_supported="yes"
647 if [ "$_args_supported" = "yes" ]; then
648 if [ "$_openssl_syntax" = "yes" ]; then
649 _cs=$(get_strong_ciphersuites_for "openssl")
650 elif [ "$_gnutls_syntax" = "yes" ]; then
651 _cs=$(get_strong_ciphersuites_for "gnutls")
658 # Return cipher suite string specified by user, otherwise return strong TLS 1.2-1.3 cipher suites
659 # if support by local tools is detected. Detection currently supports these wget backends:
660 # GnuTLS and OpenSSL (possibly also LibreSSL and BoringSSL). Return value can be empty.
661 get_ciphersuites_for_wget() {
662 if [ -n "${RUSTUP_TLS_CIPHERSUITES-}" ]; then
663 # user specified custom cipher suites, assume they know what they're doing
664 RETVAL="$RUSTUP_TLS_CIPHERSUITES"
669 if wget -V | grep -q '\-DHAVE_LIBSSL'; then
670 # "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc.
671 if check_help_for "notspecified" "wget" "TLSv1_2" "--ciphers" "--https-only" "--secure-protocol"; then
672 _cs=$(get_strong_ciphersuites_for "openssl")
674 elif wget -V | grep -q '\-DHAVE_LIBGNUTLS'; then
675 # "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc.
676 if check_help_for "notspecified" "wget" "TLSv1_2" "--ciphers" "--https-only" "--secure-protocol"; then
677 _cs=$(get_strong_ciphersuites_for "gnutls")
684 # Return strong TLS 1.2-1.3 cipher suites in OpenSSL or GnuTLS syntax. TLS 1.2
685 # excludes non-ECDHE and non-AEAD cipher suites. DHE is excluded due to bad
686 # DH params often found on servers (see RFC 7919). Sequence matches or is
687 # similar to Firefox 68 ESR with weak cipher suites disabled via about:config.
688 # $1 must be openssl or gnutls.
689 get_strong_ciphersuites_for() {
690 if [ "$1" = "openssl" ]; then
691 # OpenSSL is forgiving of unknown values, no problems with TLS 1.3 values on versions that don't support it yet.
692 echo "TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384"
693 elif [ "$1" = "gnutls" ]; then
694 # GnuTLS isn't forgiving of unknown values, so this may require a GnuTLS version that supports TLS 1.3 even if wget doesn't.
695 # Begin with SECURE128 (and higher) then remove/add to build cipher suites. Produces same 9 cipher suites as OpenSSL but in slightly different order.
696 echo "SECURE128:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-DTLS-ALL:-CIPHER-ALL:-MAC-ALL:-KX-ALL:+AEAD:+ECDHE-ECDSA:+ECDHE-RSA:+AES-128-GCM:+CHACHA20-POLY1305:+AES-256-GCM"