/ rustup.sh
rustup.sh
  1  #!/bin/sh
  2  # shellcheck shell=dash
  3  # shellcheck disable=SC2039  # local is non-POSIX
  4  
  5  # This is just a little script that can be downloaded from the internet to
  6  # install rustup. It just does platform detection, downloads the installer
  7  # and runs it.
  8  
  9  # It runs on Unix shells like {a,ba,da,k,z}sh. It uses the common `local`
 10  # extension. Note: Most shells limit `local` to 1 var per line, contra bash.
 11  
 12  # Some versions of ksh have no `local` keyword. Alias it to `typeset`, but
 13  # beware this makes variables global with f()-style function syntax in ksh93.
 14  # mksh has this alias by default.
 15  has_local() {
 16      # shellcheck disable=SC2034  # deliberately unused
 17      local _has_local
 18  }
 19  
 20  has_local 2>/dev/null || alias local=typeset
 21  
 22  is_zsh() {
 23      [ -n "${ZSH_VERSION-}" ]
 24  }
 25  
 26  set -u
 27  
 28  # If RUSTUP_UPDATE_ROOT is unset or empty, default it.
 29  RUSTUP_UPDATE_ROOT="${RUSTUP_UPDATE_ROOT:-https://static.rust-lang.org/rustup}"
 30  # Set quiet as a global for ease of use
 31  RUSTUP_QUIET=no
 32  
 33  # NOTICE: If you change anything here, please make the same changes in setup_mode.rs
 34  usage() {
 35      cat <<EOF
 36  rustup-init 1.28.2 (d1f31992a 2025-04-28)
 37  
 38  The installer for rustup
 39  
 40  Usage: rustup-init[EXE] [OPTIONS]
 41  
 42  Options:
 43    -v, --verbose
 44            Set log level to 'DEBUG' if 'RUSTUP_LOG' is unset
 45    -q, --quiet
 46            Disable progress output, set log level to 'WARN' if 'RUSTUP_LOG' is unset
 47    -y
 48            Disable confirmation prompt
 49        --default-host <DEFAULT_HOST>
 50            Choose a default host triple
 51        --default-toolchain <DEFAULT_TOOLCHAIN>
 52            Choose a default toolchain to install. Use 'none' to not install any toolchains at all
 53        --profile <PROFILE>
 54            [default: default] [possible values: minimal, default, complete]
 55    -c, --component <COMPONENT>
 56            Comma-separated list of component names to also install
 57    -t, --target <TARGET>
 58            Comma-separated list of target names to also install
 59        --no-update-default-toolchain
 60            Don't update any existing default toolchain after install
 61        --no-modify-path
 62            Don't configure the PATH environment variable
 63    -h, --help
 64            Print help
 65    -V, --version
 66            Print version
 67  EOF
 68  }
 69  
 70  main() {
 71      downloader --check
 72      need_cmd uname
 73      need_cmd mktemp
 74      need_cmd chmod
 75      need_cmd mkdir
 76      need_cmd rm
 77      need_cmd rmdir
 78  
 79      get_architecture || return 1
 80      local _arch="$RETVAL"
 81      assert_nz "$_arch" "arch"
 82  
 83      local _ext=""
 84      case "$_arch" in
 85          *windows*)
 86              _ext=".exe"
 87              ;;
 88      esac
 89  
 90      local _url
 91      if [ "${RUSTUP_VERSION+set}" = 'set' ]; then
 92          say "\`RUSTUP_VERSION\` has been set to \`${RUSTUP_VERSION}\`"
 93          _url="${RUSTUP_UPDATE_ROOT}/archive/${RUSTUP_VERSION}"
 94      else
 95          _url="${RUSTUP_UPDATE_ROOT}/dist"
 96      fi
 97      _url="${_url}/${_arch}/rustup-init${_ext}"
 98  
 99  
100      local _dir
101      if ! _dir="$(ensure mktemp -d)"; then
102          # Because the previous command ran in a subshell, we must manually
103          # propagate exit status.
104          exit 1
105      fi
106      local _file="${_dir}/rustup-init${_ext}"
107  
108      local _ansi_escapes_are_valid=false
109      if [ -t 2 ]; then
110          if [ "${TERM+set}" = 'set' ]; then
111              case "$TERM" in
112                  xterm*|rxvt*|urxvt*|linux*|vt*)
113                      _ansi_escapes_are_valid=true
114                  ;;
115              esac
116          fi
117      fi
118  
119      # check if we have to use /dev/tty to prompt the user
120      local need_tty=yes
121      for arg in "$@"; do
122          case "$arg" in
123              --help)
124                  usage
125                  exit 0
126                  ;;
127              --quiet)
128                  RUSTUP_QUIET=yes
129                  ;;
130              *)
131                  OPTIND=1
132                  if [ "${arg%%--*}" = "" ]; then
133                      # Long option (other than --help);
134                      # don't attempt to interpret it.
135                      continue
136                  fi
137                  while getopts :hqy sub_arg "$arg"; do
138                      case "$sub_arg" in
139                          h)
140                              usage
141                              exit 0
142                              ;;
143                          q)
144                              RUSTUP_QUIET=yes
145                              ;;
146                          y)
147                              # user wants to skip the prompt --
148                              # we don't need /dev/tty
149                              need_tty=no
150                              ;;
151                          *)
152                              ;;
153                          esac
154                  done
155                  ;;
156          esac
157      done
158  
159      say 'downloading installer'
160  
161      ensure mkdir -p "$_dir"
162      ensure downloader "$_url" "$_file" "$_arch"
163      ensure chmod u+x "$_file"
164      if [ ! -x "$_file" ]; then
165          err "Cannot execute $_file (likely because of mounting /tmp as noexec)."
166          err "Please copy the file to a location where you can execute binaries and run ./rustup-init${_ext}."
167          exit 1
168      fi
169  
170      if [ "$need_tty" = "yes" ] && [ ! -t 0 ]; then
171          # The installer is going to want to ask for confirmation by
172          # reading stdin.  This script was piped into `sh` though and
173          # doesn't have stdin to pass to its children. Instead we're going
174          # to explicitly connect /dev/tty to the installer's stdin.
175          if [ ! -t 1 ]; then
176              err "Unable to run interactively. Run with -y to accept defaults, --help for additional options"
177              exit 1;
178          fi
179  
180          ignore "$_file" "$@" < /dev/tty
181      else
182          ignore "$_file" "$@"
183      fi
184  
185      local _retval=$?
186  
187      ignore rm "$_file"
188      ignore rmdir "$_dir"
189  
190      return "$_retval"
191  }
192  
193  get_current_exe() {
194      # Returns the executable used for system architecture detection
195      # This is only run on Linux
196      local _current_exe
197      if test -L /proc/self/exe ; then
198          _current_exe=/proc/self/exe
199      else
200          warn "Unable to find /proc/self/exe. System architecture detection might be inaccurate."
201          if test -n "$SHELL" ; then
202              _current_exe=$SHELL
203          else
204              need_cmd /bin/sh
205              _current_exe=/bin/sh
206          fi
207          warn "Falling back to $_current_exe."
208      fi
209      echo "$_current_exe"
210  }
211  
212  get_bitness() {
213      need_cmd head
214      # Architecture detection without dependencies beyond coreutils.
215      # ELF files start out "\x7fELF", and the following byte is
216      #   0x01 for 32-bit and
217      #   0x02 for 64-bit.
218      # The printf builtin on some shells like dash only supports octal
219      # escape sequences, so we use those.
220      local _current_exe=$1
221      local _current_exe_head
222      _current_exe_head=$(head -c 5 "$_current_exe")
223      if [ "$_current_exe_head" = "$(printf '\177ELF\001')" ]; then
224          echo 32
225      elif [ "$_current_exe_head" = "$(printf '\177ELF\002')" ]; then
226          echo 64
227      else
228          err "unknown platform bitness"
229          exit 1;
230      fi
231  }
232  
233  is_host_amd64_elf() {
234      local _current_exe=$1
235  
236      need_cmd head
237      need_cmd tail
238      # ELF e_machine detection without dependencies beyond coreutils.
239      # Two-byte field at offset 0x12 indicates the CPU,
240      # but we're interested in it being 0x3E to indicate amd64, or not that.
241      local _current_exe_machine
242      _current_exe_machine=$(head -c 19 "$_current_exe" | tail -c 1)
243      [ "$_current_exe_machine" = "$(printf '\076')" ]
244  }
245  
246  get_endianness() {
247      local _current_exe=$1
248      local cputype=$2
249      local suffix_eb=$3
250      local suffix_el=$4
251  
252      # detect endianness without od/hexdump, like get_bitness() does.
253      need_cmd head
254      need_cmd tail
255  
256      local _current_exe_endianness
257      _current_exe_endianness="$(head -c 6 "$_current_exe" | tail -c 1)"
258      if [ "$_current_exe_endianness" = "$(printf '\001')" ]; then
259          echo "${cputype}${suffix_el}"
260      elif [ "$_current_exe_endianness" = "$(printf '\002')" ]; then
261          echo "${cputype}${suffix_eb}"
262      else
263          err "unknown platform endianness"
264          exit 1
265      fi
266  }
267  
268  # Detect the Linux/LoongArch UAPI flavor, with all errors being non-fatal.
269  # Returns 0 or 234 in case of successful detection, 1 otherwise (/tmp being
270  # noexec, or other causes).
271  check_loongarch_uapi() {
272      need_cmd base64
273  
274      local _tmp
275      if ! _tmp="$(ensure mktemp)"; then
276          return 1
277      fi
278  
279      # Minimal Linux/LoongArch UAPI detection, exiting with 0 in case of
280      # upstream ("new world") UAPI, and 234 (-EINVAL truncated) in case of
281      # old-world (as deployed on several early commercial Linux distributions
282      # for LoongArch).
283      #
284      # See https://gist.github.com/xen0n/5ee04aaa6cecc5c7794b9a0c3b65fc7f for
285      # source to this helper binary.
286      ignore base64 -d > "$_tmp" <<EOF
287  f0VMRgIBAQAAAAAAAAAAAAIAAgEBAAAAeAAgAAAAAABAAAAAAAAAAAAAAAAAAAAAQQAAAEAAOAAB
288  AAAAAAAAAAEAAAAFAAAAAAAAAAAAAAAAACAAAAAAAAAAIAAAAAAAJAAAAAAAAAAkAAAAAAAAAAAA
289  AQAAAAAABCiAAwUAFQAGABUAByCAAwsYggMAACsAC3iBAwAAKwAxen0n
290  EOF
291  
292      ignore chmod u+x "$_tmp"
293      if [ ! -x "$_tmp" ]; then
294          ignore rm "$_tmp"
295          return 1
296      fi
297  
298      "$_tmp"
299      local _retval=$?
300  
301      ignore rm "$_tmp"
302      return "$_retval"
303  }
304  
305  ensure_loongarch_uapi() {
306      check_loongarch_uapi
307      case $? in
308          0)
309              return 0
310              ;;
311          234)
312              err 'Your Linux kernel does not provide the ABI required by this Rust distribution.'
313              err 'Please check with your OS provider for how to obtain a compatible Rust package for your system.'
314              exit 1
315              ;;
316          *)
317              warn "Cannot determine current system's ABI flavor, continuing anyway."
318              warn 'Note that the official Rust distribution only works with the upstream kernel ABI.'
319              warn 'Installation will fail if your running kernel happens to be incompatible.'
320              ;;
321      esac
322  }
323  
324  get_architecture() {
325      local _ostype _cputype _bitness _arch _clibtype
326      _ostype="$(uname -s)"
327      _cputype="$(uname -m)"
328      _clibtype="gnu"
329  
330      if [ "$_ostype" = Linux ]; then
331          if [ "$(uname -o)" = Android ]; then
332              _ostype=Android
333          fi
334          if ldd --version 2>&1 | grep -q 'musl'; then
335              _clibtype="musl"
336          fi
337      fi
338  
339      if [ "$_ostype" = Darwin ]; then
340          # Darwin `uname -m` can lie due to Rosetta shenanigans. If you manage to
341          # invoke a native shell binary and then a native uname binary, you can
342          # get the real answer, but that's hard to ensure, so instead we use
343          # `sysctl` (which doesn't lie) to check for the actual architecture.
344          if [ "$_cputype" = i386 ]; then
345              # Handling i386 compatibility mode in older macOS versions (<10.15)
346              # running on x86_64-based Macs.
347              # Starting from 10.15, macOS explicitly bans all i386 binaries from running.
348              # See: <https://support.apple.com/en-us/HT208436>
349  
350              # Avoid `sysctl: unknown oid` stderr output and/or non-zero exit code.
351              if sysctl hw.optional.x86_64 2> /dev/null || true | grep -q ': 1'; then
352                  _cputype=x86_64
353              fi
354          elif [ "$_cputype" = x86_64 ]; then
355              # Handling x86-64 compatibility mode (a.k.a. Rosetta 2)
356              # in newer macOS versions (>=11) running on arm64-based Macs.
357              # Rosetta 2 is built exclusively for x86-64 and cannot run i386 binaries.
358  
359              # Avoid `sysctl: unknown oid` stderr output and/or non-zero exit code.
360              if sysctl hw.optional.arm64 2> /dev/null || true | grep -q ': 1'; then
361                  _cputype=arm64
362              fi
363          fi
364      fi
365  
366      if [ "$_ostype" = SunOS ]; then
367          # Both Solaris and illumos presently announce as "SunOS" in "uname -s"
368          # so use "uname -o" to disambiguate.  We use the full path to the
369          # system uname in case the user has coreutils uname first in PATH,
370          # which has historically sometimes printed the wrong value here.
371          if [ "$(/usr/bin/uname -o)" = illumos ]; then
372              _ostype=illumos
373          fi
374  
375          # illumos systems have multi-arch userlands, and "uname -m" reports the
376          # machine hardware name; e.g., "i86pc" on both 32- and 64-bit x86
377          # systems.  Check for the native (widest) instruction set on the
378          # running kernel:
379          if [ "$_cputype" = i86pc ]; then
380              _cputype="$(isainfo -n)"
381          fi
382      fi
383  
384      local _current_exe
385      case "$_ostype" in
386  
387          Android)
388              _ostype=linux-android
389              ;;
390  
391          Linux)
392              _current_exe=$(get_current_exe)
393              _ostype=unknown-linux-$_clibtype
394              _bitness=$(get_bitness "$_current_exe")
395              ;;
396  
397          FreeBSD)
398              _ostype=unknown-freebsd
399              ;;
400  
401          NetBSD)
402              _ostype=unknown-netbsd
403              ;;
404  
405          DragonFly)
406              _ostype=unknown-dragonfly
407              ;;
408  
409          Darwin)
410              _ostype=apple-darwin
411              ;;
412  
413          illumos)
414              _ostype=unknown-illumos
415              ;;
416  
417          MINGW* | MSYS* | CYGWIN* | Windows_NT)
418              _ostype=pc-windows-gnu
419              ;;
420  
421          *)
422              err "unrecognized OS type: $_ostype"
423              exit 1
424              ;;
425  
426      esac
427  
428      case "$_cputype" in
429  
430          i386 | i486 | i686 | i786 | x86)
431              _cputype=i686
432              ;;
433  
434          xscale | arm)
435              _cputype=arm
436              if [ "$_ostype" = "linux-android" ]; then
437                  _ostype=linux-androideabi
438              fi
439              ;;
440  
441          armv6l)
442              _cputype=arm
443              if [ "$_ostype" = "linux-android" ]; then
444                  _ostype=linux-androideabi
445              else
446                  _ostype="${_ostype}eabihf"
447              fi
448              ;;
449  
450          armv7l | armv8l)
451              _cputype=armv7
452              if [ "$_ostype" = "linux-android" ]; then
453                  _ostype=linux-androideabi
454              else
455                  _ostype="${_ostype}eabihf"
456              fi
457              ;;
458  
459          aarch64 | arm64)
460              _cputype=aarch64
461              ;;
462  
463          x86_64 | x86-64 | x64 | amd64)
464              _cputype=x86_64
465              ;;
466  
467          mips)
468              _cputype=$(get_endianness "$_current_exe" mips '' el)
469              ;;
470  
471          mips64)
472              if [ "$_bitness" -eq 64 ]; then
473                  # only n64 ABI is supported for now
474                  _ostype="${_ostype}abi64"
475                  _cputype=$(get_endianness "$_current_exe" mips64 '' el)
476              fi
477              ;;
478  
479          ppc)
480              _cputype=powerpc
481              ;;
482  
483          ppc64)
484              _cputype=powerpc64
485              ;;
486  
487          ppc64le)
488              _cputype=powerpc64le
489              ;;
490  
491          s390x)
492              _cputype=s390x
493              ;;
494          riscv64)
495              _cputype=riscv64gc
496              ;;
497          loongarch64)
498              _cputype=loongarch64
499              ensure_loongarch_uapi
500              ;;
501          *)
502              err "unknown CPU type: $_cputype"
503              exit 1
504  
505      esac
506  
507      # Detect 64-bit linux with 32-bit userland
508      if [ "${_ostype}" = unknown-linux-gnu ] && [ "${_bitness}" -eq 32 ]; then
509          case $_cputype in
510              x86_64)
511                  if [ -n "${RUSTUP_CPUTYPE:-}" ]; then
512                      _cputype="$RUSTUP_CPUTYPE"
513                  else {
514                      # 32-bit executable for amd64 = x32
515                      if is_host_amd64_elf "$_current_exe"; then {
516                          err "This host is running an x32 userland, for which no native toolchain is provided."
517                          err "You will have to install multiarch compatibility with i686 or amd64."
518                          err "To do so, set the RUSTUP_CPUTYPE environment variable set to i686 or amd64 and re-run this script."
519                          err "You will be able to add an x32 target after installation by running \`rustup target add x86_64-unknown-linux-gnux32\`."
520                           exit 1
521                      }; else
522                          _cputype=i686
523                      fi
524                  }; fi
525                  ;;
526              mips64)
527                  _cputype=$(get_endianness "$_current_exe" mips '' el)
528                  ;;
529              powerpc64)
530                  _cputype=powerpc
531                  ;;
532              aarch64)
533                  _cputype=armv7
534                  if [ "$_ostype" = "linux-android" ]; then
535                      _ostype=linux-androideabi
536                  else
537                      _ostype="${_ostype}eabihf"
538                  fi
539                  ;;
540              riscv64gc)
541                  err "riscv64 with 32-bit userland unsupported"
542                  exit 1
543                  ;;
544          esac
545      fi
546  
547      # Detect armv7 but without the CPU features Rust needs in that build,
548      # and fall back to arm.
549      # See https://github.com/rust-lang/rustup.rs/issues/587.
550      if [ "$_ostype" = "unknown-linux-gnueabihf" ] && [ "$_cputype" = armv7 ]; then
551          if ! (ensure grep '^Features' /proc/cpuinfo | grep -E -q 'neon|simd') ; then
552              # Either `/proc/cpuinfo` is malformed or unavailable, or
553              # at least one processor does not have NEON (which is asimd on armv8+).
554              _cputype=arm
555          fi
556      fi
557  
558      _arch="${_cputype}-${_ostype}"
559  
560      RETVAL="$_arch"
561  }
562  
563  __print() {
564      if $_ansi_escapes_are_valid; then
565          printf '\33[1m%s:\33[0m %s\n' "$1" "$2" >&2
566      else
567          printf '%s: %s\n' "$1" "$2" >&2
568      fi
569  }
570  
571  warn() {
572      __print 'warn' "$1" >&2
573  }
574  
575  say() {
576      if [ "$RUSTUP_QUIET" = "no" ]; then
577          __print 'info' "$1" >&2
578      fi
579  }
580  
581  # NOTE: you are required to exit yourself
582  # we don't do it here because of multiline errors
583  err() {
584      __print 'error' "$1" >&2
585  }
586  
587  need_cmd() {
588      if ! check_cmd "$1"; then
589          err "need '$1' (command not found)"
590          exit 1
591      fi
592  }
593  
594  check_cmd() {
595      command -v "$1" > /dev/null 2>&1
596  }
597  
598  assert_nz() {
599      if [ -z "$1" ]; then
600          err "assert_nz $2"
601          exit 1
602      fi
603  }
604  
605  # Run a command that should never fail. If the command fails execution
606  # will immediately terminate with an error showing the failing
607  # command.
608  ensure() {
609      if ! "$@"; then
610          err "command failed: $*"
611          exit 1
612      fi
613  }
614  
615  # This is just for indicating that commands' results are being
616  # intentionally ignored. Usually, because it's being executed
617  # as part of error handling.
618  ignore() {
619      "$@"
620  }
621  
622  # This wraps curl or wget. Try curl first, if not installed,
623  # use wget instead.
624  downloader() {
625      # zsh does not split words by default, Required for curl retry arguments below.
626      is_zsh && setopt local_options shwordsplit
627  
628      local _dld
629      local _ciphersuites
630      local _err
631      local _status
632      local _retry
633      if check_cmd curl; then
634          _dld=curl
635      elif check_cmd wget; then
636          _dld=wget
637      else
638          _dld='curl or wget' # to be used in error message of need_cmd
639      fi
640  
641      if [ "$1" = --check ]; then
642          need_cmd "$_dld"
643      elif [ "$_dld" = curl ]; then
644          check_curl_for_retry_support
645          _retry="$RETVAL"
646          get_ciphersuites_for_curl
647          _ciphersuites="$RETVAL"
648          if [ -n "$_ciphersuites" ]; then
649              # shellcheck disable=SC2086
650              _err=$(curl $_retry --proto '=https' --tlsv1.2 --ciphers "$_ciphersuites" --silent --show-error --fail --location "$1" --output "$2" 2>&1)
651              _status=$?
652          else
653              warn "Not enforcing strong cipher suites for TLS, this is potentially less secure"
654              if ! check_help_for "$3" curl --proto --tlsv1.2; then
655                  warn "Not enforcing TLS v1.2, this is potentially less secure"
656                  # shellcheck disable=SC2086
657                  _err=$(curl $_retry --silent --show-error --fail --location "$1" --output "$2" 2>&1)
658                  _status=$?
659              else
660                  # shellcheck disable=SC2086
661                  _err=$(curl $_retry --proto '=https' --tlsv1.2 --silent --show-error --fail --location "$1" --output "$2" 2>&1)
662                  _status=$?
663              fi
664          fi
665          if [ -n "$_err" ]; then
666              warn "$_err"
667              if echo "$_err" | grep -q 404$; then
668                  err "installer for platform '$3' not found, this may be unsupported"
669                  exit 1
670              fi
671          fi
672          return $_status
673      elif [ "$_dld" = wget ]; then
674          if [ "$(wget -V 2>&1|head -2|tail -1|cut -f1 -d" ")" = "BusyBox" ]; then
675              warn "using the BusyBox version of wget.  Not enforcing strong cipher suites for TLS or TLS v1.2, this is potentially less secure"
676              _err=$(wget "$1" -O "$2" 2>&1)
677              _status=$?
678          else
679              get_ciphersuites_for_wget
680              _ciphersuites="$RETVAL"
681              if [ -n "$_ciphersuites" ]; then
682                  _err=$(wget --https-only --secure-protocol=TLSv1_2 --ciphers "$_ciphersuites" "$1" -O "$2" 2>&1)
683                  _status=$?
684              else
685                  warn "Not enforcing strong cipher suites for TLS, this is potentially less secure"
686                  if ! check_help_for "$3" wget --https-only --secure-protocol; then
687                      warn "Not enforcing TLS v1.2, this is potentially less secure"
688                      _err=$(wget "$1" -O "$2" 2>&1)
689                      _status=$?
690                  else
691                      _err=$(wget --https-only --secure-protocol=TLSv1_2 "$1" -O "$2" 2>&1)
692                      _status=$?
693                  fi
694              fi
695          fi
696          if [ -n "$_err" ]; then
697              warn "$_err"
698              if echo "$_err" | grep -q ' 404 Not Found$'; then
699                  err "installer for platform '$3' not found, this may be unsupported"
700                  exit 1
701              fi
702          fi
703          return $_status
704      else
705          err "Unknown downloader"   # should not reach here
706          exit 1
707      fi
708  }
709  
710  check_help_for() {
711      local _arch
712      local _cmd
713      local _arg
714      _arch="$1"
715      shift
716      _cmd="$1"
717      shift
718  
719      local _category
720      if "$_cmd" --help | grep -q '"--help all"'; then
721        _category="all"
722      else
723        _category=""
724      fi
725  
726      case "$_arch" in
727  
728          *darwin*)
729          if check_cmd sw_vers; then
730              local _os_version
731              local _os_major
732              _os_version=$(sw_vers -productVersion)
733              _os_major=$(echo "$_os_version" | cut -d. -f1)
734              case $_os_major in
735                  10)
736                      # If we're running on macOS, older than 10.13, then we always
737                      # fail to find these options to force fallback
738                      if [ "$(echo "$_os_version" | cut -d. -f2)" -lt 13 ]; then
739                          # Older than 10.13
740                          warn "Detected macOS platform older than 10.13"
741                          return 1
742                      fi
743                      ;;
744                  *)
745                      if ! { [ "$_os_major" -eq "$_os_major" ] 2>/dev/null && [ "$_os_major" -ge 11 ]; }; then
746                          # Unknown product version, warn and continue
747                          warn "Detected unknown macOS major version: $_os_version"
748                          warn "TLS capabilities detection may fail"
749                      fi
750                      ;; # We assume that macOS v11+ will always be okay.
751              esac
752          fi
753          ;;
754  
755      esac
756  
757      for _arg in "$@"; do
758          if ! "$_cmd" --help "$_category" | grep -q -- "$_arg"; then
759              return 1
760          fi
761      done
762  
763      true # not strictly needed
764  }
765  
766  # Check if curl supports the --retry flag, then pass it to the curl invocation.
767  check_curl_for_retry_support() {
768      local _retry_supported=""
769      # "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc.
770      if check_help_for "notspecified" "curl" "--retry"; then
771          _retry_supported="--retry 3"
772          if check_help_for "notspecified" "curl" "--continue-at"; then
773              # "-C -" tells curl to automatically find where to resume the download when retrying.
774              _retry_supported="--retry 3 -C -"
775          fi
776      fi
777  
778      RETVAL="$_retry_supported"
779  }
780  
781  # Return cipher suite string specified by user, otherwise return strong TLS 1.2-1.3 cipher suites
782  # if support by local tools is detected. Detection currently supports these curl backends:
783  # GnuTLS and OpenSSL (possibly also LibreSSL and BoringSSL). Return value can be empty.
784  get_ciphersuites_for_curl() {
785      if [ -n "${RUSTUP_TLS_CIPHERSUITES-}" ]; then
786          # user specified custom cipher suites, assume they know what they're doing
787          RETVAL="$RUSTUP_TLS_CIPHERSUITES"
788          return
789      fi
790  
791      local _openssl_syntax="no"
792      local _gnutls_syntax="no"
793      local _backend_supported="yes"
794      if curl -V | grep -q ' OpenSSL/'; then
795          _openssl_syntax="yes"
796      elif curl -V | grep -iq ' LibreSSL/'; then
797          _openssl_syntax="yes"
798      elif curl -V | grep -iq ' BoringSSL/'; then
799          _openssl_syntax="yes"
800      elif curl -V | grep -iq ' GnuTLS/'; then
801          _gnutls_syntax="yes"
802      else
803          _backend_supported="no"
804      fi
805  
806      local _args_supported="no"
807      if [ "$_backend_supported" = "yes" ]; then
808          # "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc.
809          if check_help_for "notspecified" "curl" "--tlsv1.2" "--ciphers" "--proto"; then
810              _args_supported="yes"
811          fi
812      fi
813  
814      local _cs=""
815      if [ "$_args_supported" = "yes" ]; then
816          if [ "$_openssl_syntax" = "yes" ]; then
817              _cs=$(get_strong_ciphersuites_for "openssl")
818          elif [ "$_gnutls_syntax" = "yes" ]; then
819              _cs=$(get_strong_ciphersuites_for "gnutls")
820          fi
821      fi
822  
823      RETVAL="$_cs"
824  }
825  
826  # Return cipher suite string specified by user, otherwise return strong TLS 1.2-1.3 cipher suites
827  # if support by local tools is detected. Detection currently supports these wget backends:
828  # GnuTLS and OpenSSL (possibly also LibreSSL and BoringSSL). Return value can be empty.
829  get_ciphersuites_for_wget() {
830      if [ -n "${RUSTUP_TLS_CIPHERSUITES-}" ]; then
831          # user specified custom cipher suites, assume they know what they're doing
832          RETVAL="$RUSTUP_TLS_CIPHERSUITES"
833          return
834      fi
835  
836      local _cs=""
837      if wget -V | grep -q '\-DHAVE_LIBSSL'; then
838          # "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc.
839          if check_help_for "notspecified" "wget" "TLSv1_2" "--ciphers" "--https-only" "--secure-protocol"; then
840              _cs=$(get_strong_ciphersuites_for "openssl")
841          fi
842      elif wget -V | grep -q '\-DHAVE_LIBGNUTLS'; then
843          # "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc.
844          if check_help_for "notspecified" "wget" "TLSv1_2" "--ciphers" "--https-only" "--secure-protocol"; then
845              _cs=$(get_strong_ciphersuites_for "gnutls")
846          fi
847      fi
848  
849      RETVAL="$_cs"
850  }
851  
852  # Return strong TLS 1.2-1.3 cipher suites in OpenSSL or GnuTLS syntax. TLS 1.2
853  # excludes non-ECDHE and non-AEAD cipher suites. DHE is excluded due to bad
854  # DH params often found on servers (see RFC 7919). Sequence matches or is
855  # similar to Firefox 68 ESR with weak cipher suites disabled via about:config.
856  # $1 must be openssl or gnutls.
857  get_strong_ciphersuites_for() {
858      if [ "$1" = "openssl" ]; then
859          # OpenSSL is forgiving of unknown values, no problems with TLS 1.3 values on versions that don't support it yet.
860          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"
861      elif [ "$1" = "gnutls" ]; then
862          # GnuTLS isn't forgiving of unknown values, so this may require a GnuTLS version that supports TLS 1.3 even if wget doesn't.
863          # Begin with SECURE128 (and higher) then remove/add to build cipher suites. Produces same 9 cipher suites as OpenSSL but in slightly different order.
864          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"
865      fi
866  }
867  
868  set +u
869  case "$RUSTUP_INIT_SH_PRINT" in
870      arch | architecture)
871          get_architecture || exit 1
872          echo "$RETVAL"
873          ;;
874      *)
875          main "$@" || exit 1
876          ;;
877  esac