about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-01-24 23:34:35 +0000
committerbors <bors@rust-lang.org>2017-01-24 23:34:35 +0000
commit83c2d95238e3545e7ae9af4873c48b1e3651c164 (patch)
tree5888cdf6bb45695b9ed8eb06dd583bba141e8840
parent249b444efa356c252b264c56ba5041d70d0b9734 (diff)
parentf3dfcae2029be9de5ad2e015d97ca246c2381b84 (diff)
downloadrust-83c2d95238e3545e7ae9af4873c48b1e3651c164.tar.gz
rust-83c2d95238e3545e7ae9af4873c48b1e3651c164.zip
Auto merge of #39245 - alexcrichton:enable-platform, r=brson
rustbuild: Start building --enable-extended

This commit adds a new flag to the configure script,
`--enable-extended`, which is intended for specifying a desire to
compile the full suite of Rust tools such as Cargo, the RLS, etc. This
is also an indication that the build system should create combined
installers such as the pkg/exe/msi artifacts.

Currently the `--enable-extended` flag just indicates that combined
installers should be built, and Cargo is itself not compiled just yet
but rather only downloaded from its location. The intention here is to
quickly get to feature parity with the current release process and then
we can start improving it afterwards.

All new files in this PR inside `src/etc/installer` are copied from the
rust-packaging repository.

cc #38531
-rw-r--r--.gitattributes3
-rw-r--r--.travis.yml4
-rw-r--r--appveyor.yml12
-rwxr-xr-xconfigure1
-rw-r--r--src/bootstrap/channel.rs3
-rw-r--r--src/bootstrap/config.rs4
-rw-r--r--src/bootstrap/config.toml.example6
-rw-r--r--src/bootstrap/dist.rs392
-rw-r--r--src/bootstrap/lib.rs4
-rw-r--r--src/bootstrap/step.rs14
-rw-r--r--src/ci/docker/dist-arm-linux/Dockerfile2
-rw-r--r--src/ci/docker/dist-armv7-aarch64-linux/Dockerfile2
-rw-r--r--src/ci/docker/dist-freebsd/Dockerfile2
-rw-r--r--src/ci/docker/dist-mips-linux/Dockerfile2
-rw-r--r--src/ci/docker/dist-mips64-linux/Dockerfile2
-rw-r--r--src/ci/docker/dist-powerpc-linux/Dockerfile2
-rw-r--r--src/ci/docker/dist-powerpc64-linux/Dockerfile2
-rw-r--r--src/ci/docker/dist-s390x-linux-netbsd/Dockerfile2
-rw-r--r--src/ci/docker/dist-x86-linux/Dockerfile2
-rw-r--r--src/etc/installer/README.md28
-rw-r--r--src/etc/installer/exe/modpath.iss219
-rw-r--r--src/etc/installer/exe/rust.iss80
-rw-r--r--src/etc/installer/exe/upgrade.iss61
-rw-r--r--src/etc/installer/gfx/banner.bmpbin0 -> 114514 bytes
-rw-r--r--src/etc/installer/gfx/banner.xcfbin0 -> 148261 bytes
-rw-r--r--src/etc/installer/gfx/dialogbg.bmpbin0 -> 615402 bytes
-rw-r--r--src/etc/installer/gfx/dialogbg.xcfbin0 -> 216045 bytes
-rw-r--r--src/etc/installer/gfx/rust-logo.icobin0 -> 370070 bytes
-rw-r--r--src/etc/installer/gfx/rust-logo.pngbin0 -> 5844 bytes
-rw-r--r--src/etc/installer/msi/remove-duplicates.xsl24
-rw-r--r--src/etc/installer/msi/rust.wxs279
-rw-r--r--src/etc/installer/msi/rustwelcomedlg.wxs57
-rw-r--r--src/etc/installer/msi/squash-components.xsl34
-rw-r--r--src/etc/installer/msi/ui.wxs83
-rw-r--r--src/etc/installer/pkg/Distribution.xml71
-rwxr-xr-xsrc/etc/installer/pkg/postinstall26
36 files changed, 1398 insertions, 25 deletions
diff --git a/.gitattributes b/.gitattributes
index 1d4c6252f2c..e2d04142766 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -4,7 +4,6 @@
 *.cpp rust
 *.h rust
 *.rs rust
-src/etc/pkg/rust-logo.ico binary
-src/etc/pkg/rust-logo.png binary
+src/etc/installer/gfx/* binary
 *.woff binary
 src/vendor/* binary
diff --git a/.travis.yml b/.travis.yml
index 0546f6827a6..bbe0cdfb6f8 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -57,7 +57,7 @@ matrix:
 
     - env: >
         SCRIPT="./x.py test && ./x.py dist"
-        RUST_CONFIGURE_ARGS=--build=i686-apple-darwin
+        RUST_CONFIGURE_ARGS="--build=i686-apple-darwin --enable-extended"
         SRC=.
         DEPLOY=1
       os: osx
@@ -76,7 +76,7 @@ matrix:
       after_failure: *osx_after_failure
     - env: >
         RUST_CHECK_TARGET=dist
-        RUST_CONFIGURE_ARGS=--target=aarch64-apple-ios,armv7-apple-ios,armv7s-apple-ios,i386-apple-ios,x86_64-apple-ios
+        RUST_CONFIGURE_ARGS="--target=aarch64-apple-ios,armv7-apple-ios,armv7s-apple-ios,i386-apple-ios,x86_64-apple-ios --enable-extended"
         SRC=.
         DEPLOY=1
       os: osx
diff --git a/appveyor.yml b/appveyor.yml
index cd61f95875f..f158d788d16 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -7,11 +7,11 @@ environment:
   matrix:
   # 32/64 bit MSVC
   - MSYS_BITS: 64
-    RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc
+    RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-extended
     SCRIPT: python x.py test && python x.py dist
     DEPLOY: 1
   - MSYS_BITS: 32
-    RUST_CONFIGURE_ARGS: --build=i686-pc-windows-msvc --target=i586-pc-windows-msvc
+    RUST_CONFIGURE_ARGS: --build=i686-pc-windows-msvc --target=i586-pc-windows-msvc --enable-extended
     SCRIPT: python x.py test --host i686-pc-windows-msvc --target i686-pc-windows-msvc && python x.py dist
     DEPLOY: 1
 
@@ -51,7 +51,7 @@ environment:
   # *not* use debug assertions and llvm assertions. This is because they take
   # too long on appveyor and this is tested by rustbuild below.
   - MSYS_BITS: 32
-    RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu
+    RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu --enable-extended
     SCRIPT: python x.py test && python x.py dist
     MINGW_URL: https://s3.amazonaws.com/rust-lang-ci
     MINGW_ARCHIVE: i686-4.9.2-release-win32-dwarf-rt_v4-rev4.7z
@@ -67,7 +67,7 @@ environment:
 
   - MSYS_BITS: 64
     SCRIPT: python x.py test && python x.py dist
-    RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu
+    RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu --enable-extended
     MINGW_URL: https://s3.amazonaws.com/rust-lang-ci
     MINGW_ARCHIVE: x86_64-4.9.2-release-win32-seh-rt_v4-rev4.7z
     MINGW_DIR: mingw64
@@ -103,6 +103,10 @@ install:
   - 7z x -y sccache.tar > nul
   - set PATH=%PATH%;%CD%\sccache2
 
+  # Install InnoSetup to get `iscc` used to produce installers
+  - choco install -y InnoSetup
+  - set PATH="C:\Program Files (x86)\Inno Setup 5";%PATH%
+
   # Help debug some handle issues on AppVeyor
   - ps: Invoke-WebRequest -Uri https://download.sysinternals.com/files/Handle.zip -OutFile handle.zip
   - mkdir handle
diff --git a/configure b/configure
index 208fb7e6836..505767cede5 100755
--- a/configure
+++ b/configure
@@ -707,6 +707,7 @@ opt_nosave clang 0 "prefer clang to gcc for building the runtime"
 opt_nosave jemalloc 1 "build liballoc with jemalloc"
 opt elf-tls 1 "elf thread local storage on platforms where supported"
 opt full-bootstrap 0 "build three compilers instead of two"
+opt extended 0 "build an extended rust tool set"
 
 valopt_nosave prefix "/usr/local" "set installation prefix"
 valopt_nosave local-rust-root "/usr/local" "set prefix for local rust binary"
diff --git a/src/bootstrap/channel.rs b/src/bootstrap/channel.rs
index c38bb33aa02..585d9f51b92 100644
--- a/src/bootstrap/channel.rs
+++ b/src/bootstrap/channel.rs
@@ -40,6 +40,9 @@ pub fn collect(build: &mut Build) {
         }
     }
 
+    build.release_num = release_num.to_string();
+    build.prerelease_version = release_num.to_string();
+
     // Depending on the channel, passed in `./configure --release-channel`,
     // determine various properties of the build.
     match &build.config.channel[..] {
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index 152164342cd..7d1abcfa6f6 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -47,6 +47,7 @@ pub struct Config {
     pub vendor: bool,
     pub target_config: HashMap<String, Target>,
     pub full_bootstrap: bool,
+    pub extended: bool,
 
     // llvm codegen options
     pub llvm_assertions: bool,
@@ -140,6 +141,7 @@ struct Build {
     nodejs: Option<String>,
     python: Option<String>,
     full_bootstrap: Option<bool>,
+    extended: Option<bool>,
 }
 
 /// TOML representation of various global install decisions.
@@ -276,6 +278,7 @@ impl Config {
         set(&mut config.submodules, build.submodules);
         set(&mut config.vendor, build.vendor);
         set(&mut config.full_bootstrap, build.full_bootstrap);
+        set(&mut config.extended, build.extended);
 
         if let Some(ref install) = toml.install {
             config.prefix = install.prefix.clone().map(PathBuf::from);
@@ -412,6 +415,7 @@ impl Config {
                 ("CODEGEN_TESTS", self.codegen_tests),
                 ("VENDOR", self.vendor),
                 ("FULL_BOOTSTRAP", self.full_bootstrap),
+                ("EXTENDED", self.extended),
             }
 
             match key {
diff --git a/src/bootstrap/config.toml.example b/src/bootstrap/config.toml.example
index 47e50cb79b4..4b859482562 100644
--- a/src/bootstrap/config.toml.example
+++ b/src/bootstrap/config.toml.example
@@ -118,6 +118,12 @@
 # option to true.
 #full-bootstrap = false
 
+# Enable a build of the and extended rust tool set which is not only the
+# compiler but also tools such as Cargo. This will also produce "combined
+# installers" which are used to install Rust and Cargo together. This is
+# disabled by default.
+#extended = false
+
 # =============================================================================
 # General install configuration options
 # =============================================================================
diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs
index dc45d3817fe..e5f05059523 100644
--- a/src/bootstrap/dist.rs
+++ b/src/bootstrap/dist.rs
@@ -18,11 +18,14 @@
 //! out to `rust-installer` still. This may one day be replaced with bits and
 //! pieces of `rustup.rs`!
 
+use std::env;
 use std::fs::{self, File};
-use std::io::Write;
+use std::io::{Read, Write};
 use std::path::{PathBuf, Path};
 use std::process::Command;
 
+use build_helper::output;
+
 use {Build, Compiler, Mode};
 use util::{cp_r, libdir, is_dylib, cp_filtered, copy};
 
@@ -35,6 +38,10 @@ pub fn package_vers(build: &Build) -> &str {
     }
 }
 
+fn pkgname(build: &Build, component: &str) -> String {
+    format!("{}-{}", component, package_vers(build))
+}
+
 fn distdir(build: &Build) -> PathBuf {
     build.out.join("dist")
 }
@@ -53,8 +60,8 @@ pub fn docs(build: &Build, stage: u32, host: &str) {
         return
     }
 
-    let name = format!("rust-docs-{}", package_vers(build));
-    let image = tmpdir(build).join(format!("{}-{}-image", name, name));
+    let name = pkgname(build, "rust-docs");
+    let image = tmpdir(build).join(format!("{}-{}-image", name, host));
     let _ = fs::remove_dir_all(&image);
 
     let dst = image.join("share/doc/rust/html");
@@ -94,7 +101,7 @@ pub fn docs(build: &Build, stage: u32, host: &str) {
 /// in Rust.
 pub fn mingw(build: &Build, host: &str) {
     println!("Dist mingw ({})", host);
-    let name = format!("rust-mingw-{}", package_vers(build));
+    let name = pkgname(build, "rust-mingw");
     let image = tmpdir(build).join(format!("{}-{}-image", name, host));
     let _ = fs::remove_dir_all(&image);
     t!(fs::create_dir_all(&image));
@@ -130,7 +137,7 @@ pub fn mingw(build: &Build, host: &str) {
 /// Creates the `rustc` installer component.
 pub fn rustc(build: &Build, stage: u32, host: &str) {
     println!("Dist rustc stage{} ({})", stage, host);
-    let name = format!("rustc-{}", package_vers(build));
+    let name = pkgname(build, "rustc");
     let image = tmpdir(build).join(format!("{}-{}-image", name, host));
     let _ = fs::remove_dir_all(&image);
     let overlay = tmpdir(build).join(format!("{}-{}-overlay", name, host));
@@ -274,7 +281,7 @@ pub fn std(build: &Build, compiler: &Compiler, target: &str) {
         return
     }
 
-    let name = format!("rust-std-{}", package_vers(build));
+    let name = pkgname(build, "rust-std");
     let image = tmpdir(build).join(format!("{}-{}-image", name, target));
     let _ = fs::remove_dir_all(&image);
 
@@ -328,7 +335,7 @@ pub fn analysis(build: &Build, compiler: &Compiler, target: &str) {
         compiler.clone()
     };
 
-    let name = format!("rust-analysis-{}", package_vers(build));
+    let name = pkgname(build, "rust-analysis");
     let image = tmpdir(build).join(format!("{}-{}-image", name, target));
 
     let src = build.stage_out(&compiler, Mode::Libstd).join(target).join("release").join("deps");
@@ -357,7 +364,7 @@ pub fn analysis(build: &Build, compiler: &Compiler, target: &str) {
 pub fn rust_src(build: &Build) {
     println!("Dist src");
 
-    let name = format!("rust-src-{}", package_vers(build));
+    let name = pkgname(build, "rust-src");
     let image = tmpdir(build).join(format!("{}-image", name));
     let _ = fs::remove_dir_all(&image);
 
@@ -500,3 +507,372 @@ fn write_file(path: &Path, data: &[u8]) {
     let mut vf = t!(fs::File::create(path));
     t!(vf.write_all(data));
 }
+
+// FIXME(#38531) eventually this should package up a Cargo that we just compiled
+//               and tested locally, but for now we're downloading cargo
+//               artifacts from their compiled location.
+pub fn cargo(build: &Build, stage: u32, target: &str) {
+    println!("Dist cargo stage{} ({})", stage, target);
+
+    let branch = match &build.config.channel[..] {
+        "stable" |
+        "beta" => {
+            build.release.split(".").take(2).collect::<Vec<_>>().join(".")
+        }
+        _ => "master".to_string(),
+    };
+
+    let dst = tmpdir(build).join("cargo");
+    let _ = fs::remove_dir_all(&dst);
+    build.run(Command::new("git")
+                .arg("clone")
+                .arg("--depth").arg("1")
+                .arg("--branch").arg(&branch)
+                .arg("https://github.com/rust-lang/cargo")
+                .current_dir(dst.parent().unwrap()));
+    let sha = output(Command::new("git")
+                .arg("rev-parse")
+                .arg("HEAD")
+                .current_dir(&dst));
+    let sha = sha.trim();
+    println!("\tgot cargo sha: {}", sha);
+
+    let input = format!("https://s3.amazonaws.com/rust-lang-ci/cargo-builds\
+                         /{}/cargo-nightly-{}.tar.gz", sha, target);
+    let output = distdir(build).join(format!("cargo-nightly-{}.tar.gz", target));
+    println!("\tdownloading {}", input);
+    let mut curl = Command::new("curl");
+    curl.arg("-f")
+        .arg("-o").arg(&output)
+        .arg(&input)
+        .arg("--retry").arg("3");
+    build.run(&mut curl);
+}
+
+/// Creates a combined installer for the specified target in the provided stage.
+pub fn extended(build: &Build, stage: u32, target: &str) {
+    println!("Dist extended stage{} ({})", stage, target);
+
+    let dist = distdir(build);
+    let rustc_installer = dist.join(format!("{}-{}.tar.gz",
+                                            pkgname(build, "rustc"),
+                                            target));
+    let cargo_installer = dist.join(format!("cargo-nightly-{}.tar.gz", target));
+    let docs_installer = dist.join(format!("{}-{}.tar.gz",
+                                           pkgname(build, "rust-docs"),
+                                           target));
+    let mingw_installer = dist.join(format!("{}-{}.tar.gz",
+                                            pkgname(build, "rust-mingw"),
+                                            target));
+    let std_installer = dist.join(format!("{}-{}.tar.gz",
+                                          pkgname(build, "rust-std"),
+                                          target));
+
+    let tmp = tmpdir(build);
+    let overlay = tmp.join("extended-overlay");
+    let etc = build.src.join("src/etc/installer");
+    let work = tmp.join("work");
+
+    let _ = fs::remove_dir_all(&overlay);
+    install(&build.src.join("COPYRIGHT"), &overlay, 0o644);
+    install(&build.src.join("LICENSE-APACHE"), &overlay, 0o644);
+    install(&build.src.join("LICENSE-MIT"), &overlay, 0o644);
+    let version = &build.version;
+    t!(t!(File::create(overlay.join("version"))).write_all(version.as_bytes()));
+    install(&etc.join("README.md"), &overlay, 0o644);
+
+    // When rust-std package split from rustc, we needed to ensure that during
+    // upgrades rustc was upgraded before rust-std. To avoid rustc clobbering
+    // the std files during uninstall. To do this ensure that rustc comes
+    // before rust-std in the list below.
+    let mut input_tarballs = format!("{},{},{},{}",
+                                     sanitize_sh(&rustc_installer),
+                                     sanitize_sh(&cargo_installer),
+                                     sanitize_sh(&docs_installer),
+                                     sanitize_sh(&std_installer));
+    if target.contains("pc-windows-gnu") {
+        input_tarballs.push_str(",");
+        input_tarballs.push_str(&sanitize_sh(&mingw_installer));
+    }
+
+    let mut cmd = Command::new("sh");
+    cmd.arg(sanitize_sh(&build.src.join("src/rust-installer/combine-installers.sh")))
+       .arg("--product-name=Rust")
+       .arg("--rel-manifest-dir=rustlib")
+       .arg("--success-message=Rust-is-ready-to-roll.")
+       .arg(format!("--work-dir={}", sanitize_sh(&work)))
+       .arg(format!("--output-dir={}", sanitize_sh(&distdir(build))))
+       .arg(format!("--package-name={}-{}", pkgname(build, "rust"), target))
+       .arg("--legacy-manifest-dirs=rustlib,cargo")
+       .arg(format!("--input-tarballs={}", input_tarballs))
+       .arg(format!("--non-installed-overlay={}", sanitize_sh(&overlay)));
+    build.run(&mut cmd);
+
+    let mut license = String::new();
+    t!(t!(File::open(build.src.join("COPYRIGHT"))).read_to_string(&mut license));
+    license.push_str("\n");
+    t!(t!(File::open(build.src.join("LICENSE-APACHE"))).read_to_string(&mut license));
+    license.push_str("\n");
+    t!(t!(File::open(build.src.join("LICENSE-MIT"))).read_to_string(&mut license));
+
+    let rtf = r"{\rtf1\ansi\deff0{\fonttbl{\f0\fnil\fcharset0 Arial;}}\nowwrap\fs18";
+    let mut rtf = rtf.to_string();
+    rtf.push_str("\n");
+    for line in license.lines() {
+        rtf.push_str(line);
+        rtf.push_str("\\line ");
+    }
+    rtf.push_str("}");
+
+    if target.contains("apple-darwin") {
+        let pkg = tmp.join("pkg");
+        let _ = fs::remove_dir_all(&pkg);
+        t!(fs::create_dir_all(pkg.join("rustc")));
+        t!(fs::create_dir_all(pkg.join("cargo")));
+        t!(fs::create_dir_all(pkg.join("rust-docs")));
+        t!(fs::create_dir_all(pkg.join("rust-std")));
+
+        cp_r(&work.join(&format!("{}-{}", pkgname(build, "rustc"), target)),
+             &pkg.join("rustc"));
+        cp_r(&work.join(&format!("cargo-nightly-{}", target)),
+             &pkg.join("cargo"));
+        cp_r(&work.join(&format!("{}-{}", pkgname(build, "rust-docs"), target)),
+             &pkg.join("rust-docs"));
+        cp_r(&work.join(&format!("{}-{}", pkgname(build, "rust-std"), target)),
+             &pkg.join("rust-std"));
+
+        install(&etc.join("pkg/postinstall"), &pkg.join("rustc"), 0o755);
+        install(&etc.join("pkg/postinstall"), &pkg.join("cargo"), 0o755);
+        install(&etc.join("pkg/postinstall"), &pkg.join("rust-docs"), 0o755);
+        install(&etc.join("pkg/postinstall"), &pkg.join("rust-std"), 0o755);
+
+        let pkgbuild = |component: &str| {
+            let mut cmd = Command::new("pkgbuild");
+            cmd.arg("--identifier").arg(format!("org.rust-lang.{}", component))
+               .arg("--scripts").arg(pkg.join(component))
+               .arg("--nopayload")
+               .arg(pkg.join(component).with_extension("pkg"));
+            build.run(&mut cmd);
+        };
+        pkgbuild("rustc");
+        pkgbuild("cargo");
+        pkgbuild("rust-docs");
+        pkgbuild("rust-std");
+
+        // create an 'uninstall' package
+        install(&etc.join("pkg/postinstall"), &pkg.join("uninstall"), 0o755);
+        pkgbuild("uninstall");
+
+        t!(fs::create_dir_all(pkg.join("res")));
+        t!(t!(File::create(pkg.join("res/LICENSE.txt"))).write_all(license.as_bytes()));
+        install(&etc.join("gfx/rust-logo.png"), &pkg.join("res"), 0o644);
+        let mut cmd = Command::new("productbuild");
+        cmd.arg("--distribution").arg(etc.join("pkg/Distribution.xml"))
+           .arg("--resources").arg(pkg.join("res"))
+           .arg(distdir(build).join(format!("{}-{}.pkg",
+                                             pkgname(build, "rust"),
+                                             target)))
+           .arg("--package-path").arg(&pkg);
+        build.run(&mut cmd);
+    }
+
+    if target.contains("windows") {
+        let exe = tmp.join("exe");
+        let _ = fs::remove_dir_all(&exe);
+        t!(fs::create_dir_all(exe.join("rustc")));
+        t!(fs::create_dir_all(exe.join("cargo")));
+        t!(fs::create_dir_all(exe.join("rust-docs")));
+        t!(fs::create_dir_all(exe.join("rust-std")));
+        cp_r(&work.join(&format!("{}-{}", pkgname(build, "rustc"), target))
+                  .join("rustc"),
+             &exe.join("rustc"));
+        cp_r(&work.join(&format!("cargo-nightly-{}", target))
+                  .join("cargo"),
+             &exe.join("cargo"));
+        cp_r(&work.join(&format!("{}-{}", pkgname(build, "rust-docs"), target))
+                  .join("rust-docs"),
+             &exe.join("rust-docs"));
+        cp_r(&work.join(&format!("{}-{}", pkgname(build, "rust-std"), target))
+                  .join(format!("rust-std-{}", target)),
+             &exe.join("rust-std"));
+
+        t!(fs::remove_file(exe.join("rustc/manifest.in")));
+        t!(fs::remove_file(exe.join("cargo/manifest.in")));
+        t!(fs::remove_file(exe.join("rust-docs/manifest.in")));
+        t!(fs::remove_file(exe.join("rust-std/manifest.in")));
+
+        if target.contains("windows-gnu") {
+            t!(fs::create_dir_all(exe.join("rust-mingw")));
+            cp_r(&work.join(&format!("{}-{}", pkgname(build, "rust-mingw"), target))
+                      .join("rust-mingw"),
+                 &exe.join("rust-mingw"));
+            t!(fs::remove_file(exe.join("rust-mingw/manifest.in")));
+        }
+
+        install(&etc.join("exe/rust.iss"), &exe, 0o644);
+        install(&etc.join("exe/modpath.iss"), &exe, 0o644);
+        install(&etc.join("exe/upgrade.iss"), &exe, 0o644);
+        install(&etc.join("gfx/rust-logo.ico"), &exe, 0o644);
+        t!(t!(File::create(exe.join("LICENSE.txt"))).write_all(license.as_bytes()));
+
+        // Generate exe installer
+        let mut cmd = Command::new("iscc");
+        cmd.arg("rust.iss")
+           .current_dir(&exe);
+        if target.contains("windows-gnu") {
+            cmd.arg("/dMINGW");
+        }
+        add_env(build, &mut cmd, target);
+        build.run(&mut cmd);
+        install(&exe.join(format!("{}-{}.exe", pkgname(build, "rust"), target)),
+                &distdir(build),
+                0o755);
+
+        // Generate msi installer
+        let wix = PathBuf::from(env::var_os("WIX").unwrap());
+        let heat = wix.join("bin/heat.exe");
+        let candle = wix.join("bin/candle.exe");
+        let light = wix.join("bin/light.exe");
+
+        let heat_flags = ["-nologo", "-gg", "-sfrag", "-srd", "-sreg"];
+        build.run(Command::new(&heat)
+                        .current_dir(&exe)
+                        .arg("dir")
+                        .arg("rustc")
+                        .args(&heat_flags)
+                        .arg("-cg").arg("RustcGroup")
+                        .arg("-dr").arg("Rustc")
+                        .arg("-var").arg("var.RustcDir")
+                        .arg("-out").arg(exe.join("RustcGroup.wxs")));
+        build.run(Command::new(&heat)
+                        .current_dir(&exe)
+                        .arg("dir")
+                        .arg("rust-docs")
+                        .args(&heat_flags)
+                        .arg("-cg").arg("DocsGroup")
+                        .arg("-dr").arg("Docs")
+                        .arg("-var").arg("var.DocsDir")
+                        .arg("-out").arg(exe.join("DocsGroup.wxs"))
+                        .arg("-t").arg(etc.join("msi/squash-components.xsl")));
+        build.run(Command::new(&heat)
+                        .current_dir(&exe)
+                        .arg("dir")
+                        .arg("cargo")
+                        .args(&heat_flags)
+                        .arg("-cg").arg("CargoGroup")
+                        .arg("-dr").arg("Cargo")
+                        .arg("-var").arg("var.CargoDir")
+                        .arg("-out").arg(exe.join("CargoGroup.wxs"))
+                        .arg("-t").arg(etc.join("msi/remove-duplicates.xsl")));
+        build.run(Command::new(&heat)
+                        .current_dir(&exe)
+                        .arg("dir")
+                        .arg("rust-std")
+                        .args(&heat_flags)
+                        .arg("-cg").arg("StdGroup")
+                        .arg("-dr").arg("Std")
+                        .arg("-var").arg("var.StdDir")
+                        .arg("-out").arg(exe.join("StdGroup.wxs")));
+        if target.contains("windows-gnu") {
+            build.run(Command::new(&heat)
+                            .current_dir(&exe)
+                            .arg("dir")
+                            .arg("rust-mingw")
+                            .args(&heat_flags)
+                            .arg("-cg").arg("GccGroup")
+                            .arg("-dr").arg("Gcc")
+                            .arg("-var").arg("var.GccDir")
+                            .arg("-out").arg(exe.join("GccGroup.wxs")));
+        }
+
+        let candle = |input: &Path| {
+            let output = exe.join(input.file_stem().unwrap())
+                            .with_extension("wixobj");
+            let arch = if target.contains("x86_64") {"x64"} else {"x86"};
+            let mut cmd = Command::new(&candle);
+            cmd.current_dir(&exe)
+               .arg("-nologo")
+               .arg("-dRustcDir=rustc")
+               .arg("-dDocsDir=rust-docs")
+               .arg("-dCargoDir=cargo")
+               .arg("-dStdDir=rust-std")
+               .arg("-arch").arg(&arch)
+               .arg("-out").arg(&output)
+               .arg(&input);
+            add_env(build, &mut cmd, target);
+
+            if target.contains("windows-gnu") {
+               cmd.arg("-dGccDir=rust-mingw");
+            }
+            build.run(&mut cmd);
+        };
+        candle(&etc.join("msi/rust.wxs"));
+        candle(&etc.join("msi/ui.wxs"));
+        candle(&etc.join("msi/rustwelcomedlg.wxs"));
+        candle("RustcGroup.wxs".as_ref());
+        candle("DocsGroup.wxs".as_ref());
+        candle("CargoGroup.wxs".as_ref());
+        candle("StdGroup.wxs".as_ref());
+
+        if target.contains("windows-gnu") {
+            candle("GccGroup.wxs".as_ref());
+        }
+
+        t!(t!(File::create(exe.join("LICENSE.rtf"))).write_all(rtf.as_bytes()));
+        install(&etc.join("gfx/banner.bmp"), &exe, 0o644);
+        install(&etc.join("gfx/dialogbg.bmp"), &exe, 0o644);
+
+        let filename = format!("{}-{}.msi", pkgname(build, "rust"), target);
+        let mut cmd = Command::new(&light);
+        cmd.arg("-nologo")
+           .arg("-ext").arg("WixUIExtension")
+           .arg("-ext").arg("WixUtilExtension")
+           .arg("-out").arg(distdir(build).join(filename))
+           .arg("rust.wixobj")
+           .arg("ui.wixobj")
+           .arg("rustwelcomedlg.wixobj")
+           .arg("RustcGroup.wixobj")
+           .arg("DocsGroup.wixobj")
+           .arg("CargoGroup.wixobj")
+           .arg("StdGroup.wixobj")
+           .current_dir(&exe);
+
+        if target.contains("windows-gnu") {
+           cmd.arg("GccGroup.wixobj");
+        }
+        // ICE57 wrongly complains about the shortcuts
+        cmd.arg("-sice:ICE57");
+
+        build.run(&mut cmd);
+    }
+}
+
+fn add_env(build: &Build, cmd: &mut Command, target: &str) {
+    let mut parts = build.release_num.split('.');
+    cmd.env("CFG_RELEASE_INFO", &build.version)
+       .env("CFG_RELEASE_NUM", &build.release_num)
+       .env("CFG_RELEASE", &build.release)
+       .env("CFG_PRERELEASE_VERSION", &build.prerelease_version)
+       .env("CFG_VER_MAJOR", parts.next().unwrap())
+       .env("CFG_VER_MINOR", parts.next().unwrap())
+       .env("CFG_VER_PATCH", parts.next().unwrap())
+       .env("CFG_VER_BUILD", "0") // just needed to build
+       .env("CFG_PACKAGE_VERS", package_vers(build))
+       .env("CFG_PACKAGE_NAME", pkgname(build, "rust"))
+       .env("CFG_BUILD", target)
+       .env("CFG_CHANNEL", &build.config.channel);
+
+    if target.contains("windows-gnu") {
+       cmd.env("CFG_MINGW", "1")
+          .env("CFG_ABI", "GNU");
+    } else {
+       cmd.env("CFG_MINGW", "0")
+          .env("CFG_ABI", "MSVC");
+    }
+
+    if target.contains("x86_64") {
+       cmd.env("CFG_PLATFORM", "x64");
+    } else {
+       cmd.env("CFG_PLATFORM", "x86");
+    }
+}
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
index 6b2b6ad5c02..db2fe2db813 100644
--- a/src/bootstrap/lib.rs
+++ b/src/bootstrap/lib.rs
@@ -170,6 +170,8 @@ pub struct Build {
     version: String,
     package_vers: String,
     local_rebuild: bool,
+    release_num: String,
+    prerelease_version: String,
 
     // Probed tools at runtime
     lldb_version: Option<String>,
@@ -271,6 +273,8 @@ impl Build {
             lldb_version: None,
             lldb_python_dir: None,
             is_sudo: is_sudo,
+            release_num: String::new(),
+            prerelease_version: String::new(),
         }
     }
 
diff --git a/src/bootstrap/step.rs b/src/bootstrap/step.rs
index 289621184f0..697b14c6050 100644
--- a/src/bootstrap/step.rs
+++ b/src/bootstrap/step.rs
@@ -618,6 +618,20 @@ pub fn build_rules<'a>(build: &'a Build) -> Rules {
     rules.dist("install", "path/to/nowhere")
          .dep(|s| s.name("default:dist"))
          .run(move |s| install::install(build, s.stage, s.target));
+    rules.dist("dist-cargo", "cargo")
+         .host(true)
+         .only_host_build(true)
+         .run(move |s| dist::cargo(build, s.stage, s.target));
+    rules.dist("dist-extended", "extended")
+         .default(build.config.extended)
+         .host(true)
+         .only_host_build(true)
+         .dep(|d| d.name("dist-std"))
+         .dep(|d| d.name("dist-rustc"))
+         .dep(|d| d.name("dist-mingw"))
+         .dep(|d| d.name("dist-docs"))
+         .dep(|d| d.name("dist-cargo"))
+         .run(move |s| dist::extended(build, s.stage, s.target));
 
     rules.verify();
     return rules;
diff --git a/src/ci/docker/dist-arm-linux/Dockerfile b/src/ci/docker/dist-arm-linux/Dockerfile
index 03ca4c28078..217a724fb9a 100644
--- a/src/ci/docker/dist-arm-linux/Dockerfile
+++ b/src/ci/docker/dist-arm-linux/Dockerfile
@@ -76,5 +76,5 @@ ENV CC_arm_unknown_linux_gnueabi=arm-unknown-linux-gnueabi-gcc \
 ENV HOSTS=arm-unknown-linux-gnueabi
 ENV HOSTS=$HOSTS,arm-unknown-linux-gnueabihf
 
-ENV RUST_CONFIGURE_ARGS --host=$HOSTS
+ENV RUST_CONFIGURE_ARGS --host=$HOSTS --enable-extended
 ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS
diff --git a/src/ci/docker/dist-armv7-aarch64-linux/Dockerfile b/src/ci/docker/dist-armv7-aarch64-linux/Dockerfile
index 93d9e004c8c..f26885bbb53 100644
--- a/src/ci/docker/dist-armv7-aarch64-linux/Dockerfile
+++ b/src/ci/docker/dist-armv7-aarch64-linux/Dockerfile
@@ -77,5 +77,5 @@ ENV CC_aarch64_unknown_linux_gnu=aarch64-unknown-linux-gnueabi-gcc \
 ENV HOSTS=armv7-unknown-linux-gnueabihf
 ENV HOSTS=$HOSTS,aarch64-unknown-linux-gnu
 
-ENV RUST_CONFIGURE_ARGS --host=$HOSTS
+ENV RUST_CONFIGURE_ARGS --host=$HOSTS --enable-extended
 ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS
diff --git a/src/ci/docker/dist-freebsd/Dockerfile b/src/ci/docker/dist-freebsd/Dockerfile
index 4b6db62d389..d824c4041ce 100644
--- a/src/ci/docker/dist-freebsd/Dockerfile
+++ b/src/ci/docker/dist-freebsd/Dockerfile
@@ -38,5 +38,5 @@ ENV \
 ENV HOSTS=x86_64-unknown-freebsd
 ENV HOSTS=$HOSTS,i686-unknown-freebsd
 
-ENV RUST_CONFIGURE_ARGS --host=$HOSTS
+ENV RUST_CONFIGURE_ARGS --host=$HOSTS --enable-extended
 ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS
diff --git a/src/ci/docker/dist-mips-linux/Dockerfile b/src/ci/docker/dist-mips-linux/Dockerfile
index df189c25809..38ee95038f6 100644
--- a/src/ci/docker/dist-mips-linux/Dockerfile
+++ b/src/ci/docker/dist-mips-linux/Dockerfile
@@ -27,5 +27,5 @@ ENTRYPOINT ["/usr/bin/dumb-init", "--"]
 ENV HOSTS=mips-unknown-linux-gnu
 ENV HOSTS=$HOSTS,mipsel-unknown-linux-gnu
 
-ENV RUST_CONFIGURE_ARGS --host=$HOSTS
+ENV RUST_CONFIGURE_ARGS --host=$HOSTS --enable-extended
 ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS
diff --git a/src/ci/docker/dist-mips64-linux/Dockerfile b/src/ci/docker/dist-mips64-linux/Dockerfile
index 9152965caee..c9d89d62874 100644
--- a/src/ci/docker/dist-mips64-linux/Dockerfile
+++ b/src/ci/docker/dist-mips64-linux/Dockerfile
@@ -27,5 +27,5 @@ ENTRYPOINT ["/usr/bin/dumb-init", "--"]
 ENV HOSTS=mips64-unknown-linux-gnuabi64
 ENV HOSTS=$HOSTS,mips64el-unknown-linux-gnuabi64
 
-ENV RUST_CONFIGURE_ARGS --host=$HOSTS
+ENV RUST_CONFIGURE_ARGS --host=$HOSTS --enable-extended
 ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS
diff --git a/src/ci/docker/dist-powerpc-linux/Dockerfile b/src/ci/docker/dist-powerpc-linux/Dockerfile
index d5f9e5269f1..ed4e9a35960 100644
--- a/src/ci/docker/dist-powerpc-linux/Dockerfile
+++ b/src/ci/docker/dist-powerpc-linux/Dockerfile
@@ -25,7 +25,7 @@ ENTRYPOINT ["/usr/bin/dumb-init", "--"]
 
 ENV HOSTS=powerpc-unknown-linux-gnu
 
-ENV RUST_CONFIGURE_ARGS --host=$HOSTS
+ENV RUST_CONFIGURE_ARGS --host=$HOSTS --enable-extended
 ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS
 
 # FIXME(#36150) this will fail the bootstrap. Probably means something bad is
diff --git a/src/ci/docker/dist-powerpc64-linux/Dockerfile b/src/ci/docker/dist-powerpc64-linux/Dockerfile
index 3b222581bd4..6c04048f4dd 100644
--- a/src/ci/docker/dist-powerpc64-linux/Dockerfile
+++ b/src/ci/docker/dist-powerpc64-linux/Dockerfile
@@ -32,5 +32,5 @@ ENV \
 ENV HOSTS=powerpc64-unknown-linux-gnu
 ENV HOSTS=$HOSTS,powerpc64le-unknown-linux-gnu
 
-ENV RUST_CONFIGURE_ARGS --host=$HOSTS
+ENV RUST_CONFIGURE_ARGS --host=$HOSTS --enable-extended
 ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS
diff --git a/src/ci/docker/dist-s390x-linux-netbsd/Dockerfile b/src/ci/docker/dist-s390x-linux-netbsd/Dockerfile
index 7350b11a69f..ec38855fe3a 100644
--- a/src/ci/docker/dist-s390x-linux-netbsd/Dockerfile
+++ b/src/ci/docker/dist-s390x-linux-netbsd/Dockerfile
@@ -36,5 +36,5 @@ ENV \
 ENV HOSTS=x86_64-unknown-netbsd
 ENV HOSTS=$HOSTS,s390x-unknown-linux-gnu
 
-ENV RUST_CONFIGURE_ARGS --host=$HOSTS
+ENV RUST_CONFIGURE_ARGS --host=$HOSTS --enable-extended
 ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS
diff --git a/src/ci/docker/dist-x86-linux/Dockerfile b/src/ci/docker/dist-x86-linux/Dockerfile
index 283a4aaab56..4e4f5dd6f1e 100644
--- a/src/ci/docker/dist-x86-linux/Dockerfile
+++ b/src/ci/docker/dist-x86-linux/Dockerfile
@@ -76,5 +76,5 @@ RUN curl -L https://api.pub.build.mozilla.org/tooltool/sha512/$SCCACHE_DIGEST |
 ENV HOSTS=i686-unknown-linux-gnu
 ENV HOSTS=$HOSTS,x86_64-unknown-linux-gnu
 
-ENV RUST_CONFIGURE_ARGS --host=$HOSTS
+ENV RUST_CONFIGURE_ARGS --host=$HOSTS --enable-extended
 ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS
diff --git a/src/etc/installer/README.md b/src/etc/installer/README.md
new file mode 100644
index 00000000000..dbefe753bee
--- /dev/null
+++ b/src/etc/installer/README.md
@@ -0,0 +1,28 @@
+# The Rust Programming Language
+
+This is a compiler for Rust, including standard libraries, tools and
+documentation. Rust is a systems programming language that is fast,
+memory safe and multithreaded, but does not employ a garbage collector
+or otherwise impose significant runtime overhead.
+
+To install to /usr/local (the default), run the included `install.sh` script:
+
+    $ sudo ./install.sh
+
+To uninstall:
+
+    $ sudo /usr/local/lib/rustlib/uninstall.sh
+
+`install.sh` has a few options, including the possibility to set an installation
+prefix. You can display these options by running:
+
+    $ sudo ./install.sh --help
+
+Read [The Book](http://doc.rust-lang.org/book/index.html) to learn how
+to use Rust.
+
+Rust is primarily distributed under the terms of both the MIT license
+and the Apache License (Version 2.0), with portions covered by various
+BSD-like licenses.
+
+See LICENSE-APACHE, LICENSE-MIT, and COPYRIGHT for details.
diff --git a/src/etc/installer/exe/modpath.iss b/src/etc/installer/exe/modpath.iss
new file mode 100644
index 00000000000..35cc0097035
--- /dev/null
+++ b/src/etc/installer/exe/modpath.iss
@@ -0,0 +1,219 @@
+// ----------------------------------------------------------------------------
+//
+// Inno Setup Ver:	5.4.2
+// Script Version:	1.4.1
+// Author:			Jared Breland <jbreland@legroom.net>
+// Homepage:		http://www.legroom.net/software
+// License:			GNU Lesser General Public License (LGPL), version 3
+//						http://www.gnu.org/licenses/lgpl.html
+//
+// Script Function:
+//	Allow modification of environmental path directly from Inno Setup installers
+//
+// Instructions:
+//	Copy modpath.iss to the same directory as your setup script
+//
+//	Add this statement to your [Setup] section
+//		ChangesEnvironment=true
+//
+//	Add this statement to your [Tasks] section
+//	You can change the Description or Flags
+//	You can change the Name, but it must match the ModPathName setting below
+//		Name: modifypath; Description: &Add application directory to your environmental path; Flags: unchecked
+//
+//	Add the following to the end of your [Code] section
+//	ModPathName defines the name of the task defined above
+//	ModPathType defines whether the 'user' or 'system' path will be modified;
+//		this will default to user if anything other than system is set
+//	setArrayLength must specify the total number of dirs to be added
+//	Result[0] contains first directory, Result[1] contains second, etc.
+//		const
+//			ModPathName = 'modifypath';
+//			ModPathType = 'user';
+//
+//		function ModPathDir(): TArrayOfString;
+//		begin
+//			setArrayLength(Result, 1);
+//			Result[0] := ExpandConstant('{app}');
+//		end;
+//		#include "modpath.iss"
+// ----------------------------------------------------------------------------
+
+procedure ModPath();
+var
+	oldpath:	String;
+	newpath:	String;
+	updatepath:	Boolean;
+	pathArr:	TArrayOfString;
+	aExecFile:	String;
+	aExecArr:	TArrayOfString;
+	i, d:		Integer;
+	pathdir:	TArrayOfString;
+	regroot:	Integer;
+	regpath:	String;
+
+begin
+	// Get constants from main script and adjust behavior accordingly
+	// ModPathType MUST be 'system' or 'user'; force 'user' if invalid
+	if ModPathType = 'system' then begin
+		regroot := HKEY_LOCAL_MACHINE;
+		regpath := 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment';
+	end else begin
+		regroot := HKEY_CURRENT_USER;
+		regpath := 'Environment';
+	end;
+
+	// Get array of new directories and act on each individually
+	pathdir := ModPathDir();
+	for d := 0 to GetArrayLength(pathdir)-1 do begin
+		updatepath := true;
+
+		// Modify WinNT path
+		if UsingWinNT() = true then begin
+
+			// Get current path, split into an array
+			RegQueryStringValue(regroot, regpath, 'Path', oldpath);
+			oldpath := oldpath + ';';
+			i := 0;
+
+			while (Pos(';', oldpath) > 0) do begin
+				SetArrayLength(pathArr, i+1);
+				pathArr[i] := Copy(oldpath, 0, Pos(';', oldpath)-1);
+				oldpath := Copy(oldpath, Pos(';', oldpath)+1, Length(oldpath));
+				i := i + 1;
+
+				// Check if current directory matches app dir
+				if pathdir[d] = pathArr[i-1] then begin
+					// if uninstalling, remove dir from path
+					if IsUninstaller() = true then begin
+						continue;
+					// if installing, flag that dir already exists in path
+					end else begin
+						updatepath := false;
+					end;
+				end;
+
+				// Add current directory to new path
+				if i = 1 then begin
+					newpath := pathArr[i-1];
+				end else begin
+					newpath := newpath + ';' + pathArr[i-1];
+				end;
+			end;
+
+			// Append app dir to path if not already included
+			if (IsUninstaller() = false) AND (updatepath = true) then
+				newpath := newpath + ';' + pathdir[d];
+
+			// Write new path
+			RegWriteStringValue(regroot, regpath, 'Path', newpath);
+
+		// Modify Win9x path
+		end else begin
+
+			// Convert to shortened dirname
+			pathdir[d] := GetShortName(pathdir[d]);
+
+			// If autoexec.bat exists, check if app dir already exists in path
+			aExecFile := 'C:\AUTOEXEC.BAT';
+			if FileExists(aExecFile) then begin
+				LoadStringsFromFile(aExecFile, aExecArr);
+				for i := 0 to GetArrayLength(aExecArr)-1 do begin
+					if IsUninstaller() = false then begin
+						// If app dir already exists while installing, skip add
+						if (Pos(pathdir[d], aExecArr[i]) > 0) then
+							updatepath := false;
+							break;
+					end else begin
+						// If app dir exists and = what we originally set, then delete at uninstall
+						if aExecArr[i] = 'SET PATH=%PATH%;' + pathdir[d] then
+							aExecArr[i] := '';
+					end;
+				end;
+			end;
+
+			// If app dir not found, or autoexec.bat didn't exist, then (create and) append to current path
+			if (IsUninstaller() = false) AND (updatepath = true) then begin
+				SaveStringToFile(aExecFile, #13#10 + 'SET PATH=%PATH%;' + pathdir[d], True);
+
+			// If uninstalling, write the full autoexec out
+			end else begin
+				SaveStringsToFile(aExecFile, aExecArr, False);
+			end;
+		end;
+	end;
+end;
+
+// Split a string into an array using passed delimeter
+procedure Explode(var Dest: TArrayOfString; Text: String; Separator: String);
+var
+	i: Integer;
+begin
+	i := 0;
+	repeat
+		SetArrayLength(Dest, i+1);
+		if Pos(Separator,Text) > 0 then	begin
+			Dest[i] := Copy(Text, 1, Pos(Separator, Text)-1);
+			Text := Copy(Text, Pos(Separator,Text) + Length(Separator), Length(Text));
+			i := i + 1;
+		end else begin
+			 Dest[i] := Text;
+			 Text := '';
+		end;
+	until Length(Text)=0;
+end;
+
+
+procedure ModPathCurStepChanged(CurStep: TSetupStep);
+var
+	taskname:	String;
+begin
+	taskname := ModPathName;
+	if CurStep = ssPostInstall then
+		if IsTaskSelected(taskname) then
+			ModPath();
+end;
+
+procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
+var
+	aSelectedTasks:	TArrayOfString;
+	i:				Integer;
+	taskname:		String;
+	regpath:		String;
+	regstring:		String;
+	appid:			String;
+begin
+	// only run during actual uninstall
+	if CurUninstallStep = usUninstall then begin
+		// get list of selected tasks saved in registry at install time
+		appid := '{#emit SetupSetting("AppId")}';
+		if appid = '' then appid := '{#emit SetupSetting("AppName")}';
+		regpath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\'+appid+'_is1');
+		RegQueryStringValue(HKLM, regpath, 'Inno Setup: Selected Tasks', regstring);
+		if regstring = '' then RegQueryStringValue(HKCU, regpath, 'Inno Setup: Selected Tasks', regstring);
+
+		// check each task; if matches modpath taskname, trigger patch removal
+		if regstring <> '' then begin
+			taskname := ModPathName;
+			Explode(aSelectedTasks, regstring, ',');
+			if GetArrayLength(aSelectedTasks) > 0 then begin
+				for i := 0 to GetArrayLength(aSelectedTasks)-1 do begin
+					if comparetext(aSelectedTasks[i], taskname) = 0 then
+						ModPath();
+				end;
+			end;
+		end;
+	end;
+end;
+
+function NeedRestart(): Boolean;
+var
+	taskname:	String;
+begin
+	taskname := ModPathName;
+	if IsTaskSelected(taskname) and not UsingWinNT() then begin
+		Result := True;
+	end else begin
+		Result := False;
+	end;
+end;
diff --git a/src/etc/installer/exe/rust.iss b/src/etc/installer/exe/rust.iss
new file mode 100644
index 00000000000..a61a19f909a
--- /dev/null
+++ b/src/etc/installer/exe/rust.iss
@@ -0,0 +1,80 @@
+#define CFG_RELEASE_NUM GetEnv("CFG_RELEASE_NUM")
+#define CFG_RELEASE GetEnv("CFG_RELEASE")
+#define CFG_PACKAGE_NAME GetEnv("CFG_PACKAGE_NAME")
+#define CFG_BUILD GetEnv("CFG_BUILD")
+
+[Setup]
+
+SetupIconFile=rust-logo.ico
+AppName=Rust
+AppVersion={#CFG_RELEASE}
+AppCopyright=Copyright (C) 2006-2014 Mozilla Foundation, MIT license
+AppPublisher=Mozilla Foundation
+AppPublisherURL=http://www.rust-lang.org
+VersionInfoVersion={#CFG_RELEASE_NUM}
+LicenseFile=LICENSE.txt
+
+PrivilegesRequired=lowest
+DisableWelcomePage=true
+DisableProgramGroupPage=true
+DisableReadyPage=true
+DisableStartupPrompt=true
+
+OutputDir=.\
+SourceDir=.\
+OutputBaseFilename={#CFG_PACKAGE_NAME}-{#CFG_BUILD}
+DefaultDirName={sd}\Rust
+
+Compression=lzma2/ultra
+InternalCompressLevel=ultra
+SolidCompression=true
+
+ChangesEnvironment=true
+ChangesAssociations=no
+AllowUNCPath=false
+AllowNoIcons=true
+Uninstallable=yes
+
+[Tasks]
+Name: modifypath; Description: &Add {app}\bin to your PATH (recommended)
+
+[Components]
+Name: rust; Description: "Rust compiler and standard crates"; Types: full compact custom; Flags: fixed
+#ifdef MINGW
+Name: gcc; Description: "Linker and platform libraries"; Types: full
+#endif
+Name: docs; Description: "HTML documentation"; Types: full
+Name: cargo; Description: "Cargo, the Rust package manager"; Types: full
+Name: std; Description: "The Rust Standard Library"; Types: full
+
+[Files]
+Source: "rustc/*.*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: rust
+#ifdef MINGW
+Source: "rust-mingw/*.*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: gcc
+#endif
+Source: "rust-docs/*.*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: docs
+Source: "cargo/*.*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: cargo
+Source: "rust-std/*.*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Components: std
+
+[Code]
+const
+	ModPathName = 'modifypath';
+	ModPathType = 'user';
+
+function ModPathDir(): TArrayOfString;
+begin
+	setArrayLength(Result, 1)
+	Result[0] := ExpandConstant('{app}\bin');
+end;
+
+#include "modpath.iss"
+#include "upgrade.iss"
+
+// Both modpath.iss and upgrade.iss want to overload CurStepChanged.
+// This version does the overload then delegates to each.
+
+procedure CurStepChanged(CurStep: TSetupStep);
+begin
+  UpgradeCurStepChanged(CurStep);
+  ModPathCurStepChanged(CurStep);
+end;
diff --git a/src/etc/installer/exe/upgrade.iss b/src/etc/installer/exe/upgrade.iss
new file mode 100644
index 00000000000..29da7c333bb
--- /dev/null
+++ b/src/etc/installer/exe/upgrade.iss
@@ -0,0 +1,61 @@
+// The following code taken from https://stackoverflow.com/questions/2000296/innosetup-how-to-automatically-uninstall-previous-installed-version
+// It performs upgrades by running the uninstaller before the install
+
+/////////////////////////////////////////////////////////////////////
+function GetUninstallString(): String;
+var
+  sUnInstPath: String;
+  sUnInstallString: String;
+begin
+  sUnInstPath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\Rust_is1');
+  sUnInstallString := '';
+  if not RegQueryStringValue(HKLM, sUnInstPath, 'UninstallString', sUnInstallString) then
+    RegQueryStringValue(HKCU, sUnInstPath, 'UninstallString', sUnInstallString);
+  Result := sUnInstallString;
+end;
+
+
+/////////////////////////////////////////////////////////////////////
+function IsUpgrade(): Boolean;
+begin
+  Result := (GetUninstallString() <> '');
+end;
+
+
+/////////////////////////////////////////////////////////////////////
+function UnInstallOldVersion(): Integer;
+var
+  sUnInstallString: String;
+  iResultCode: Integer;
+begin
+// Return Values:
+// 1 - uninstall string is empty
+// 2 - error executing the UnInstallString
+// 3 - successfully executed the UnInstallString
+
+  // default return value
+  Result := 0;
+
+  // get the uninstall string of the old app
+  sUnInstallString := GetUninstallString();
+  if sUnInstallString <> '' then begin
+    sUnInstallString := RemoveQuotes(sUnInstallString);
+    if Exec(sUnInstallString, '/SILENT /NORESTART /SUPPRESSMSGBOXES','', SW_HIDE, ewWaitUntilTerminated, iResultCode) then
+      Result := 3
+    else
+      Result := 2;
+  end else
+    Result := 1;
+end;
+
+/////////////////////////////////////////////////////////////////////
+procedure UpgradeCurStepChanged(CurStep: TSetupStep);
+begin
+  if (CurStep=ssInstall) then
+  begin
+    if (IsUpgrade()) then
+    begin
+      UnInstallOldVersion();
+    end;
+  end;
+end;
diff --git a/src/etc/installer/gfx/banner.bmp b/src/etc/installer/gfx/banner.bmp
new file mode 100644
index 00000000000..b5459a797d2
--- /dev/null
+++ b/src/etc/installer/gfx/banner.bmp
Binary files differdiff --git a/src/etc/installer/gfx/banner.xcf b/src/etc/installer/gfx/banner.xcf
new file mode 100644
index 00000000000..53296518ee2
--- /dev/null
+++ b/src/etc/installer/gfx/banner.xcf
Binary files differdiff --git a/src/etc/installer/gfx/dialogbg.bmp b/src/etc/installer/gfx/dialogbg.bmp
new file mode 100644
index 00000000000..7e4674a4fea
--- /dev/null
+++ b/src/etc/installer/gfx/dialogbg.bmp
Binary files differdiff --git a/src/etc/installer/gfx/dialogbg.xcf b/src/etc/installer/gfx/dialogbg.xcf
new file mode 100644
index 00000000000..49ca4e0c33e
--- /dev/null
+++ b/src/etc/installer/gfx/dialogbg.xcf
Binary files differdiff --git a/src/etc/installer/gfx/rust-logo.ico b/src/etc/installer/gfx/rust-logo.ico
new file mode 100644
index 00000000000..a58225d5acb
--- /dev/null
+++ b/src/etc/installer/gfx/rust-logo.ico
Binary files differdiff --git a/src/etc/installer/gfx/rust-logo.png b/src/etc/installer/gfx/rust-logo.png
new file mode 100644
index 00000000000..2c3de300087
--- /dev/null
+++ b/src/etc/installer/gfx/rust-logo.png
Binary files differdiff --git a/src/etc/installer/msi/remove-duplicates.xsl b/src/etc/installer/msi/remove-duplicates.xsl
new file mode 100644
index 00000000000..05b4c9bcc93
--- /dev/null
+++ b/src/etc/installer/msi/remove-duplicates.xsl
@@ -0,0 +1,24 @@
+<?xml version="1.0" ?>
+<xsl:stylesheet version="1.0"
+        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+        xmlns:wix="http://schemas.microsoft.com/wix/2006/wi">
+    <!-- Copy all attributes and elements to the output. -->
+    <xsl:template match="@*|*">
+        <xsl:copy>
+            <xsl:apply-templates select="@*|*"/>
+        </xsl:copy>
+    </xsl:template>
+    <xsl:output method="xml" indent="yes" />
+
+    <!-- LICENSE* files are installed from rustc dir. -->
+    <xsl:key name="duplicates-cmp-ids" match="wix:Component[./wix:File[contains(@Source, 'LICENSE')]|./wix:File[contains(@Source, 'rust-installer-version')]]" use="@Id" />
+    <xsl:template match="wix:Component[key('duplicates-cmp-ids', @Id)]" />
+    <xsl:template match="wix:ComponentRef[key('duplicates-cmp-ids', @Id)]" />
+
+    <xsl:template match="wix:File[contains(@Source, 'README.md')]">
+        <xsl:copy>
+            <xsl:apply-templates select="@*|*"/>
+            <xsl:attribute name="Name">README-CARGO.md</xsl:attribute>
+        </xsl:copy>
+    </xsl:template>
+</xsl:stylesheet>
diff --git a/src/etc/installer/msi/rust.wxs b/src/etc/installer/msi/rust.wxs
new file mode 100644
index 00000000000..fb076ccb091
--- /dev/null
+++ b/src/etc/installer/msi/rust.wxs
@@ -0,0 +1,279 @@
+<?xml version="1.0"?>
+<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
+
+    <?if $(sys.BUILDARCH)="x64" ?>
+        <?define ArchSuffix=" 64-bit" ?>
+    <?else?>
+        <?define ArchSuffix="" ?>
+    <?endif?>
+
+    <?if $(env.CFG_CHANNEL)="stable" ?>
+        <?define ProductName="Rust $(env.CFG_VER_MAJOR).$(env.CFG_VER_MINOR) ($(env.CFG_ABI)$(var.ArchSuffix))" ?>
+    <?else?>
+        <?define ProductName="Rust $(env.CFG_CHANNEL) $(env.CFG_VER_MAJOR).$(env.CFG_VER_MINOR) ($(env.CFG_ABI)$(var.ArchSuffix))" ?>
+    <?endif?>
+
+    <?define BaseRegKey="Software\[Manufacturer]\Rust $(env.CFG_CHANNEL) ($(env.CFG_ABI)$(var.ArchSuffix))\$(env.CFG_VER_MAJOR).$(env.CFG_VER_MINOR)" ?>
+
+    <!-- Upgrade code should be different for each platform -->
+    <?if $(sys.BUILDARCH)="x64" ?>
+        <?if $(env.CFG_ABI)="GNU" ?>
+            <!-- UpgradeCode shoud stay the same for all MSI versions in channel -->
+            <?if $(env.CFG_CHANNEL)="stable" ?>
+                <?define UpgradeCode="B440B077-F8D1-4730-8E1D-D6D37702B4CE" ?>
+            <?elseif $(env.CFG_CHANNEL)="beta" ?>
+                <?define UpgradeCode="7205CEDC-CDA6-4B62-8E4E-4D19EC5D88FC" ?>
+            <?elseif $(env.CFG_CHANNEL)="nightly" ?>
+                <?define UpgradeCode="622497D9-E0B1-448E-838A-4A33D0C5F82C" ?>
+            <?elseif $(env.CFG_CHANNEL)="dev" ?>
+                <?define UpgradeCode="7D32FD99-BB26-45CF-935D-1B0593BBDDBE" ?>
+            <?endif ?>
+        <?elseif $(env.CFG_ABI)="MSVC" ?>
+            <?if $(env.CFG_CHANNEL)="stable" ?>
+                <?define UpgradeCode="123039F9-68E3-44F1-AC9F-C78ADD4D0723" ?>
+            <?elseif $(env.CFG_CHANNEL)="beta" ?>
+                <?define UpgradeCode="ABC640B9-2AB5-4270-9A0D-E54E502A1CCA" ?>
+            <?elseif $(env.CFG_CHANNEL)="nightly" ?>
+                <?define UpgradeCode="56263F12-4AA1-4FE1-AFAE-572915C4FA3E" ?>
+            <?elseif $(env.CFG_CHANNEL)="dev" ?>
+                <?define UpgradeCode="231A9544-7E39-4A60-A069-0EB3CA4BAB2E" ?>
+            <?endif ?>
+        <?endif ?>
+        <?define PlatformProgramFilesFolder="ProgramFiles64Folder" ?>
+    <?elseif $(sys.BUILDARCH)="x86" ?>
+        <?if $(env.CFG_ABI)="GNU" ?>
+            <?if $(env.CFG_CHANNEL)="stable" ?>
+                <?define UpgradeCode="1C7CADA5-D117-43F8-A356-DF15F9FBEFF6" ?>
+            <?elseif $(env.CFG_CHANNEL)="beta" ?>
+                <?define UpgradeCode="5229EAC1-AB7C-4A62-9881-6FAD2DE7D0F9" ?>
+            <?elseif $(env.CFG_CHANNEL)="nightly" ?>
+                <?define UpgradeCode="B94FF1C2-2C7B-4859-A08B-546815516FDA" ?>
+            <?elseif $(env.CFG_CHANNEL)="dev" ?>
+                <?define UpgradeCode="7E6D1349-2773-4792-B8CD-EA2685D86A99" ?>
+            <?endif ?>
+        <?elseif $(env.CFG_ABI)="MSVC" ?>
+            <?if $(env.CFG_CHANNEL)="stable" ?>
+                <?define UpgradeCode="5805719C-45E9-4CF6-9CE7-1E8B57F3C243" ?>
+            <?elseif $(env.CFG_CHANNEL)="beta" ?>
+                <?define UpgradeCode="BC0731C1-BED1-424C-BE99-3589C35C84DE" ?>
+            <?elseif $(env.CFG_CHANNEL)="nightly" ?>
+                <?define UpgradeCode="FF193BBC-E73B-4FBD-ADE0-12F3CFC84145" ?>
+            <?elseif $(env.CFG_CHANNEL)="dev" ?>
+                <?define UpgradeCode="87DFC303-6492-4E9B-911E-56EAD56C5E58" ?>
+            <?endif ?>
+        <?endif ?>
+        <?define PlatformProgramFilesFolder="ProgramFilesFolder" ?>
+    <?else ?>
+        <?error Unsupported value of sys.BUILDARCH=$(sys.BUILDARCH)?>
+    <?endif ?>
+
+    <Product Id="*"
+        Name="$(var.ProductName)"
+        Language="1033"
+        Version="$(env.CFG_VER_MAJOR).$(env.CFG_VER_MINOR).$(env.CFG_VER_PATCH).$(env.CFG_VER_BUILD)"
+        UpgradeCode="$(var.UpgradeCode)"
+        Manufacturer="The Rust Project Developers">
+        <Package
+            Comments="Rust is a systems programming language that runs blazingly fast, prevents almost all crashes, and eliminates data races."
+            InstallerVersion="200"
+            InstallPrivileges="elevated"
+            Compressed="yes" />
+
+        <Icon Id="rust.ico" SourceFile="rust-logo.ico" />
+        <Property Id="ApplicationFolderName" Value="Rust $(env.CFG_CHANNEL) $(env.CFG_ABI) $(env.CFG_VER_MAJOR).$(env.CFG_VER_MINOR)" />
+        <Property Id="WixAppFolder" Value="WixPerMachineFolder" />
+        <Property Id="ARPPRODUCTICON" Value="rust.ico" />
+        <Property Id="ARPURLINFOABOUT" Value="https://www.rust-lang.org/" />
+        <Property Id="ARPCOMMENTS" Value="$(env.CFG_RELEASE_INFO)" />
+        <!-- This is a dual-mode package. http://msdn.microsoft.com/en-us/library/windows/desktop/dd408068.aspx -->
+        <Property Id="ALLUSERS" Value="2" Secure="yes" />
+        <Property Id="MSIINSTALLPERUSER" Secure="yes" />
+        <!-- The actual install location (initialized below) -->
+        <Property Id="INSTALLDIR" Secure="yes" />
+
+        <!-- Detect path(s) of a previous installation. -->
+        <Property Id="INSTALLDIR_USER">
+            <RegistrySearch Id="InstallDir_User" Type="raw" Root="HKCU" Key="$(var.BaseRegKey)" Name="InstallDir" />
+        </Property>
+        <Property Id="INSTALLDIR_MACHINE">
+            <RegistrySearch Id="InstallDir_Mach" Type="raw" Root="HKLM" Key="$(var.BaseRegKey)" Name="InstallDir" />
+        </Property>
+
+        <!-- Set ALLUSERS to match the previous installation mode, otherwise FindRelatedProducts will ignore
+             the previous installation. If both INSTALLDIR_USER and INSTALLDIR_MACHINE are set, prefer the former. -->
+        <SetProperty Sequence="first" Before="FindRelatedProducts"
+            Id="ALLUSERS" Value="{}">INSTALLDIR_USER</SetProperty>
+
+        <!-- Set default values if RegSearch found nothing, or if we not upgrading -->
+        <SetProperty Sequence="both" Before="SetINSTALLDIR1"
+            Id="INSTALLDIR_USER" Value="[LocalAppDataFolder]Programs\[ApplicationFolderName]">NOT INSTALLDIR_USER</SetProperty>
+        <SetProperty Sequence="both" Before="SetINSTALLDIR1"
+            Id="INSTALLDIR_MACHINE" Value="[$(var.PlatformProgramFilesFolder)][ApplicationFolderName]">NOT INSTALLDIR_MACHINE</SetProperty>
+
+        <!-- Choose the default install location according to ALLUSERS (unless set from the command line) -->
+        <SetProperty Sequence="both" Action="SetINSTALLDIR1" Before="SetINSTALLDIR2"
+            Id="INSTALLDIR" Value="[INSTALLDIR_USER]">NOT INSTALLDIR AND NOT ALLUSERS</SetProperty>
+        <SetProperty Sequence="both" Action="SetINSTALLDIR2" Before="CostFinalize"
+            Id="INSTALLDIR" Value="[INSTALLDIR_MACHINE]">NOT INSTALLDIR AND ALLUSERS</SetProperty>
+
+        <SetProperty Sequence="ui" Before="CostFinalize"
+            Id="WixAppFolder" Value="WixPerUserFolder">NOT ALLUSERS</SetProperty>
+
+        <!-- UI sets ALLUSERS per user selection; progagate this choice to MSIINSTALLPERUSER before executing installation actions -->
+        <SetProperty Sequence="ui" Before="ExecuteAction"
+            Id="MSIINSTALLPERUSER" Value="1">NOT ALLUSERS</SetProperty>
+
+        <!-- Update ARPINSTALLLOCATION to match INSTALLDIR -->
+        <SetProperty Sequence="execute" Before="CostFinalize"
+            Id="ARPINSTALLLOCATION" Value="[INSTALLDIR]" />
+
+        <!-- Path of cmd.exe for the shortcut -->
+        <Property Id="SHORTCUTTARGET" Value="%windir%\System32\cmd.exe" />
+        <!-- Microsoft Installer will resolve any Enviroment Variables in the working directory at install time -->
+        <Property Id="SHORTCUTWKDIR" Value="%SystemDrive%\" />
+
+        <InstallUISequence>
+            <FindRelatedProducts After="AppSearch" />
+        </InstallUISequence>
+        <InstallExecuteSequence>
+            <FindRelatedProducts After="AppSearch" />
+            <RemoveExistingProducts Before="InstallInitialize" />
+        </InstallExecuteSequence>
+
+        <Upgrade Id="$(var.UpgradeCode)">
+            <UpgradeVersion
+                Minimum="$(env.CFG_VER_MAJOR).$(env.CFG_VER_MINOR).0"
+                Maximum="$(env.CFG_VER_MAJOR).$(env.CFG_VER_MINOR).65535"
+                IncludeMinimum="yes"
+                IncludeMaximum="yes"
+                MigrateFeatures="yes"
+                Property="UPGRADE_DETECTED"
+                />
+        </Upgrade>
+
+        <!-- Specifies a single cab file to be embedded in the installer's .msi. -->
+        <MediaTemplate EmbedCab="yes" CompressionLevel="high" />
+
+        <!-- Send a WM_SETTINGCHANGE message to tell processes like explorer to update their
+             environments so any new command prompts get the updated %PATH% -->
+        <CustomActionRef Id="WixBroadcastEnvironmentChange" />
+
+        <!-- Installation directory and files are defined in Files.wxs -->
+        <Directory Id="TARGETDIR" Name="SourceDir">
+            <Directory Id="$(var.PlatformProgramFilesFolder)">
+                <Directory Id="INSTALLDIR" Name="Rust">
+                    <!-- Root directories for every feature should have different IDs for correct work of heat.exe -->
+                    <Directory Id="Rustc" Name="." />
+                    <?if $(env.CFG_MINGW)="1" ?>
+                        <Directory Id="Gcc" Name="." />
+                    <?endif?>
+                    <Directory Id="Docs" Name="." />
+                    <Directory Id="Cargo" Name="." />
+                    <Directory Id="Std" Name="." />
+                </Directory>
+            </Directory>
+
+            <!-- Record our install location -->
+            <Component Id="InstallDir" Guid="*">
+                <RegistryValue Root="HKMU" Key="$(var.BaseRegKey)"
+                               Type="string"
+                               Name="InstallDir"
+                               Value="[INSTALLDIR]" />
+            </Component>
+
+            <!-- Add $/bin to PATH -->
+            <Component Id="PathEnvPerMachine" Guid="*">
+                <Condition>ALLUSERS=1 OR (ALLUSERS=2 AND Privileged)</Condition>
+                <RegistryValue Root="HKMU" Key="$(var.BaseRegKey)" Name="PathEnvPerMachine" Type="string" Value="1" KeyPath="yes" />
+                <!-- [INSTALLDIR] contains trailing backslash -->
+                <Environment Id="PathPerMachine" Name="PATH" Value="[INSTALLDIR]bin" Permanent="no" Part="last" Action="set" System="yes" />
+            </Component>
+            <Component Id="PathEnvPerUser" Guid="*">
+                <Condition>ALLUSERS="" OR (ALLUSERS=2 AND (NOT Privileged))</Condition>
+                <RegistryValue Root="HKMU" Key="$(var.BaseRegKey)" Name="PathEnvPerUser" Type="string" Value="1" KeyPath="yes" />
+                <Environment Id="PathPerUser" Name="PATH" Value="[INSTALLDIR]bin" Permanent="no" Part="last" Action="set" System="no" />
+            </Component>
+
+            <!-- Start Menu shortcuts -->
+            <Directory Id="ProgramMenuFolder">
+                <Directory Id="ApplicationProgramsFolder" Name="Rust">
+                    <Component Id="RustShellShortcut" Guid="*">
+                        <Shortcut Id="RustShell"
+                                  Name="$(var.ProductName) Shell"
+                                  Description="Opens Command Prompt with Rust tools directory added to the PATH"
+                                  Target="[SHORTCUTTARGET]"
+                                  Arguments="/K path [INSTALLDIR]bin;%PATH%"
+                                  WorkingDirectory="SHORTCUTWKDIR">
+                            <Icon Id="rust2.ico" SourceFile="rust-logo.ico" />
+                        </Shortcut>
+                        <RegistryValue Root="HKMU" Key="$(var.BaseRegKey)" Name="RustShell" Type="integer" Value="1" KeyPath="yes" />
+                        <RemoveFolder Id="ApplicationProgramsFolder1" On="uninstall" />
+                    </Component>
+                    <Component Id="DocIndexShortcut" Guid="*">
+                        <Shortcut Id="RustDocs"
+                                  Name="$(var.ProductName) Documentation"
+                                  Description="Opens Rust HTML documentation in the default browser"
+                                  Target="[INSTALLDIR]share\doc\rust\html\index.html" />
+                        <RegistryValue Root="HKMU" Key="$(var.BaseRegKey)" Name="RustDocs" Type="integer" Value="1" KeyPath="yes" />
+                        <RemoveFolder Id="ApplicationProgramsFolder2" On="uninstall" />
+                    </Component>
+                </Directory>
+            </Directory>
+
+        </Directory>
+
+        <Feature Id="Rustc"
+                 Title="Rust compiler and standard crates"
+                 Display="1"
+                 Level="1"
+                 Absent="disallow"
+                 AllowAdvertise="no">
+                 <ComponentGroupRef Id="RustcGroup" />
+                 <ComponentRef Id="RustShellShortcut" />
+                 <ComponentRef Id="InstallDir" />
+        </Feature>
+        <Feature Id="Std"
+                 Title="The Rust standard library"
+                 Display="2"
+                 Level="1"
+                 AllowAdvertise="no">
+                 <ComponentGroupRef Id="StdGroup" />
+        </Feature>
+        <Feature Id="Cargo"
+                 Title="Cargo, the Rust package manager"
+                 Display="3"
+                 Level="1"
+                 AllowAdvertise="no">
+                 <ComponentGroupRef Id="CargoGroup" />
+        </Feature>
+        <?if $(env.CFG_MINGW)="1" ?>
+            <Feature Id="Gcc"
+                     Title="Linker and platform libraries"
+                     Description="If you choose to not install this component, you will require an external MinGW installation in order to create executables and libraries."
+                     Display="4"
+                     Level="1"
+                     AllowAdvertise="no">
+                     <ComponentGroupRef Id="GccGroup" />
+            </Feature>
+        <?endif?>
+        <Feature Id="Docs"
+                 Title="HTML documentation"
+                 Display="5"
+                 Level="1"
+                 AllowAdvertise="no">
+                 <ComponentGroupRef Id="DocsGroup" />
+                 <ComponentRef Id="DocIndexShortcut" />
+        </Feature>
+        <Feature Id="Path"
+                 Title="Add to PATH"
+                 Description="Add Rust to PATH environment variable"
+                 Display="6"
+                 Level="1"
+                 AllowAdvertise="no">
+                 <ComponentRef Id="PathEnvPerMachine" />
+                 <ComponentRef Id="PathEnvPerUser" />
+        </Feature>
+
+        <UIRef Id="RustUI" />
+    </Product>
+</Wix>
diff --git a/src/etc/installer/msi/rustwelcomedlg.wxs b/src/etc/installer/msi/rustwelcomedlg.wxs
new file mode 100644
index 00000000000..0ee5415ffba
--- /dev/null
+++ b/src/etc/installer/msi/rustwelcomedlg.wxs
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
+    <Fragment>
+        <UI>
+            <Dialog Id="RustWelcomeDlg" Width="370" Height="270" Title="!(loc.AdvancedWelcomeEulaDlg_Title)">
+                <Control Id="BannerBitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="44" TabSkip="no" Text="!(loc.AdvancedWelcomeEulaDlgBannerBitmap)" />
+                <Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" />
+                <Control Id="Title" Type="Text" X="20" Y="10" Width="300" Height="24" Transparent="yes" NoPrefix="yes" Text="!(loc.AdvancedWelcomeEulaDlgTitle)" />
+                <Control Id="DescriptionPerMachine" Type="Text" X="20" Y="182" Width="330" Height="31" Transparent="yes" NoPrefix="yes" Hidden="yes" Text="!(loc.AdvancedWelcomeEulaDlgDescriptionPerMachine)">
+                    <Condition Action="show">NOT UPGRADE_DETECTED AND ALLUSERS</Condition>
+                </Control>
+                <Control Id="DescriptionPerUser" Type="Text" X="20" Y="182" Width="330" Height="31" Transparent="yes" NoPrefix="yes" Hidden="yes" Text="!(loc.AdvancedWelcomeEulaDlgDescriptionPerUser)">
+                    <Condition Action="show">NOT UPGRADE_DETECTED AND NOT ALLUSERS</Condition>
+                </Control>
+                <Control Id="DescriptionUpgrade" Type="Text" X="20" Y="182" Width="330" Height="31" Transparent="yes" NoPrefix="yes" Hidden="yes"
+                    Text="Click Install to upgrade the existing version of [ProductName].  Click Advanced to change installation options.">
+                    <Condition Action="show">UPGRADE_DETECTED</Condition>
+                </Control>
+                <Control Id="TargetPath" Type="Text" X="20" Y="212" Width="330" Height="31" Transparent="yes" NoPrefix="yes" Hidden="no"
+                    Text="Install Directory: [INSTALLDIR]">
+                </Control>
+                <Control Id="Print" Type="PushButton" X="88" Y="243" Width="56" Height="17" Text="!(loc.WixUIPrint)">
+                    <Publish Event="DoAction" Value="WixUIPrintEula">1</Publish>
+                </Control>
+                <Control Id="Advanced" Type="PushButton" X="156" Y="243" Width="56" Height="17" Text="!(loc.AdvancedWelcomeEulaDlgAdvanced)" />
+                <Control Id="Install" Type="PushButton" ElevationShield="yes" X="212" Y="243" Width="80" Height="17" Default="yes" Text="!(loc.AdvancedWelcomeEulaDlgInstall)" Hidden="yes">
+                    <Publish Event="SpawnWaitDialog" Value="WaitForCostingDlg">!(wix.WixUICostingPopupOptOut) OR CostingComplete = 1</Publish>
+                    <Publish Event="EndDialog" Value="Return"><![CDATA[OutOfDiskSpace <> 1]]></Publish>
+                    <Publish Event="SpawnDialog" Value="OutOfRbDiskDlg">OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND (PROMPTROLLBACKCOST="P" OR NOT PROMPTROLLBACKCOST)</Publish>
+                    <Publish Event="EndDialog" Value="Return">OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND PROMPTROLLBACKCOST="D"</Publish>
+                    <Publish Event="EnableRollback" Value="False">OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND PROMPTROLLBACKCOST="D"</Publish>
+                    <Publish Event="SpawnDialog" Value="OutOfDiskDlg">(OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 1) OR (OutOfDiskSpace = 1 AND PROMPTROLLBACKCOST="F")</Publish>
+                    <Condition Action="show">ALLUSERS</Condition>
+                </Control>
+                <Control Id="InstallNoShield" Type="PushButton" ElevationShield="no" X="212" Y="243" Width="80" Height="17" Default="yes" Text="!(loc.AdvancedWelcomeEulaDlgInstall)" Hidden="yes">
+                    <Publish Event="SpawnWaitDialog" Value="WaitForCostingDlg">!(wix.WixUICostingPopupOptOut) OR CostingComplete = 1</Publish>
+                    <Publish Event="EndDialog" Value="Return"><![CDATA[OutOfDiskSpace <> 1]]></Publish>
+                    <Publish Event="SpawnDialog" Value="OutOfRbDiskDlg">OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND (PROMPTROLLBACKCOST="P" OR NOT PROMPTROLLBACKCOST)</Publish>
+                    <Publish Event="EndDialog" Value="Return">OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND PROMPTROLLBACKCOST="D"</Publish>
+                    <Publish Event="EnableRollback" Value="False">OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND PROMPTROLLBACKCOST="D"</Publish>
+                    <Publish Event="SpawnDialog" Value="OutOfDiskDlg">(OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 1) OR (OutOfDiskSpace = 1 AND PROMPTROLLBACKCOST="F")</Publish>
+                    <Condition Action="show">NOT ALLUSERS</Condition>
+                </Control>
+                <Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Cancel="yes" Text="!(loc.WixUICancel)">
+                    <Publish Event="SpawnDialog" Value="CancelDlg">1</Publish>
+                </Control>
+                <Control Id="LicenseText" Type="ScrollableText" X="20" Y="55" Width="330" Height="121" Sunken="yes" TabSkip="no">
+                    <Text SourceFile="!(wix.WixUILicenseRtf)" />
+                </Control>
+            </Dialog>
+        </UI>
+
+        <InstallUISequence>
+            <Show Dialog="RustWelcomeDlg" Before="ProgressDlg">NOT Installed</Show>
+        </InstallUISequence>
+    </Fragment>
+</Wix>
diff --git a/src/etc/installer/msi/squash-components.xsl b/src/etc/installer/msi/squash-components.xsl
new file mode 100644
index 00000000000..17b4e0388f9
--- /dev/null
+++ b/src/etc/installer/msi/squash-components.xsl
@@ -0,0 +1,34 @@
+<?xml version="1.0" ?>
+<xsl:stylesheet version="1.0"
+        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+        xmlns:wix="http://schemas.microsoft.com/wix/2006/wi">
+    <!-- Copy all attributes and elements to the output. -->
+    <xsl:template match="@*|*">
+        <xsl:copy>
+            <xsl:apply-templates select="@*|*"/>
+        </xsl:copy>
+    </xsl:template>
+    <xsl:output method="xml" indent="yes" />
+
+    <!-- Move all files in directory into first component in that directory. -->
+    <xsl:template match="wix:Component[1]">
+        <xsl:copy>
+            <xsl:apply-templates select="@*|*"/>
+            <xsl:for-each select="../wix:Component[preceding-sibling::*]/wix:File">
+                <xsl:copy>
+                    <!-- Component can only have one KeyPath -->
+                    <xsl:apply-templates select="@*[not(name()='KeyPath')]|*"/>
+                </xsl:copy>
+            </xsl:for-each>
+        </xsl:copy>
+    </xsl:template>
+
+    <!-- Now the rest of components are empty, find them. -->
+    <xsl:key name="empty-cmp-ids" match="wix:Component[preceding-sibling::*]" use="@Id" />
+
+    <!-- And remove. -->
+    <xsl:template match="wix:Component[preceding-sibling::*]" />
+
+    <!-- Also remove componentsrefs referencing empty components. -->
+    <xsl:template match="wix:ComponentRef[key('empty-cmp-ids', @Id)]" />
+</xsl:stylesheet>
diff --git a/src/etc/installer/msi/ui.wxs b/src/etc/installer/msi/ui.wxs
new file mode 100644
index 00000000000..3e2db6051bc
--- /dev/null
+++ b/src/etc/installer/msi/ui.wxs
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Based on WixUI_Advanced
+-->
+
+<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
+    <Fragment>
+        <WixVariable Id="WixUISupportPerUser" Value="1" Overridable="yes" />
+        <WixVariable Id="WixUISupportPerMachine" Value="1" Overridable="yes" />
+        <WixVariable Id="WixUILicenseRtf" Value="LICENSE.rtf" />
+        <WixVariable Id="WixUIDialogBmp" Value="dialogbg.bmp" />
+        <WixVariable Id="WixUIBannerBmp" Value="banner.bmp" />
+
+        <UI Id="RustUI">
+            <TextStyle Id="WixUI_Font_Normal" FaceName="!(loc.Advanced_Font_FaceName)" Size="!(loc.Advanced_Font_Normal_Size)" />
+            <TextStyle Id="WixUI_Font_Bigger" FaceName="!(loc.Advanced_Font_FaceName)" Size="!(loc.Advanced_Font_Bigger_Size)" />
+            <TextStyle Id="WixUI_Font_Title" FaceName="!(loc.Advanced_Font_FaceName)" Size="!(loc.Advanced_Font_Title_Size)" Bold="yes" />
+            <TextStyle Id="WixUI_Font_Emphasized" FaceName="!(loc.Advanced_Font_FaceName)" Size="!(loc.Advanced_Font_Emphasized_Size)" Bold="yes" />
+
+            <Property Id="DefaultUIFont" Value="WixUI_Font_Normal" />
+            <Property Id="WixUI_Mode" Value="Advanced" />
+
+            <DialogRef Id="BrowseDlg" />
+            <DialogRef Id="DiskCostDlg" />
+            <DialogRef Id="ErrorDlg" />
+            <DialogRef Id="FatalError" />
+            <DialogRef Id="FilesInUse" />
+            <DialogRef Id="MsiRMFilesInUse" />
+            <DialogRef Id="PrepareDlg" />
+            <DialogRef Id="ProgressDlg" />
+            <DialogRef Id="ResumeDlg" />
+            <DialogRef Id="UserExit" />
+            <DialogRef Id="WelcomeDlg"/>
+
+            <Publish Dialog="ExitDialog" Control="Finish" Event="EndDialog" Value="Return" Order="999">1</Publish>
+
+            <Publish Dialog="BrowseDlg" Control="OK" Event="DoAction" Value="WixUIValidatePath" Order="1">1</Publish>
+            <Publish Dialog="BrowseDlg" Control="OK" Event="SpawnDialog" Value="InvalidDirDlg" Order="2"><![CDATA[WIXUI_INSTALLDIR_VALID<>"1"]]></Publish>
+
+            <Publish Dialog="RustWelcomeDlg" Control="Advanced" Event="NewDialog" Value="InstallScopeDlg" Order="1" />
+
+            <Publish Dialog="InstallScopeDlg" Control="Back" Event="NewDialog" Value="RustWelcomeDlg">1</Publish>
+            <!-- override default WixAppFolder of WixPerMachineFolder as standard user won't be shown the radio group to set WixAppFolder -->
+            <Publish Dialog="InstallScopeDlg" Control="Next" Property="WixAppFolder" Value="WixPerUserFolder" Order="1">!(wix.WixUISupportPerUser) AND NOT Privileged</Publish>
+            <Publish Dialog="InstallScopeDlg" Control="Next" Property="ALLUSERS" Value="{}" Order="2">WixAppFolder = "WixPerUserFolder"</Publish>
+            <Publish Dialog="InstallScopeDlg" Control="Next" Property="ALLUSERS" Value="1" Order="3">WixAppFolder = "WixPerMachineFolder"</Publish>
+            <Publish Dialog="InstallScopeDlg" Control="Next" Property="INSTALLDIR" Value="[INSTALLDIR_USER]" Order="4">WixAppFolder = "WixPerUserFolder"</Publish>
+            <Publish Dialog="InstallScopeDlg" Control="Next" Property="INSTALLDIR" Value="[INSTALLDIR_MACHINE]" Order="5">WixAppFolder = "WixPerMachineFolder"</Publish>
+            <Publish Dialog="InstallScopeDlg" Control="Next" Event="NewDialog" Value="InstallDirDlg" Order="6">1</Publish>
+
+            <Publish Dialog="InstallDirDlg" Control="Back" Event="NewDialog" Value="InstallScopeDlg" />
+            <Publish Dialog="InstallDirDlg" Control="Next" Event="SetTargetPath" Value="[WIXUI_INSTALLDIR]" Order="1">1</Publish>
+            <Publish Dialog="InstallDirDlg" Control="Next" Event="DoAction" Value="WixUIValidatePath" Order="2">NOT WIXUI_DONTVALIDATEPATH</Publish>
+            <Publish Dialog="InstallDirDlg" Control="Next" Event="SpawnDialog" Value="InvalidDirDlg" Order="3"><![CDATA[NOT WIXUI_DONTVALIDATEPATH AND WIXUI_INSTALLDIR_VALID<>"1"]]></Publish>
+            <Publish Dialog="InstallDirDlg" Control="Next" Event="NewDialog" Value="FeaturesDlg" Order="4">WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID="1"</Publish>
+            <Publish Dialog="InstallDirDlg" Control="ChangeFolder" Property="_BrowseProperty" Value="[WIXUI_INSTALLDIR]" Order="1">1</Publish>
+            <Publish Dialog="InstallDirDlg" Control="ChangeFolder" Event="SpawnDialog" Value="BrowseDlg" Order="2">1</Publish>
+
+            <Publish Dialog="FeaturesDlg" Control="Back" Event="NewDialog" Value="InstallDirDlg">NOT Installed AND WixAppFolder = "WixPerUserFolder"</Publish>
+            <Publish Dialog="FeaturesDlg" Control="Back" Event="NewDialog" Value="InstallDirDlg">NOT Installed AND WixAppFolder = "WixPerMachineFolder"</Publish>
+            <Publish Dialog="FeaturesDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg">Installed</Publish>
+
+            <Publish Dialog="MaintenanceWelcomeDlg" Control="Next" Event="NewDialog" Value="MaintenanceTypeDlg">1</Publish>
+
+            <Publish Dialog="MaintenanceTypeDlg" Control="ChangeButton" Event="NewDialog" Value="FeaturesDlg">1</Publish>
+            <Publish Dialog="MaintenanceTypeDlg" Control="RepairButton" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
+            <Publish Dialog="MaintenanceTypeDlg" Control="RemoveButton" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
+            <Publish Dialog="MaintenanceTypeDlg" Control="Back" Event="NewDialog" Value="MaintenanceWelcomeDlg">1</Publish>
+
+            <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg" Order="2">Installed AND NOT PATCH</Publish>
+            <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="3">Installed AND PATCH</Publish>
+
+            <Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg">Installed AND PATCH</Publish>
+        </UI>
+
+        <InstallUISequence>
+            <Show Dialog="WelcomeDlg" Before="RustWelcomeDlg" >Installed AND PATCH</Show>
+        </InstallUISequence>
+
+        <Property Id="WIXUI_INSTALLDIR" Value="INSTALLDIR" />
+        <UIRef Id="WixUI_Common" />
+    </Fragment>
+</Wix>
diff --git a/src/etc/installer/pkg/Distribution.xml b/src/etc/installer/pkg/Distribution.xml
new file mode 100644
index 00000000000..79f99818ba3
--- /dev/null
+++ b/src/etc/installer/pkg/Distribution.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<installer-gui-script minSpecVersion="2">
+    <title>The Rust Compiler</title>
+    <license file="LICENSE.txt" mime-type="text/plain"/>
+    <pkg-ref id="org.rust-lang.rust"/>
+    <options customize="always" require-scripts="false" hostArchitectures="i386,x86_64"/>
+    <domains enable_anywhere="false" enable_currentUserHome="false" enable_localSystem="true" />
+    <volume-check>
+        <allowed-os-versions>
+            <os-version min="10.7"/>
+        </allowed-os-versions>
+    </volume-check>
+    <choices-outline>
+      <line choice="install">
+	<line choice="rustc"/>
+    <line choice="rust-std"/>
+	<line choice="cargo"/>
+	<line choice="rust-docs"/>
+      </line>
+      <line choice="uninstall" />
+    </choices-outline>
+    <!--
+	These 'selected' scripts ensure that install and uninstall can never be selected at
+	the same time. Exectly how they work is pretty mysterious, tied to the unspecified algorithm
+	the installer uses to traverse the options after one is toggled.
+      -->
+    <choice id="install" visible="true"
+	    title="Install Rust" description="Install the Rust compiler, package manager and documentation."
+	    customLocation="/usr/local"
+	    selected="!choices.uninstall.selected"
+	    />
+    <choice id="uninstall" visible="true"
+	    title="Uninstall Rust" description="Select this option to uninstall an existing Rust installation."
+	    customLocation="/usr/local"
+	    selected="!(choices.install.selected || choices.rustc.selected || choices.cargo.selected || choices['rust-docs'].selected)"
+	    start_selected="false"
+	    >
+        <pkg-ref id="org.rust-lang.uninstall" />
+    </choice>
+    <choice id="rustc" visible="true"
+	    title="Compiler" description="rustc, the Rust compiler, and rustdoc, the API documentation tool."
+	    selected="(!choices.uninstall.selected &amp;&amp; choices.rustc.selected) || (choices.uninstall.selected &amp;&amp; choices.install.selected)"
+	    >
+        <pkg-ref id="org.rust-lang.rustc"/>
+    </choice>
+    <choice id="cargo" visible="true"
+	    title="Cargo" description="cargo, the Rust package manager."
+	    selected="(!choices.uninstall.selected &amp;&amp; choices.cargo.selected) || (choices.uninstall.selected &amp;&amp; choices.install.selected)"
+	    >
+        <pkg-ref id="org.rust-lang.cargo"/>
+    </choice>
+    <choice id="rust-std" visible="true"
+	    title="Standard Library" description="The Rust standard library."
+	    selected="(!choices.uninstall.selected &amp;&amp; choices['rust-std'].selected) || (choices.uninstall.selected &amp;&amp; choices.install.selected)"
+	    >
+        <pkg-ref id="org.rust-lang.rust-std"/>
+    </choice>
+    <choice id="rust-docs" visible="true"
+	    title="Documentation" description="HTML documentation."
+	    selected="(!choices.uninstall.selected &amp;&amp; choices['rust-docs'].selected) || (choices.uninstall.selected &amp;&amp; choices.install.selected)"
+	    >
+        <pkg-ref id="org.rust-lang.rust-docs"/>
+    </choice>
+    <pkg-ref id="org.rust-lang.rustc" version="0" onConclusion="none">rustc.pkg</pkg-ref>
+    <pkg-ref id="org.rust-lang.cargo" version="0" onConclusion="none">cargo.pkg</pkg-ref>
+    <pkg-ref id="org.rust-lang.rust-docs" version="0" onConclusion="none">rust-docs.pkg</pkg-ref>
+    <pkg-ref id="org.rust-lang.rust-std" version="0" onConclusion="none">rust-std.pkg</pkg-ref>
+    <pkg-ref id="org.rust-lang.uninstall" version="0" onConclusion="none">uninstall.pkg</pkg-ref>
+    <background file="rust-logo.png" mime-type="image/png"
+                alignment="bottomleft"/>
+</installer-gui-script>
diff --git a/src/etc/installer/pkg/postinstall b/src/etc/installer/pkg/postinstall
new file mode 100755
index 00000000000..fb035a48690
--- /dev/null
+++ b/src/etc/installer/pkg/postinstall
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+source_dir="$(dirname "$0")"
+dest_dir="$2"
+package_id="$INSTALL_PKG_SESSION_ID"
+
+if [ -z "$source_dir" ]; then
+    exit 1
+fi
+if [ -z "$dest_dir" ]; then
+    exit 1
+fi
+if [ -z "$package_id" ]; then
+    exit 1
+fi
+
+if [ "$package_id" = "org.rust-lang.uninstall" ]; then
+    if [ ! -e "$dest_dir/lib/rustlib/uninstall.sh" ]; then
+	exit 1
+    fi
+    sh "$dest_dir/lib/rustlib/uninstall.sh"
+else
+    sh "$source_dir/install.sh" --prefix="$dest_dir"
+fi
+
+exit 0