diff --git a/rustup-init.sh b/rustup-init.sh index 20f1a59d38..7440a6ffe7 100755 --- a/rustup-init.sh +++ b/rustup-init.sh @@ -37,6 +37,7 @@ OPTIONS: --default-host Choose a default host triple --default-toolchain Choose a default toolchain to install --default-toolchain none Do not install any toolchains + --profile [minimal|default|complete] Choose a profile EOF } diff --git a/src/rustup-cli/main.rs b/src/rustup-cli/main.rs index de6d23da8c..6d2ffabba2 100644 --- a/src/rustup-cli/main.rs +++ b/src/rustup-cli/main.rs @@ -114,6 +114,7 @@ fn run_rustup() -> Result<()> { let opts = self_update::InstallOpts { default_host_triple: TargetTriple::from_host_or_build().to_string(), default_toolchain: "stable".to_string(), + profile: String::new(), no_modify_path: false, }; if cfg!(windows) { diff --git a/src/rustup-cli/self_update.rs b/src/rustup-cli/self_update.rs index 3817fd4a31..f6bf12e977 100644 --- a/src/rustup-cli/self_update.rs +++ b/src/rustup-cli/self_update.rs @@ -47,6 +47,7 @@ use regex::Regex; pub struct InstallOpts { pub default_host_triple: String, pub default_toolchain: String, + pub profile: String, pub no_modify_path: bool, } @@ -274,7 +275,12 @@ pub fn install(no_prompt: bool, verbose: bool, mut opts: InstallOpts) -> Result< // FIXME: Someday we can stop setting up the symlink, and when // we do that we can stop creating ~/.rustup as well. utils::create_rustup_home()?; - maybe_install_rust(&opts.default_toolchain, &opts.default_host_triple, verbose)?; + maybe_install_rust( + &opts.default_toolchain, + &opts.profile, + &opts.default_host_triple, + verbose, + )?; if cfg!(unix) { let ref env_file = utils::cargo_home()?.join("env"); @@ -606,10 +612,12 @@ fn current_install_opts(opts: &InstallOpts) -> String { - ` `default host triple: `{}` - ` `default toolchain: `{}` +- ` `profile: `{}` - modify PATH variable: `{}` ", opts.default_host_triple, opts.default_toolchain, + opts.profile, if !opts.no_modify_path { "yes" } else { "no" } ) } @@ -631,6 +639,11 @@ fn customize_install(mut opts: InstallOpts) -> Result { &opts.default_toolchain, )?; + opts.profile = common::question_str( + "Profile (which tools and data to install)? (minimal/default/complete)", + &opts.profile, + )?; + opts.no_modify_path = !common::question_bool("Modify PATH variable? (y/n)", !opts.no_modify_path)?; @@ -762,8 +775,14 @@ pub fn install_proxies() -> Result<()> { Ok(()) } -fn maybe_install_rust(toolchain_str: &str, default_host_triple: &str, verbose: bool) -> Result<()> { +fn maybe_install_rust( + toolchain_str: &str, + profile_str: &str, + default_host_triple: &str, + verbose: bool, +) -> Result<()> { let ref cfg = common::set_globals(verbose)?; + cfg.set_profile(profile_str)?; // If there is already an install, then `toolchain_str` may not be // a toolchain the user actually wants. Don't do anything. FIXME: diff --git a/src/rustup-cli/setup_mode.rs b/src/rustup-cli/setup_mode.rs index 98288ba6c4..49d3c97d42 100644 --- a/src/rustup-cli/setup_mode.rs +++ b/src/rustup-cli/setup_mode.rs @@ -41,6 +41,12 @@ pub fn main() -> Result<()> { .takes_value(true) .help("Choose a default toolchain to install"), ) + .arg( + Arg::with_name("profile") + .long("profile") + .takes_value(true) + .help("Choose a profile to install"), + ) .arg( Arg::with_name("no-modify-path") .long("no-modify-path") @@ -55,11 +61,13 @@ pub fn main() -> Result<()> { .map(|s| s.to_owned()) .unwrap_or_else(|| TargetTriple::from_host_or_build().to_string()); let default_toolchain = matches.value_of("default-toolchain").unwrap_or("stable"); + let profile = matches.value_of("profile").unwrap_or("default"); let no_modify_path = matches.is_present("no-modify-path"); let opts = InstallOpts { default_host_triple: default_host, default_toolchain: default_toolchain.to_owned(), + profile: profile.to_owned(), no_modify_path: no_modify_path, }; diff --git a/src/rustup-dist/src/dist.rs b/src/rustup-dist/src/dist.rs index 231ae23429..99558e80ba 100644 --- a/src/rustup-dist/src/dist.rs +++ b/src/rustup-dist/src/dist.rs @@ -3,8 +3,8 @@ use errors::*; use notifications::*; use rustup_utils::{self, utils}; use prefix::InstallPrefix; -use manifest::Component; use manifest::Manifest as ManifestV2; +use manifest::Component; use manifestation::{Changes, Manifestation, UpdateStatus}; use download::DownloadCfg; @@ -434,6 +434,29 @@ impl<'a> Manifest<'a> { } } +#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] +pub enum Profile { + Minimal, + Default, + Complete, +} + +impl Profile { + pub fn from_str(name: &str) -> Result { + match name { + "minimal" | "m" => Ok(Profile::Minimal), + "default" | "d" | "" => Ok(Profile::Default), + "complete" | "c" => Ok(Profile::Complete), + _ => Err(ErrorKind::InvalidProfile(name.to_owned()).into()), + } + } + + // Non-required components that were required before profiles exist. + pub fn legacy_profile() -> Vec { + vec!["rust-docs".to_owned()] + } +} + impl fmt::Display for TargetTriple { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.0.fmt(f) @@ -474,6 +497,16 @@ impl fmt::Display for ToolchainDesc { } } +impl fmt::Display for Profile { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Profile::Minimal => write!(f, "minimal"), + Profile::Default => write!(f, "default"), + Profile::Complete => write!(f, "complete"), + } + } +} + // Installs or updates a toolchain from a dist server. If an initial // install then it will be installed with the default components. If // an upgrade then all the existing components will be upgraded. @@ -483,20 +516,23 @@ pub fn update_from_dist<'a>( download: DownloadCfg<'a>, update_hash: Option<&Path>, toolchain: &ToolchainDesc, + profile: Profile, prefix: &InstallPrefix, - add: &[Component], - remove: &[Component], force_update: bool, ) -> Result> { let fresh_install = !prefix.path().exists(); + let profile = if fresh_install { + Some(profile) + } else { + None + }; let res = update_from_dist_( download, update_hash, toolchain, + profile, prefix, - add, - remove, force_update, ); @@ -511,23 +547,17 @@ pub fn update_from_dist<'a>( res } -pub fn update_from_dist_<'a>( +fn update_from_dist_<'a>( download: DownloadCfg<'a>, update_hash: Option<&Path>, toolchain: &ToolchainDesc, + profile: Option, prefix: &InstallPrefix, - add: &[Component], - remove: &[Component], force_update: bool, ) -> Result> { let toolchain_str = toolchain.to_string(); let manifestation = Manifestation::open(prefix.clone(), toolchain.target.clone())?; - let changes = Changes { - add_extensions: add.to_owned(), - remove_extensions: remove.to_owned(), - }; - // TODO: Add a notification about which manifest version is going to be used (download.notify_handler)(Notification::DownloadingManifest(&toolchain_str)); match dl_v2_manifest(download, update_hash, toolchain) { @@ -536,6 +566,25 @@ pub fn update_from_dist_<'a>( &m.date, m.get_rust_version().ok(), )); + + let add_extensions = match profile { + Some(profile) => { + m.get_profile_components(profile) + .map(|v| v + .iter() + .map(|name| Component { pkg: name.to_owned(), target: Some(TargetTriple::from_host_or_build()) }) + .collect::>() + ) + .unwrap_or_else(|_| Profile::legacy_profile().into_iter().map(|s| Component { pkg: s, target: Some(TargetTriple::from_host_or_build()) }).collect()) + } + None => Vec::new(), + }; + + let changes = Changes { + add_extensions, + remove_extensions: Vec::new(), + }; + return match manifestation.update( &m, changes, diff --git a/src/rustup-dist/src/errors.rs b/src/rustup-dist/src/errors.rs index f193c4288f..115fc69061 100644 --- a/src/rustup-dist/src/errors.rs +++ b/src/rustup-dist/src/errors.rs @@ -24,6 +24,11 @@ error_chain! { description("invalid custom toolchain name") display("invalid custom toolchain name: '{}'", t) } + InvalidProfile(t: String) { + description("invalid profile name") + display("invalid profile name: '{}'; valid names are: \ + `minimal`, `default`, and `complete`", t) + } ChecksumFailed { url: String, expected: String, diff --git a/src/rustup-dist/src/manifest.rs b/src/rustup-dist/src/manifest.rs index 81f85c781a..b9a99e5182 100644 --- a/src/rustup-dist/src/manifest.rs +++ b/src/rustup-dist/src/manifest.rs @@ -15,7 +15,7 @@ use toml; use rustup_utils::toml_utils::*; use std::collections::HashMap; -use dist::TargetTriple; +use dist::{Profile, TargetTriple}; pub const SUPPORTED_MANIFEST_VERSIONS: [&'static str; 1] = ["2"]; pub const DEFAULT_MANIFEST_VERSION: &'static str = "2"; @@ -26,6 +26,7 @@ pub struct Manifest { pub date: String, pub packages: HashMap, pub renames: HashMap, + pub profiles: HashMap>, } #[derive(Clone, Debug, PartialEq)] @@ -82,7 +83,8 @@ impl Manifest { manifest_version: version, date: get_string(&mut table, "date", path)?, packages: Self::table_to_packages(&mut table, path)?, - renames: Self::table_to_renames(table, path)?, + renames: Self::table_to_renames(&mut table, path)?, + profiles: Self::table_to_profiles(&mut table, path)?, }) } pub fn to_toml(self) -> toml::value::Table { @@ -100,6 +102,9 @@ impl Manifest { let packages = Self::packages_to_table(self.packages); result.insert("pkg".to_owned(), toml::Value::Table(packages)); + let profiles = Self::profiles_to_table(self.profiles); + result.insert("profiles".to_owned(), toml::Value::Table(profiles)); + result } @@ -127,11 +132,11 @@ impl Manifest { } fn table_to_renames( - mut table: toml::value::Table, + table: &mut toml::value::Table, path: &str, ) -> Result> { let mut result = HashMap::new(); - let rename_table = get_table(&mut table, "rename", path)?; + let rename_table = get_table(table, "rename", path)?; for (k, v) in rename_table { if let toml::Value::Table(mut t) = v { @@ -151,6 +156,36 @@ impl Manifest { result } + fn table_to_profiles( + table: &mut toml::value::Table, + path: &str, + ) -> Result>> { + let mut result = HashMap::new(); + let profile_table = get_table(table, "profile", path)?; + + for (k, v) in profile_table { + if let toml::Value::Array(a) = v { + let values = a.into_iter() + .filter_map(|v| match v { + toml::Value::String(s) => Some(s), + _ => None, + }) + .collect(); + result.insert(Profile::from_str(&k)?, values); + } + } + + Ok(result) + } + fn profiles_to_table(profiles: HashMap>) -> toml::value::Table { + let mut result = toml::value::Table::new(); + for (profile, values) in profiles { + let array = values.into_iter().map(|v| toml::Value::String(v)).collect(); + result.insert(profile.to_string(), toml::Value::Array(array)); + } + result + } + pub fn get_package(&self, name: &str) -> Result<&Package> { self.packages .get(name) @@ -161,6 +196,12 @@ impl Manifest { self.get_package("rust").map(|p| &*p.version) } + pub fn get_profile_components(&self, profile: Profile) -> Result<&Vec> { + self.profiles + .get(&profile) + .ok_or_else(|| format!("profile not found: '{}'", profile).into()) + } + fn validate_targeted_package(&self, tpkg: &TargetedPackage) -> Result<()> { for c in tpkg.components.iter().chain(tpkg.extensions.iter()) { let cpkg = self.get_package(&c.pkg) diff --git a/src/rustup-dist/src/manifestation.rs b/src/rustup-dist/src/manifestation.rs index 60312af7de..ad08fed68c 100644 --- a/src/rustup-dist/src/manifestation.rs +++ b/src/rustup-dist/src/manifestation.rs @@ -94,10 +94,10 @@ impl Manifestation { /// /// `update` takes a manifest describing a release of Rust (which /// may be either a freshly-downloaded one, or the same one used - /// for the previous install), as well as lists off extension + /// for the previous install), as well as lists of extension /// components to add and remove. - /// From that it schedules a list of components to uninstall and + /// From that it schedules a list of components to install and /// to uninstall to bring the installation up to date. It /// downloads the components' packages. Then in a Transaction /// uninstalls old packages and installs new packages, writes the diff --git a/src/rustup-utils/src/toml_utils.rs b/src/rustup-utils/src/toml_utils.rs index 1ecc174185..906a7b66b0 100644 --- a/src/rustup-utils/src/toml_utils.rs +++ b/src/rustup-utils/src/toml_utils.rs @@ -80,7 +80,7 @@ pub fn get_array( if let toml::Value::Array(s) = v { Ok(s) } else { - Err(ErrorKind::ExpectedType("table", path.to_owned() + key).into()) + Err(ErrorKind::ExpectedType("array", path.to_owned() + key).into()) } } else { Ok(toml::value::Array::new()) diff --git a/src/rustup/config.rs b/src/rustup/config.rs index 04a1675a58..96334e966c 100644 --- a/src/rustup/config.rs +++ b/src/rustup/config.rs @@ -123,6 +123,26 @@ impl Cfg { Ok(()) } + pub fn set_profile(&self, profile: &str) -> Result<()> { + self.settings_file.with_mut(|s| { + s.profile = Some(profile.to_owned()); + Ok(()) + })?; + (self.notify_handler)(Notification::SetProfile(profile)); + Ok(()) + } + + pub fn get_profile(&self) -> Result { + self.settings_file + .with(|s| { + s.profile + .as_ref() + .map(|s| dist::Profile::from_str(&s)) + .unwrap_or(Ok(dist::Profile::Default)) + .map_err(|e| e.into()) + }) + } + pub fn get_toolchain(&self, name: &str, create_parent: bool) -> Result { if create_parent { utils::ensure_dir_exists("toolchains", &self.toolchains_dir, &|n| { diff --git a/src/rustup/install.rs b/src/rustup/install.rs index 2ee13ae646..6cb95aa8dd 100644 --- a/src/rustup/install.rs +++ b/src/rustup/install.rs @@ -19,6 +19,7 @@ pub enum InstallMethod<'a> { // bool is whether to force an update Dist( &'a dist::ToolchainDesc, + dist::Profile, Option<&'a Path>, DownloadCfg<'a>, bool, @@ -50,15 +51,14 @@ impl<'a> InstallMethod<'a> { InstallMethod::tar_gz(src, path, &temp_cfg, notify_handler)?; Ok(true) } - InstallMethod::Dist(toolchain, update_hash, dl_cfg, force_update) => { + InstallMethod::Dist(toolchain, profile, update_hash, dl_cfg, force_update) => { let prefix = &InstallPrefix::from(path.to_owned()); let maybe_new_hash = dist::update_from_dist( dl_cfg, update_hash, toolchain, + profile, prefix, - &[], - &[], force_update, )?; diff --git a/src/rustup/notifications.rs b/src/rustup/notifications.rs index 5a9f152f23..d6a0e3b444 100644 --- a/src/rustup/notifications.rs +++ b/src/rustup/notifications.rs @@ -15,6 +15,7 @@ pub enum Notification<'a> { SetDefaultToolchain(&'a str), SetOverrideToolchain(&'a Path, &'a str), + SetProfile(&'a str), LookingForToolchain(&'a str), ToolchainDirectory(&'a Path, &'a str), UpdatingToolchain(&'a str), @@ -71,6 +72,7 @@ impl<'a> Notification<'a> { | TelemetryCleanupError(_) => NotificationLevel::Verbose, SetDefaultToolchain(_) | SetOverrideToolchain(_, _) + | SetProfile(_) | UsingExistingToolchain(_) | UninstallingToolchain(_) | UninstalledToolchain(_) @@ -98,6 +100,7 @@ impl<'a> Display for Notification<'a> { path.display(), name ), + SetProfile(name) => write!(f, "profile set to '{}'", name), LookingForToolchain(name) => write!(f, "looking for installed toolchain '{}'", name), ToolchainDirectory(path, _) => write!(f, "toolchain directory: '{}'", path.display()), UpdatingToolchain(name) => write!(f, "updating existing install for '{}'", name), diff --git a/src/rustup/settings.rs b/src/rustup/settings.rs index 383bbd726f..f1ddfeac7b 100644 --- a/src/rustup/settings.rs +++ b/src/rustup/settings.rs @@ -126,6 +126,7 @@ pub struct Settings { pub version: String, pub default_host_triple: Option, pub default_toolchain: Option, + pub profile: Option, pub overrides: BTreeMap, pub telemetry: TelemetryMode, } @@ -136,6 +137,7 @@ impl Default for Settings { version: DEFAULT_METADATA_VERSION.to_owned(), default_host_triple: None, default_toolchain: None, + profile: None, overrides: BTreeMap::new(), telemetry: TelemetryMode::Off, } @@ -191,6 +193,7 @@ impl Settings { version: version, default_host_triple: get_opt_string(&mut table, "default_host_triple", path)?, default_toolchain: get_opt_string(&mut table, "default_toolchain", path)?, + profile: get_opt_string(&mut table, "profile", path)?, overrides: Self::table_to_overrides(&mut table, path)?, telemetry: if get_opt_bool(&mut table, "telemetry", path)?.unwrap_or(false) { TelemetryMode::On @@ -212,6 +215,10 @@ impl Settings { result.insert("default_toolchain".to_owned(), toml::Value::String(v)); } + if let Some(v) = self.profile { + result.insert("profile".to_owned(), toml::Value::String(v)); + } + let overrides = Self::overrides_to_table(self.overrides); result.insert("overrides".to_owned(), toml::Value::Table(overrides)); diff --git a/src/rustup/toolchain.rs b/src/rustup/toolchain.rs index 1539437e63..32affc7d3f 100644 --- a/src/rustup/toolchain.rs +++ b/src/rustup/toolchain.rs @@ -174,6 +174,7 @@ impl<'a> Toolchain<'a> { let update_hash = self.update_hash()?; self.install(InstallMethod::Dist( &self.desc()?, + self.cfg.get_profile()?, update_hash.as_ref().map(|p| &**p), self.download_cfg(), force_update, @@ -214,6 +215,7 @@ impl<'a> Toolchain<'a> { let update_hash = self.update_hash()?; self.install_if_not_installed(InstallMethod::Dist( &self.desc()?, + self.cfg.get_profile()?, update_hash.as_ref().map(|p| &**p), self.download_cfg(), false,