[gnome-builder/wip/chergert/rustup] wip: stub out idea for rustup plugin
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/chergert/rustup] wip: stub out idea for rustup plugin
- Date: Sun, 30 Oct 2016 09:19:18 +0000 (UTC)
commit bd43e97b5b67556a68be6b6e941a4410b18efe71
Author: Christian Hergert <chergert redhat com>
Date: Sun Oct 30 02:15:09 2016 -0700
wip: stub out idea for rustup plugin
If you'd like to work on this and finish it, please contact me!
configure.ac | 2 +
plugins/rustup/Makefile.am | 30 +
plugins/rustup/configure.ac | 12 +
.../org.gnome.builder.plugins.rustup.gschema.xml | 19 +
plugins/rustup/rustup.plugin | 9 +
plugins/rustup/rustup_plugin/__init__.py | 198 ++
plugins/rustup/rustup_plugin/resources/rustup.sh | 1998 ++++++++++++++++++++
7 files changed, 2268 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index e0bd388..7459456 100644
--- a/configure.ac
+++ b/configure.ac
@@ -311,6 +311,7 @@ m4_include([plugins/python-gi-imports-completion/configure.ac])
m4_include([plugins/python-pack/configure.ac])
m4_include([plugins/quick-highlight/configure.ac])
m4_include([plugins/rust-langserv/configure.ac])
+m4_include([plugins/rustup/configure.ac])
m4_include([plugins/support/configure.ac])
m4_include([plugins/symbol-tree/configure.ac])
m4_include([plugins/sysmon/configure.ac])
@@ -622,6 +623,7 @@ echo " Python Jedi Autocompletion ........... : ${enable_jedi_plugin}"
echo " Python Language Pack ................. : ${enable_python_pack_plugin}"
echo " Quick Highlight ...................... : ${enable_quick_highlight_plugin}"
echo " Rust Language Server ................. : ${enable_rust_langserv_plugin}"
+echo " RustUp ............................... : ${enable_rustup_plugin}"
echo " Support .............................. : ${enable_support_plugin}"
echo " System Monitor ....................... : ${enable_sysmon_plugin}"
echo " Sysprof System Profiler .............. : ${enable_sysprof_plugin}"
diff --git a/plugins/rustup/Makefile.am b/plugins/rustup/Makefile.am
new file mode 100644
index 0000000..99a40ff
--- /dev/null
+++ b/plugins/rustup/Makefile.am
@@ -0,0 +1,30 @@
+if ENABLE_RUSTUP_PLUGIN
+
+EXTRA_DIST = $(plugin_DATA)
+
+plugindir = $(libdir)/gnome-builder/plugins
+dist_plugin_DATA = \
+ rustup.plugin \
+ rustup_plugin/__init__.py \
+ $(NULL)
+
+resourcedir = $(datadir)/gnome-builder/plugins/
+nobase_resource_DATA = \
+ rustup_plugin/resources/rustup.sh \
+ $(NULL)
+
+gsettings_SCHEMAS = \
+ gsettings/org.gnome.builder.plugins.rustup.gschema.xml \
+ $(NULL)
+
+.PRECIOUS: $(gsettings_SCHEMAS)
+
+@GSETTINGS_RULES@
+
+EXTRA_DIST += $(gsettings_SCHEMAS)
+
+endif
+
+include $(top_srcdir)/plugins/Makefile.plugin
+
+-include $(top_srcdir)/git.mk
diff --git a/plugins/rustup/configure.ac b/plugins/rustup/configure.ac
new file mode 100644
index 0000000..26edcd0
--- /dev/null
+++ b/plugins/rustup/configure.ac
@@ -0,0 +1,12 @@
+# --enable-rustup-plugin=yes/no
+AC_ARG_ENABLE([rustup-plugin],
+ [AS_HELP_STRING([--enable-rustup-plugin=@<:@yes/no@:>@],
+ [Build with support for RustUp integration.])],
+ [enable_rustup_plugin=$enableval],
+ [enable_rustup_plugin=yes])
+
+# for if ENABLE_RUSTUP_PLUGIN in Makefile.am
+AM_CONDITIONAL(ENABLE_RUSTUP_PLUGIN, test x$enable_rustup_plugin = xyes)
+
+# Ensure our makefile is generated by autoconf
+AC_CONFIG_FILES([plugins/rustup/Makefile])
diff --git a/plugins/rustup/gsettings/org.gnome.builder.plugins.rustup.gschema.xml
b/plugins/rustup/gsettings/org.gnome.builder.plugins.rustup.gschema.xml
new file mode 100644
index 0000000..9d2e42a
--- /dev/null
+++ b/plugins/rustup/gsettings/org.gnome.builder.plugins.rustup.gschema.xml
@@ -0,0 +1,19 @@
+<schemalist>
+ <schema id="org.gnome.builder.plugins.rustup" path="/org/gnome/builder/plugins/rustup/"
gettext-domain="gnome-builder">
+ <key name="auto-update" type="b">
+ <default>false</default>
+ <summary>Auto-update using rustup</summary>
+ <description>If rustup should be used to auto-update your rust installation.</description>
+ </key>
+ <key name="channel" type="s">
+ <default>"stable"</default>
+ <summary>The RustUp channel</summary>
+ <description>The channel that should be used for rustup updates.</description>
+ </key>
+ <key name="prefix" type="s">
+ <default>"~/.local"</default>
+ <summary>Installation prefix</summary>
+ <description>The prefix for installing rust.</description>
+ </key>
+ </schema>
+</schemalist>
diff --git a/plugins/rustup/rustup.plugin b/plugins/rustup/rustup.plugin
new file mode 100644
index 0000000..8a0b677
--- /dev/null
+++ b/plugins/rustup/rustup.plugin
@@ -0,0 +1,9 @@
+[Plugin]
+Module=rustup_plugin
+Name=RustUp
+Loader=python3
+Description=Helps you keep your rust installation updated
+Authors=Christian Hergert <christian hergert me>
+Copyright=Copyright © 2016 Christian Hergert
+Builtin=true
+Hidden=true
diff --git a/plugins/rustup/rustup_plugin/__init__.py b/plugins/rustup/rustup_plugin/__init__.py
new file mode 100644
index 0000000..1d10d25
--- /dev/null
+++ b/plugins/rustup/rustup_plugin/__init__.py
@@ -0,0 +1,198 @@
+#!/usr/bin/env python3
+
+#
+# __init__.py
+#
+# Copyright (C) 2016 Christian Hergert <chergert redhat com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import gi
+import os
+
+gi.require_version('Ide', '1.0')
+gi.require_version('Gtk', '3.0')
+
+from gi.repository import GLib
+from gi.repository import GObject
+from gi.repository import Gio
+from gi.repository import Gtk
+from gi.repository import Ide
+from gi.repository import Peas
+
+_ = Ide.gettext
+
+def get_module_data_path(name):
+ engine = Peas.Engine.get_default()
+ plugin = engine.get_plugin_info('rustup_plugin')
+ data_dir = plugin.get_data_dir()
+ return GLib.build_filenamev([data_dir, name])
+
+class RustupApplicationAddin(GObject.Object, Ide.ApplicationAddin):
+ """
+ The RustupApplicationAddin provides us a single point to manage updates
+ within the Builder process. Our other addins will perform their various
+ actions by communicating with this singleton.
+ """
+ # Our singleton instance
+ instance = None
+
+ # Current updater while we are busy
+ updater = None
+
+ @GObject.Property(type=bool, default=False)
+ def busy(self):
+ return self.updater is not None
+
+ def do_load(self, application):
+ RustupApplicationAddin.instance = self
+
+ def do_unload(self, application):
+ RustupApplicationAddin.instance = None
+
+ def check_update(self, *args):
+ """
+ This function will begin checking the rustup service for updates
+ to Rust. If we find one, we will notify listeners that we have
+ started a new transfer (and they can add it to their particular
+ IdeTransferManager.
+ """
+ settings = Gio.Settings.new('org.gnome.builder.plugins.rustup')
+
+ channel = settings.get_string('channel')
+ prefix = settings.get_string('prefix')
+
+ self.updater = RustupUpdater(channel=channel, prefix=prefix)
+ self.updater.run()
+
+ self.notify("busy")
+
+ def cancel(self):
+ if self.updater:
+ self.updater.cancel()
+
+class RustupUpdater(GObject.Object):
+ """
+ The RustUpdater class handles the process of checking for a new version
+ of rust as well as tracking the transfer progress of the process.
+
+ Compare this to RustupTransfer() which is a wrapper object around the
+ updater so that we can see the transfer in all workbench windows but
+ only a single update process for the process.
+ """
+ channel = GObject.Property(type=str, default='stable')
+ prefix = GObject.Property(type=str, default='~/.local/')
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.cancellable = Gio.Cancellable()
+
+ def cancel(self):
+ self.cancellable.cancel()
+
+ def run(self):
+ rustup_sh_path = get_module_data_path('resources/rustup.sh')
+ prefix = os.path.expanduser(self.props.prefix)
+
+ launcher = Ide.SubprocessLauncher()
+ launcher.set_flags(Gio.SubprocessFlags.STDOUT_PIPE |
+ Gio.SubprocessFlags.STDERR_MERGE)
+ launcher.set_run_on_host(True)
+ launcher.set_clear_env(False)
+ launcher.push_argv(rustup_sh_path)
+ launcher.push_argv('--disable-sudo')
+ launcher.push_argv('--yes')
+ launcher.push_argv('--save')
+ launcher.push_argv('--channel=' + self.props.channel)
+ launcher.push_argv('--prefix=' + prefix)
+
+ subprocess = launcher.spawn()
+
+ stdout_pipe = subprocess.get_stdout_pipe()
+ data_stream = Gio.DataInputStream.new(stdout_pipe)
+ data_stream.read_line_async(GLib.PRIORITY_DEFAULT, self.cancellable, self._read_line_cb)
+
+ subprocess.wait_check_async(self.cancellable, self._wait_cb)
+
+ def _wait_cb(self, subprocess, result):
+ print('>>> subprocess exited')
+ try:
+ subprocess.wait_check_finish(result)
+ print("Finished cleanly")
+ except Exception as ex:
+ print(ex)
+
+ def _read_line_cb(self, data_stream, result):
+ try:
+ line, length = data_stream.read_line_finish_utf8(result)
+ print(">>>", line)
+ if length > 0:
+ data_stream.read_line_async(GLib.PRIORITY_DEFAULT, self.cancellable, self._read_line_cb)
+ except Exception as ex:
+ print(ex)
+
+class RustupPreferencesAddin(GObject.Object, Ide.PreferencesAddin):
+
+ def do_load(self, preferences):
+ preferences.add_page('sdk', _('SDKs'), 550)
+ preferences.add_list_group('sdk', 'rustup', _('Rust Developer Channel'), Gtk.SelectionMode.SINGLE,
100)
+
+ updater = Gtk.Button(halign=Gtk.Align.END, label=_("Check for updates"), visible=True)
+ updater.connect('clicked', RustupApplicationAddin.instance.check_update)
+ RustupApplicationAddin.instance.bind_property('busy', updater, 'sensitive',
+ GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.INVERT_BOOLEAN)
+
+ self.ids = [
+ preferences.add_radio('sdk', 'rustup',
+ 'org.gnome.builder.plugins.rustup', 'channel', None, "'stable'", _('Stable'),
+ _('The current stable release of Rust, updated every six weeks and backwards-compatible'),
+ None, 0),
+ preferences.add_radio('sdk', 'rustup',
+ 'org.gnome.builder.plugins.rustup', 'channel', None, "'beta'", _('Beta'),
+ _('A preview of the upcoming stable release, intended for testing by crate authors. Updated
every six weeks and as needed'),
+ None, 0),
+ preferences.add_radio('sdk', 'rustup',
+ 'org.gnome.builder.plugins.rustup', 'channel', None, "'nightly'", _('Nightly'),
+ _('The current development branch. It includes unstable features that are not available in
the betas or stable releases.'),
+ None, 0),
+ preferences.add_switch('sdk', 'rustup',
+ 'org.gnome.builder.plugins.rustup', 'auto-update', None, "true", _('Automatically Update'),
+ _('Use RustUp to automatically keep your rust installation up to date.'),
+ None, 0),
+ preferences.add_custom('sdk', 'rustup', updater, None, 1000),
+ ]
+
+ def do_unload(self, preferences):
+ if self.ids:
+ for id in self.ids:
+ preferences.remove_id(id)
+
+class RustupTransfer(Ide.Object, Ide.Transfer):
+
+ title = GObject.Property(type=str)
+ icon_name = GObject.Property(type=str)
+ progress = GObject.Property(type=float)
+ status = GObject.Property(type=str)
+
+ def complete(self, task):
+ task.return_boolean(True)
+
+ def do_execute_async(self, cancellable, callback, data):
+ task = Gio.Task.new(self, cancellable, callback)
+ GLib.timeout_add_seconds(10, self.complete, task)
+
+ def do_execute_finish(self, task):
+ return task.propagate_boolean()
+
diff --git a/plugins/rustup/rustup_plugin/resources/rustup.sh
b/plugins/rustup/rustup_plugin/resources/rustup.sh
new file mode 100755
index 0000000..9c0c477
--- /dev/null
+++ b/plugins/rustup/rustup_plugin/resources/rustup.sh
@@ -0,0 +1,1998 @@
+#!/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}"
+ assert_nz "$rustup_dir" "rustup_dir"
+
+ # Install prefix can be set by the environment
+ default_prefix="${RUSTUP_PREFIX-/usr/local}"
+ default_save=false
+ if [ -n "${RUSTUP_SAVE-}" ]; then
+ default_save=true
+ fi
+
+ # Data locations
+ version_file="$rustup_dir/rustup-version"
+ temp_dir="$rustup_dir/tmp"
+ dl_dir="$rustup_dir/dl"
+
+ # Set up the GPG key
+ official_rust_gpg_key="
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1
+
+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
+Mve696B5tlHyc1KxjHR6w9GRsh4=
+=5FXw
+-----END PGP PUBLIC KEY BLOCK-----
+"
+
+ if [ -n "${RUSTUP_GPG_KEY-}" ]; then
+ gpg_key=$(cat "$RUSTUP_GPG_KEY")
+ else
+ gpg_key="$official_rust_gpg_key"
+ fi
+
+ # This is just used by test.sh for testing sha256sum fallback to shasum
+ sha256sum_cmd="${__RUSTUP_MOCK_SHA256SUM-sha256sum}"
+
+ flag_verbose=false
+ flag_yes=false
+
+ if [ -n "${RUSTUP_VERBOSE-}" ]; then
+ flag_verbose=true
+ fi
+}
+
+# Ensuresthat ~/.rustup 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 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
+ # 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 dir, which will be deleted
+ # afterward if the user doesn't pass --save. *If* ~/.rustup
+ # 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:
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]