rust/src/bootstrap/dist.rs
Alex Crichton 9e8785f017 rustbuild: Add manifest generation in-tree
This commit adds a new tool, `build-manifest`, which is used to generate a
distribution manifest of all produced artifacts. This tool is intended to
replace the `build-rust-manifest.py` script that's currently located on the
buildmaster. The intention is that we'll have a builder which periodically:

* Downloads all artifacts for a commit
* Runs `./x.py dist hash-and-sign`. This will generate `sha256` and `asc` files
  as well as TOML manifests.
* Upload all generated hashes and manifests to the directory the artifacts came
  from.
* Upload *all* artifacts (tarballs and hashes and manifests) to an archived
  location.
* If necessary, upload all artifacts to the main location.

This script is intended to just be the second step here where orchestrating
uploads and such will all happen externally from the build system itself.
2017-01-25 10:57:21 -08:00

909 lines
35 KiB
Rust

// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Implementation of the various distribution aspects of the compiler.
//!
//! This module is responsible for creating tarballs of the standard library,
//! compiler, and documentation. This ends up being what we distribute to
//! everyone as well.
//!
//! No tarball is actually created literally in this file, but rather we shell
//! 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::{Read, Write};
use std::path::{PathBuf, Path};
use std::process::{Command, Stdio};
use build_helper::output;
use {Build, Compiler, Mode};
use util::{cp_r, libdir, is_dylib, cp_filtered, copy};
pub fn package_vers(build: &Build) -> &str {
match &build.config.channel[..] {
"stable" => &build.release,
"beta" => "beta",
"nightly" => "nightly",
_ => &build.release,
}
}
fn pkgname(build: &Build, component: &str) -> String {
format!("{}-{}", component, package_vers(build))
}
fn distdir(build: &Build) -> PathBuf {
build.out.join("dist")
}
pub fn tmpdir(build: &Build) -> PathBuf {
build.out.join("tmp/dist")
}
/// Builds the `rust-docs` installer component.
///
/// Slurps up documentation from the `stage`'s `host`.
pub fn docs(build: &Build, stage: u32, host: &str) {
println!("Dist docs stage{} ({})", stage, host);
if !build.config.docs {
println!("\tskipping - docs disabled");
return
}
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");
t!(fs::create_dir_all(&dst));
let src = build.out.join(host).join("doc");
cp_r(&src, &dst);
let mut cmd = Command::new("sh");
cmd.arg(sanitize_sh(&build.src.join("src/rust-installer/gen-installer.sh")))
.arg("--product-name=Rust-Documentation")
.arg("--rel-manifest-dir=rustlib")
.arg("--success-message=Rust-documentation-is-installed.")
.arg(format!("--image-dir={}", sanitize_sh(&image)))
.arg(format!("--work-dir={}", sanitize_sh(&tmpdir(build))))
.arg(format!("--output-dir={}", sanitize_sh(&distdir(build))))
.arg(format!("--package-name={}-{}", name, host))
.arg("--component-name=rust-docs")
.arg("--legacy-manifest-dirs=rustlib,cargo")
.arg("--bulk-dirs=share/doc/rust/html");
build.run(&mut cmd);
t!(fs::remove_dir_all(&image));
// As part of this step, *also* copy the docs directory to a directory which
// buildbot typically uploads.
if host == build.config.build {
let dst = distdir(build).join("doc").join(&build.package_vers);
t!(fs::create_dir_all(&dst));
cp_r(&src, &dst);
}
}
/// Build the `rust-mingw` installer component.
///
/// This contains all the bits and pieces to run the MinGW Windows targets
/// without any extra installed software (e.g. we bundle gcc, libraries, etc).
/// Currently just shells out to a python script, but that should be rewritten
/// in Rust.
pub fn mingw(build: &Build, host: &str) {
println!("Dist mingw ({})", host);
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));
// The first argument to the script is a "temporary directory" which is just
// thrown away (this contains the runtime DLLs included in the rustc package
// above) and the second argument is where to place all the MinGW components
// (which is what we want).
//
// FIXME: this script should be rewritten into Rust
let mut cmd = Command::new(build.python());
cmd.arg(build.src.join("src/etc/make-win-dist.py"))
.arg(tmpdir(build))
.arg(&image)
.arg(host);
build.run(&mut cmd);
let mut cmd = Command::new("sh");
cmd.arg(sanitize_sh(&build.src.join("src/rust-installer/gen-installer.sh")))
.arg("--product-name=Rust-MinGW")
.arg("--rel-manifest-dir=rustlib")
.arg("--success-message=Rust-MinGW-is-installed.")
.arg(format!("--image-dir={}", sanitize_sh(&image)))
.arg(format!("--work-dir={}", sanitize_sh(&tmpdir(build))))
.arg(format!("--output-dir={}", sanitize_sh(&distdir(build))))
.arg(format!("--package-name={}-{}", name, host))
.arg("--component-name=rust-mingw")
.arg("--legacy-manifest-dirs=rustlib,cargo");
build.run(&mut cmd);
t!(fs::remove_dir_all(&image));
}
/// Creates the `rustc` installer component.
pub fn rustc(build: &Build, stage: u32, host: &str) {
println!("Dist rustc stage{} ({})", stage, host);
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));
let _ = fs::remove_dir_all(&overlay);
// Prepare the rustc "image", what will actually end up getting installed
prepare_image(build, stage, host, &image);
// Prepare the overlay which is part of the tarball but won't actually be
// installed
let cp = |file: &str| {
install(&build.src.join(file), &overlay, 0o644);
};
cp("COPYRIGHT");
cp("LICENSE-APACHE");
cp("LICENSE-MIT");
cp("README.md");
// tiny morsel of metadata is used by rust-packaging
let version = &build.version;
t!(t!(File::create(overlay.join("version"))).write_all(version.as_bytes()));
// On MinGW we've got a few runtime DLL dependencies that we need to
// include. The first argument to this script is where to put these DLLs
// (the image we're creating), and the second argument is a junk directory
// to ignore all other MinGW stuff the script creates.
//
// On 32-bit MinGW we're always including a DLL which needs some extra
// licenses to distribute. On 64-bit MinGW we don't actually distribute
// anything requiring us to distribute a license, but it's likely the
// install will *also* include the rust-mingw package, which also needs
// licenses, so to be safe we just include it here in all MinGW packages.
//
// FIXME: this script should be rewritten into Rust
if host.contains("pc-windows-gnu") {
let mut cmd = Command::new(build.python());
cmd.arg(build.src.join("src/etc/make-win-dist.py"))
.arg(&image)
.arg(tmpdir(build))
.arg(host);
build.run(&mut cmd);
let dst = image.join("share/doc");
t!(fs::create_dir_all(&dst));
cp_r(&build.src.join("src/etc/third-party"), &dst);
}
// Finally, wrap everything up in a nice tarball!
let mut cmd = Command::new("sh");
cmd.arg(sanitize_sh(&build.src.join("src/rust-installer/gen-installer.sh")))
.arg("--product-name=Rust")
.arg("--rel-manifest-dir=rustlib")
.arg("--success-message=Rust-is-ready-to-roll.")
.arg(format!("--image-dir={}", sanitize_sh(&image)))
.arg(format!("--work-dir={}", sanitize_sh(&tmpdir(build))))
.arg(format!("--output-dir={}", sanitize_sh(&distdir(build))))
.arg(format!("--non-installed-overlay={}", sanitize_sh(&overlay)))
.arg(format!("--package-name={}-{}", name, host))
.arg("--component-name=rustc")
.arg("--legacy-manifest-dirs=rustlib,cargo");
build.run(&mut cmd);
t!(fs::remove_dir_all(&image));
t!(fs::remove_dir_all(&overlay));
fn prepare_image(build: &Build, stage: u32, host: &str, image: &Path) {
let src = build.sysroot(&Compiler::new(stage, host));
let libdir = libdir(host);
// Copy rustc/rustdoc binaries
t!(fs::create_dir_all(image.join("bin")));
cp_r(&src.join("bin"), &image.join("bin"));
// Copy runtime DLLs needed by the compiler
if libdir != "bin" {
for entry in t!(src.join(libdir).read_dir()).map(|e| t!(e)) {
let name = entry.file_name();
if let Some(s) = name.to_str() {
if is_dylib(s) {
install(&entry.path(), &image.join(libdir), 0o644);
}
}
}
}
// Man pages
t!(fs::create_dir_all(image.join("share/man/man1")));
cp_r(&build.src.join("man"), &image.join("share/man/man1"));
// Debugger scripts
debugger_scripts(build, &image, host);
// Misc license info
let cp = |file: &str| {
install(&build.src.join(file), &image.join("share/doc/rust"), 0o644);
};
cp("COPYRIGHT");
cp("LICENSE-APACHE");
cp("LICENSE-MIT");
cp("README.md");
}
}
/// Copies debugger scripts for `host` into the `sysroot` specified.
pub fn debugger_scripts(build: &Build,
sysroot: &Path,
host: &str) {
let cp_debugger_script = |file: &str| {
let dst = sysroot.join("lib/rustlib/etc");
t!(fs::create_dir_all(&dst));
install(&build.src.join("src/etc/").join(file), &dst, 0o644);
};
if host.contains("windows-msvc") {
// no debugger scripts
} else {
cp_debugger_script("debugger_pretty_printers_common.py");
// gdb debugger scripts
install(&build.src.join("src/etc/rust-gdb"), &sysroot.join("bin"),
0o755);
cp_debugger_script("gdb_load_rust_pretty_printers.py");
cp_debugger_script("gdb_rust_pretty_printing.py");
// lldb debugger scripts
install(&build.src.join("src/etc/rust-lldb"), &sysroot.join("bin"),
0o755);
cp_debugger_script("lldb_rust_formatters.py");
}
}
/// Creates the `rust-std` installer component as compiled by `compiler` for the
/// target `target`.
pub fn std(build: &Build, compiler: &Compiler, target: &str) {
println!("Dist std stage{} ({} -> {})", compiler.stage, compiler.host,
target);
// The only true set of target libraries came from the build triple, so
// let's reduce redundant work by only producing archives from that host.
if compiler.host != build.config.build {
println!("\tskipping, not a build host");
return
}
let name = pkgname(build, "rust-std");
let image = tmpdir(build).join(format!("{}-{}-image", name, target));
let _ = fs::remove_dir_all(&image);
let dst = image.join("lib/rustlib").join(target);
t!(fs::create_dir_all(&dst));
let src = build.sysroot(compiler).join("lib/rustlib");
cp_r(&src.join(target), &dst);
let mut cmd = Command::new("sh");
cmd.arg(sanitize_sh(&build.src.join("src/rust-installer/gen-installer.sh")))
.arg("--product-name=Rust")
.arg("--rel-manifest-dir=rustlib")
.arg("--success-message=std-is-standing-at-the-ready.")
.arg(format!("--image-dir={}", sanitize_sh(&image)))
.arg(format!("--work-dir={}", sanitize_sh(&tmpdir(build))))
.arg(format!("--output-dir={}", sanitize_sh(&distdir(build))))
.arg(format!("--package-name={}-{}", name, target))
.arg(format!("--component-name=rust-std-{}", target))
.arg("--legacy-manifest-dirs=rustlib,cargo");
build.run(&mut cmd);
t!(fs::remove_dir_all(&image));
}
pub fn rust_src_location(build: &Build) -> PathBuf {
let plain_name = format!("rustc-{}-src", package_vers(build));
distdir(build).join(&format!("{}.tar.gz", plain_name))
}
/// Creates a tarball of save-analysis metadata, if available.
pub fn analysis(build: &Build, compiler: &Compiler, target: &str) {
println!("Dist analysis");
if build.config.channel != "nightly" {
println!("\tskipping - not on nightly channel");
return;
}
if compiler.host != build.config.build {
println!("\tskipping - not a build host");
return
}
if compiler.stage != 2 {
println!("\tskipping - not stage2");
return
}
// Package save-analysis from stage1 if not doing a full bootstrap, as the
// stage2 artifacts is simply copied from stage1 in that case.
let compiler = if build.force_use_stage1(compiler, target) {
Compiler::new(1, compiler.host)
} else {
compiler.clone()
};
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");
let image_src = src.join("save-analysis");
let dst = image.join("lib/rustlib").join(target).join("analysis");
t!(fs::create_dir_all(&dst));
cp_r(&image_src, &dst);
let mut cmd = Command::new("sh");
cmd.arg(sanitize_sh(&build.src.join("src/rust-installer/gen-installer.sh")))
.arg("--product-name=Rust")
.arg("--rel-manifest-dir=rustlib")
.arg("--success-message=save-analysis-saved.")
.arg(format!("--image-dir={}", sanitize_sh(&image)))
.arg(format!("--work-dir={}", sanitize_sh(&tmpdir(build))))
.arg(format!("--output-dir={}", sanitize_sh(&distdir(build))))
.arg(format!("--package-name={}-{}", name, target))
.arg(format!("--component-name=rust-analysis-{}", target))
.arg("--legacy-manifest-dirs=rustlib,cargo");
build.run(&mut cmd);
t!(fs::remove_dir_all(&image));
}
/// Creates the `rust-src` installer component and the plain source tarball
pub fn rust_src(build: &Build) {
println!("Dist src");
let name = pkgname(build, "rust-src");
let image = tmpdir(build).join(format!("{}-image", name));
let _ = fs::remove_dir_all(&image);
let dst = image.join("lib/rustlib/src");
let dst_src = dst.join("rust");
t!(fs::create_dir_all(&dst_src));
// This is the set of root paths which will become part of the source package
let src_files = [
"COPYRIGHT",
"LICENSE-APACHE",
"LICENSE-MIT",
"CONTRIBUTING.md",
"README.md",
"RELEASES.md",
"configure",
"Makefile.in"
];
let src_dirs = [
"man",
"src",
"mk"
];
let filter_fn = move |path: &Path| {
let spath = match path.to_str() {
Some(path) => path,
None => return false,
};
if spath.ends_with("~") || spath.ends_with(".pyc") {
return false
}
if spath.contains("llvm/test") || spath.contains("llvm\\test") {
if spath.ends_with(".ll") ||
spath.ends_with(".td") ||
spath.ends_with(".s") {
return false
}
}
// If we're inside the vendor directory then we need to preserve
// everything as Cargo's vendoring support tracks all checksums and we
// want to be sure we don't accidentally leave out a file.
if spath.contains("vendor") {
return true
}
let excludes = [
"CVS", "RCS", "SCCS", ".git", ".gitignore", ".gitmodules",
".gitattributes", ".cvsignore", ".svn", ".arch-ids", "{arch}",
"=RELEASE-ID", "=meta-update", "=update", ".bzr", ".bzrignore",
".bzrtags", ".hg", ".hgignore", ".hgrags", "_darcs",
];
!path.iter()
.map(|s| s.to_str().unwrap())
.any(|s| excludes.contains(&s))
};
// Copy the directories using our filter
for item in &src_dirs {
let dst = &dst_src.join(item);
t!(fs::create_dir(dst));
cp_filtered(&build.src.join(item), dst, &filter_fn);
}
// Copy the files normally
for item in &src_files {
copy(&build.src.join(item), &dst_src.join(item));
}
// Create source tarball in rust-installer format
let mut cmd = Command::new("sh");
cmd.arg(sanitize_sh(&build.src.join("src/rust-installer/gen-installer.sh")))
.arg("--product-name=Rust")
.arg("--rel-manifest-dir=rustlib")
.arg("--success-message=Awesome-Source.")
.arg(format!("--image-dir={}", sanitize_sh(&image)))
.arg(format!("--work-dir={}", sanitize_sh(&tmpdir(build))))
.arg(format!("--output-dir={}", sanitize_sh(&distdir(build))))
.arg(format!("--package-name={}", name))
.arg("--component-name=rust-src")
.arg("--legacy-manifest-dirs=rustlib,cargo");
build.run(&mut cmd);
// Rename directory, so that root folder of tarball has the correct name
let plain_name = format!("rustc-{}-src", package_vers(build));
let plain_dst_src = tmpdir(build).join(&plain_name);
let _ = fs::remove_dir_all(&plain_dst_src);
t!(fs::create_dir_all(&plain_dst_src));
cp_r(&dst_src, &plain_dst_src);
// Create the version file
write_file(&plain_dst_src.join("version"), build.version.as_bytes());
// Create plain source tarball
let mut cmd = Command::new("tar");
cmd.arg("-czf").arg(sanitize_sh(&rust_src_location(build)))
.arg(&plain_name)
.current_dir(tmpdir(build));
build.run(&mut cmd);
t!(fs::remove_dir_all(&image));
t!(fs::remove_dir_all(&plain_dst_src));
}
fn install(src: &Path, dstdir: &Path, perms: u32) {
let dst = dstdir.join(src.file_name().unwrap());
t!(fs::create_dir_all(dstdir));
t!(fs::copy(src, &dst));
chmod(&dst, perms);
}
#[cfg(unix)]
fn chmod(path: &Path, perms: u32) {
use std::os::unix::fs::*;
t!(fs::set_permissions(path, fs::Permissions::from_mode(perms)));
}
#[cfg(windows)]
fn chmod(_path: &Path, _perms: u32) {}
// We have to run a few shell scripts, which choke quite a bit on both `\`
// characters and on `C:\` paths, so normalize both of them away.
pub fn sanitize_sh(path: &Path) -> String {
let path = path.to_str().unwrap().replace("\\", "/");
return change_drive(&path).unwrap_or(path);
fn change_drive(s: &str) -> Option<String> {
let mut ch = s.chars();
let drive = ch.next().unwrap_or('C');
if ch.next() != Some(':') {
return None
}
if ch.next() != Some('/') {
return None
}
Some(format!("/{}/{}", drive, &s[drive.len_utf8() + 2..]))
}
}
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");
}
}
pub fn hash_and_sign(build: &Build) {
let compiler = Compiler::new(0, &build.config.build);
let mut cmd = build.tool_cmd(&compiler, "build-manifest");
let sign = build.config.dist_sign_folder.as_ref().unwrap_or_else(|| {
panic!("\n\nfailed to specify `dist.sign-folder` in `config.toml`\n\n")
});
let addr = build.config.dist_upload_addr.as_ref().unwrap_or_else(|| {
panic!("\n\nfailed to specify `dist.upload-addr` in `config.toml`\n\n")
});
let file = build.config.dist_gpg_password_file.as_ref().unwrap_or_else(|| {
panic!("\n\nfailed to specify `dist.gpg-password-file` in `config.toml`\n\n")
});
let mut pass = String::new();
t!(t!(File::open(&file)).read_to_string(&mut pass));
let today = output(Command::new("date").arg("+%Y-%m-%d"));
cmd.arg(sign);
cmd.arg(distdir(build));
cmd.arg(today.trim());
cmd.arg(package_vers(build));
cmd.arg(addr);
t!(fs::create_dir_all(distdir(build)));
let mut child = t!(cmd.stdin(Stdio::piped()).spawn());
t!(child.stdin.take().unwrap().write_all(pass.as_bytes()));
let status = t!(child.wait());
assert!(status.success());
}