]> git.lizzy.rs Git - rust.git/blob - src/bootstrap/dist.rs
Rollup merge of #39604 - est31:i128_tests, r=alexcrichton
[rust.git] / src / bootstrap / dist.rs
1 // Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! Implementation of the various distribution aspects of the compiler.
12 //!
13 //! This module is responsible for creating tarballs of the standard library,
14 //! compiler, and documentation. This ends up being what we distribute to
15 //! everyone as well.
16 //!
17 //! No tarball is actually created literally in this file, but rather we shell
18 //! out to `rust-installer` still. This may one day be replaced with bits and
19 //! pieces of `rustup.rs`!
20
21 use std::env;
22 use std::fs::{self, File};
23 use std::io::{Read, Write};
24 use std::path::{PathBuf, Path};
25 use std::process::{Command, Stdio};
26
27 use build_helper::output;
28
29 use {Build, Compiler, Mode};
30 use util::{cp_r, libdir, is_dylib, cp_filtered, copy};
31
32 pub fn package_vers(build: &Build) -> &str {
33     match &build.config.channel[..] {
34         "stable" => &build.release,
35         "beta" => "beta",
36         "nightly" => "nightly",
37         _ => &build.release,
38     }
39 }
40
41 fn pkgname(build: &Build, component: &str) -> String {
42     format!("{}-{}", component, package_vers(build))
43 }
44
45 fn distdir(build: &Build) -> PathBuf {
46     build.out.join("dist")
47 }
48
49 pub fn tmpdir(build: &Build) -> PathBuf {
50     build.out.join("tmp/dist")
51 }
52
53 /// Builds the `rust-docs` installer component.
54 ///
55 /// Slurps up documentation from the `stage`'s `host`.
56 pub fn docs(build: &Build, stage: u32, host: &str) {
57     println!("Dist docs stage{} ({})", stage, host);
58     if !build.config.docs {
59         println!("\tskipping - docs disabled");
60         return
61     }
62
63     let name = pkgname(build, "rust-docs");
64     let image = tmpdir(build).join(format!("{}-{}-image", name, host));
65     let _ = fs::remove_dir_all(&image);
66
67     let dst = image.join("share/doc/rust/html");
68     t!(fs::create_dir_all(&dst));
69     let src = build.out.join(host).join("doc");
70     cp_r(&src, &dst);
71
72     let mut cmd = Command::new("sh");
73     cmd.arg(sanitize_sh(&build.src.join("src/rust-installer/gen-installer.sh")))
74        .arg("--product-name=Rust-Documentation")
75        .arg("--rel-manifest-dir=rustlib")
76        .arg("--success-message=Rust-documentation-is-installed.")
77        .arg(format!("--image-dir={}", sanitize_sh(&image)))
78        .arg(format!("--work-dir={}", sanitize_sh(&tmpdir(build))))
79        .arg(format!("--output-dir={}", sanitize_sh(&distdir(build))))
80        .arg(format!("--package-name={}-{}", name, host))
81        .arg("--component-name=rust-docs")
82        .arg("--legacy-manifest-dirs=rustlib,cargo")
83        .arg("--bulk-dirs=share/doc/rust/html");
84     build.run(&mut cmd);
85     t!(fs::remove_dir_all(&image));
86
87     // As part of this step, *also* copy the docs directory to a directory which
88     // buildbot typically uploads.
89     if host == build.config.build {
90         let dst = distdir(build).join("doc").join(&build.package_vers);
91         t!(fs::create_dir_all(&dst));
92         cp_r(&src, &dst);
93     }
94 }
95
96 /// Build the `rust-mingw` installer component.
97 ///
98 /// This contains all the bits and pieces to run the MinGW Windows targets
99 /// without any extra installed software (e.g. we bundle gcc, libraries, etc).
100 /// Currently just shells out to a python script, but that should be rewritten
101 /// in Rust.
102 pub fn mingw(build: &Build, host: &str) {
103     println!("Dist mingw ({})", host);
104     let name = pkgname(build, "rust-mingw");
105     let image = tmpdir(build).join(format!("{}-{}-image", name, host));
106     let _ = fs::remove_dir_all(&image);
107     t!(fs::create_dir_all(&image));
108
109     // The first argument to the script is a "temporary directory" which is just
110     // thrown away (this contains the runtime DLLs included in the rustc package
111     // above) and the second argument is where to place all the MinGW components
112     // (which is what we want).
113     //
114     // FIXME: this script should be rewritten into Rust
115     let mut cmd = Command::new(build.python());
116     cmd.arg(build.src.join("src/etc/make-win-dist.py"))
117        .arg(tmpdir(build))
118        .arg(&image)
119        .arg(host);
120     build.run(&mut cmd);
121
122     let mut cmd = Command::new("sh");
123     cmd.arg(sanitize_sh(&build.src.join("src/rust-installer/gen-installer.sh")))
124        .arg("--product-name=Rust-MinGW")
125        .arg("--rel-manifest-dir=rustlib")
126        .arg("--success-message=Rust-MinGW-is-installed.")
127        .arg(format!("--image-dir={}", sanitize_sh(&image)))
128        .arg(format!("--work-dir={}", sanitize_sh(&tmpdir(build))))
129        .arg(format!("--output-dir={}", sanitize_sh(&distdir(build))))
130        .arg(format!("--package-name={}-{}", name, host))
131        .arg("--component-name=rust-mingw")
132        .arg("--legacy-manifest-dirs=rustlib,cargo");
133     build.run(&mut cmd);
134     t!(fs::remove_dir_all(&image));
135 }
136
137 /// Creates the `rustc` installer component.
138 pub fn rustc(build: &Build, stage: u32, host: &str) {
139     println!("Dist rustc stage{} ({})", stage, host);
140     let name = pkgname(build, "rustc");
141     let image = tmpdir(build).join(format!("{}-{}-image", name, host));
142     let _ = fs::remove_dir_all(&image);
143     let overlay = tmpdir(build).join(format!("{}-{}-overlay", name, host));
144     let _ = fs::remove_dir_all(&overlay);
145
146     // Prepare the rustc "image", what will actually end up getting installed
147     prepare_image(build, stage, host, &image);
148
149     // Prepare the overlay which is part of the tarball but won't actually be
150     // installed
151     let cp = |file: &str| {
152         install(&build.src.join(file), &overlay, 0o644);
153     };
154     cp("COPYRIGHT");
155     cp("LICENSE-APACHE");
156     cp("LICENSE-MIT");
157     cp("README.md");
158     // tiny morsel of metadata is used by rust-packaging
159     let version = &build.version;
160     t!(t!(File::create(overlay.join("version"))).write_all(version.as_bytes()));
161
162     // On MinGW we've got a few runtime DLL dependencies that we need to
163     // include. The first argument to this script is where to put these DLLs
164     // (the image we're creating), and the second argument is a junk directory
165     // to ignore all other MinGW stuff the script creates.
166     //
167     // On 32-bit MinGW we're always including a DLL which needs some extra
168     // licenses to distribute. On 64-bit MinGW we don't actually distribute
169     // anything requiring us to distribute a license, but it's likely the
170     // install will *also* include the rust-mingw package, which also needs
171     // licenses, so to be safe we just include it here in all MinGW packages.
172     //
173     // FIXME: this script should be rewritten into Rust
174     if host.contains("pc-windows-gnu") {
175         let mut cmd = Command::new(build.python());
176         cmd.arg(build.src.join("src/etc/make-win-dist.py"))
177            .arg(&image)
178            .arg(tmpdir(build))
179            .arg(host);
180         build.run(&mut cmd);
181
182         let dst = image.join("share/doc");
183         t!(fs::create_dir_all(&dst));
184         cp_r(&build.src.join("src/etc/third-party"), &dst);
185     }
186
187     // Finally, wrap everything up in a nice tarball!
188     let mut cmd = Command::new("sh");
189     cmd.arg(sanitize_sh(&build.src.join("src/rust-installer/gen-installer.sh")))
190        .arg("--product-name=Rust")
191        .arg("--rel-manifest-dir=rustlib")
192        .arg("--success-message=Rust-is-ready-to-roll.")
193        .arg(format!("--image-dir={}", sanitize_sh(&image)))
194        .arg(format!("--work-dir={}", sanitize_sh(&tmpdir(build))))
195        .arg(format!("--output-dir={}", sanitize_sh(&distdir(build))))
196        .arg(format!("--non-installed-overlay={}", sanitize_sh(&overlay)))
197        .arg(format!("--package-name={}-{}", name, host))
198        .arg("--component-name=rustc")
199        .arg("--legacy-manifest-dirs=rustlib,cargo");
200     build.run(&mut cmd);
201     t!(fs::remove_dir_all(&image));
202     t!(fs::remove_dir_all(&overlay));
203
204     fn prepare_image(build: &Build, stage: u32, host: &str, image: &Path) {
205         let src = build.sysroot(&Compiler::new(stage, host));
206         let libdir = libdir(host);
207
208         // Copy rustc/rustdoc binaries
209         t!(fs::create_dir_all(image.join("bin")));
210         cp_r(&src.join("bin"), &image.join("bin"));
211
212         // Copy runtime DLLs needed by the compiler
213         if libdir != "bin" {
214             for entry in t!(src.join(libdir).read_dir()).map(|e| t!(e)) {
215                 let name = entry.file_name();
216                 if let Some(s) = name.to_str() {
217                     if is_dylib(s) {
218                         install(&entry.path(), &image.join(libdir), 0o644);
219                     }
220                 }
221             }
222         }
223
224         // Man pages
225         t!(fs::create_dir_all(image.join("share/man/man1")));
226         cp_r(&build.src.join("man"), &image.join("share/man/man1"));
227
228         // Debugger scripts
229         debugger_scripts(build, &image, host);
230
231         // Misc license info
232         let cp = |file: &str| {
233             install(&build.src.join(file), &image.join("share/doc/rust"), 0o644);
234         };
235         cp("COPYRIGHT");
236         cp("LICENSE-APACHE");
237         cp("LICENSE-MIT");
238         cp("README.md");
239     }
240 }
241
242 /// Copies debugger scripts for `host` into the `sysroot` specified.
243 pub fn debugger_scripts(build: &Build,
244                         sysroot: &Path,
245                         host: &str) {
246     let cp_debugger_script = |file: &str| {
247         let dst = sysroot.join("lib/rustlib/etc");
248         t!(fs::create_dir_all(&dst));
249         install(&build.src.join("src/etc/").join(file), &dst, 0o644);
250     };
251     if host.contains("windows-msvc") {
252         // no debugger scripts
253     } else {
254         cp_debugger_script("debugger_pretty_printers_common.py");
255
256         // gdb debugger scripts
257         install(&build.src.join("src/etc/rust-gdb"), &sysroot.join("bin"),
258                 0o755);
259
260         cp_debugger_script("gdb_load_rust_pretty_printers.py");
261         cp_debugger_script("gdb_rust_pretty_printing.py");
262
263         // lldb debugger scripts
264         install(&build.src.join("src/etc/rust-lldb"), &sysroot.join("bin"),
265                 0o755);
266
267         cp_debugger_script("lldb_rust_formatters.py");
268     }
269 }
270
271 /// Creates the `rust-std` installer component as compiled by `compiler` for the
272 /// target `target`.
273 pub fn std(build: &Build, compiler: &Compiler, target: &str) {
274     println!("Dist std stage{} ({} -> {})", compiler.stage, compiler.host,
275              target);
276
277     // The only true set of target libraries came from the build triple, so
278     // let's reduce redundant work by only producing archives from that host.
279     if compiler.host != build.config.build {
280         println!("\tskipping, not a build host");
281         return
282     }
283
284     let name = pkgname(build, "rust-std");
285     let image = tmpdir(build).join(format!("{}-{}-image", name, target));
286     let _ = fs::remove_dir_all(&image);
287
288     let dst = image.join("lib/rustlib").join(target);
289     t!(fs::create_dir_all(&dst));
290     let src = build.sysroot(compiler).join("lib/rustlib");
291     cp_r(&src.join(target), &dst);
292
293     let mut cmd = Command::new("sh");
294     cmd.arg(sanitize_sh(&build.src.join("src/rust-installer/gen-installer.sh")))
295        .arg("--product-name=Rust")
296        .arg("--rel-manifest-dir=rustlib")
297        .arg("--success-message=std-is-standing-at-the-ready.")
298        .arg(format!("--image-dir={}", sanitize_sh(&image)))
299        .arg(format!("--work-dir={}", sanitize_sh(&tmpdir(build))))
300        .arg(format!("--output-dir={}", sanitize_sh(&distdir(build))))
301        .arg(format!("--package-name={}-{}", name, target))
302        .arg(format!("--component-name=rust-std-{}", target))
303        .arg("--legacy-manifest-dirs=rustlib,cargo");
304     build.run(&mut cmd);
305     t!(fs::remove_dir_all(&image));
306 }
307
308 pub fn rust_src_location(build: &Build) -> PathBuf {
309     let plain_name = format!("rustc-{}-src", package_vers(build));
310     distdir(build).join(&format!("{}.tar.gz", plain_name))
311 }
312
313 /// Creates a tarball of save-analysis metadata, if available.
314 pub fn analysis(build: &Build, compiler: &Compiler, target: &str) {
315     println!("Dist analysis");
316
317     if build.config.channel != "nightly" {
318         println!("\tskipping - not on nightly channel");
319         return;
320     }
321     if compiler.host != build.config.build {
322         println!("\tskipping - not a build host");
323         return
324     }
325     if compiler.stage != 2 {
326         println!("\tskipping - not stage2");
327         return
328     }
329
330     // Package save-analysis from stage1 if not doing a full bootstrap, as the
331     // stage2 artifacts is simply copied from stage1 in that case.
332     let compiler = if build.force_use_stage1(compiler, target) {
333         Compiler::new(1, compiler.host)
334     } else {
335         compiler.clone()
336     };
337
338     let name = pkgname(build, "rust-analysis");
339     let image = tmpdir(build).join(format!("{}-{}-image", name, target));
340
341     let src = build.stage_out(&compiler, Mode::Libstd).join(target).join("release").join("deps");
342
343     let image_src = src.join("save-analysis");
344     let dst = image.join("lib/rustlib").join(target).join("analysis");
345     t!(fs::create_dir_all(&dst));
346     cp_r(&image_src, &dst);
347
348     let mut cmd = Command::new("sh");
349     cmd.arg(sanitize_sh(&build.src.join("src/rust-installer/gen-installer.sh")))
350        .arg("--product-name=Rust")
351        .arg("--rel-manifest-dir=rustlib")
352        .arg("--success-message=save-analysis-saved.")
353        .arg(format!("--image-dir={}", sanitize_sh(&image)))
354        .arg(format!("--work-dir={}", sanitize_sh(&tmpdir(build))))
355        .arg(format!("--output-dir={}", sanitize_sh(&distdir(build))))
356        .arg(format!("--package-name={}-{}", name, target))
357        .arg(format!("--component-name=rust-analysis-{}", target))
358        .arg("--legacy-manifest-dirs=rustlib,cargo");
359     build.run(&mut cmd);
360     t!(fs::remove_dir_all(&image));
361 }
362
363 /// Creates the `rust-src` installer component and the plain source tarball
364 pub fn rust_src(build: &Build) {
365     println!("Dist src");
366
367     let name = pkgname(build, "rust-src");
368     let image = tmpdir(build).join(format!("{}-image", name));
369     let _ = fs::remove_dir_all(&image);
370
371     let dst = image.join("lib/rustlib/src");
372     let dst_src = dst.join("rust");
373     t!(fs::create_dir_all(&dst_src));
374
375     // This is the set of root paths which will become part of the source package
376     let src_files = [
377         "COPYRIGHT",
378         "LICENSE-APACHE",
379         "LICENSE-MIT",
380         "CONTRIBUTING.md",
381         "README.md",
382         "RELEASES.md",
383         "configure",
384         "x.py",
385     ];
386     let src_dirs = [
387         "man",
388         "src",
389     ];
390
391     let filter_fn = move |path: &Path| {
392         let spath = match path.to_str() {
393             Some(path) => path,
394             None => return false,
395         };
396         if spath.ends_with("~") || spath.ends_with(".pyc") {
397             return false
398         }
399         if spath.contains("llvm/test") || spath.contains("llvm\\test") {
400             if spath.ends_with(".ll") ||
401                spath.ends_with(".td") ||
402                spath.ends_with(".s") {
403                 return false
404             }
405         }
406
407         // If we're inside the vendor directory then we need to preserve
408         // everything as Cargo's vendoring support tracks all checksums and we
409         // want to be sure we don't accidentally leave out a file.
410         if spath.contains("vendor") {
411             return true
412         }
413
414         let excludes = [
415             "CVS", "RCS", "SCCS", ".git", ".gitignore", ".gitmodules",
416             ".gitattributes", ".cvsignore", ".svn", ".arch-ids", "{arch}",
417             "=RELEASE-ID", "=meta-update", "=update", ".bzr", ".bzrignore",
418             ".bzrtags", ".hg", ".hgignore", ".hgrags", "_darcs",
419         ];
420         !path.iter()
421              .map(|s| s.to_str().unwrap())
422              .any(|s| excludes.contains(&s))
423     };
424
425     // Copy the directories using our filter
426     for item in &src_dirs {
427         let dst = &dst_src.join(item);
428         t!(fs::create_dir(dst));
429         cp_filtered(&build.src.join(item), dst, &filter_fn);
430     }
431     // Copy the files normally
432     for item in &src_files {
433         copy(&build.src.join(item), &dst_src.join(item));
434     }
435
436     // Create source tarball in rust-installer format
437     let mut cmd = Command::new("sh");
438     cmd.arg(sanitize_sh(&build.src.join("src/rust-installer/gen-installer.sh")))
439        .arg("--product-name=Rust")
440        .arg("--rel-manifest-dir=rustlib")
441        .arg("--success-message=Awesome-Source.")
442        .arg(format!("--image-dir={}", sanitize_sh(&image)))
443        .arg(format!("--work-dir={}", sanitize_sh(&tmpdir(build))))
444        .arg(format!("--output-dir={}", sanitize_sh(&distdir(build))))
445        .arg(format!("--package-name={}", name))
446        .arg("--component-name=rust-src")
447        .arg("--legacy-manifest-dirs=rustlib,cargo");
448     build.run(&mut cmd);
449
450     // Rename directory, so that root folder of tarball has the correct name
451     let plain_name = format!("rustc-{}-src", package_vers(build));
452     let plain_dst_src = tmpdir(build).join(&plain_name);
453     let _ = fs::remove_dir_all(&plain_dst_src);
454     t!(fs::create_dir_all(&plain_dst_src));
455     cp_r(&dst_src, &plain_dst_src);
456
457     // Create the version file
458     write_file(&plain_dst_src.join("version"), build.version.as_bytes());
459
460     // Create plain source tarball
461     let mut cmd = Command::new("tar");
462     cmd.arg("-czf").arg(sanitize_sh(&rust_src_location(build)))
463        .arg(&plain_name)
464        .current_dir(tmpdir(build));
465     build.run(&mut cmd);
466
467     t!(fs::remove_dir_all(&image));
468     t!(fs::remove_dir_all(&plain_dst_src));
469 }
470
471 fn install(src: &Path, dstdir: &Path, perms: u32) {
472     let dst = dstdir.join(src.file_name().unwrap());
473     t!(fs::create_dir_all(dstdir));
474     t!(fs::copy(src, &dst));
475     chmod(&dst, perms);
476 }
477
478 #[cfg(unix)]
479 fn chmod(path: &Path, perms: u32) {
480     use std::os::unix::fs::*;
481     t!(fs::set_permissions(path, fs::Permissions::from_mode(perms)));
482 }
483 #[cfg(windows)]
484 fn chmod(_path: &Path, _perms: u32) {}
485
486 // We have to run a few shell scripts, which choke quite a bit on both `\`
487 // characters and on `C:\` paths, so normalize both of them away.
488 pub fn sanitize_sh(path: &Path) -> String {
489     let path = path.to_str().unwrap().replace("\\", "/");
490     return change_drive(&path).unwrap_or(path);
491
492     fn change_drive(s: &str) -> Option<String> {
493         let mut ch = s.chars();
494         let drive = ch.next().unwrap_or('C');
495         if ch.next() != Some(':') {
496             return None
497         }
498         if ch.next() != Some('/') {
499             return None
500         }
501         Some(format!("/{}/{}", drive, &s[drive.len_utf8() + 2..]))
502     }
503 }
504
505 fn write_file(path: &Path, data: &[u8]) {
506     let mut vf = t!(fs::File::create(path));
507     t!(vf.write_all(data));
508 }
509
510 // FIXME(#38531) eventually this should package up a Cargo that we just compiled
511 //               and tested locally, but for now we're downloading cargo
512 //               artifacts from their compiled location.
513 pub fn cargo(build: &Build, stage: u32, target: &str) {
514     println!("Dist cargo stage{} ({})", stage, target);
515
516     let branch = match &build.config.channel[..] {
517         "stable" |
518         "beta" => format!("rust-{}", build.release_num),
519         _ => "master".to_string(),
520     };
521
522     let dst = tmpdir(build).join("cargo");
523     let _ = fs::remove_dir_all(&dst);
524     build.run(Command::new("git")
525                 .arg("clone")
526                 .arg("--depth").arg("1")
527                 .arg("--branch").arg(&branch)
528                 .arg("https://github.com/rust-lang/cargo")
529                 .current_dir(dst.parent().unwrap()));
530     let sha = output(Command::new("git")
531                 .arg("rev-parse")
532                 .arg("HEAD")
533                 .current_dir(&dst));
534     let sha = sha.trim();
535     println!("\tgot cargo sha: {}", sha);
536
537     let input = format!("https://s3.amazonaws.com/rust-lang-ci/cargo-builds\
538                          /{}/cargo-nightly-{}.tar.gz", sha, target);
539     let output = distdir(build).join(format!("cargo-nightly-{}.tar.gz", target));
540     println!("\tdownloading {}", input);
541     let mut curl = Command::new("curl");
542     curl.arg("-f")
543         .arg("-o").arg(&output)
544         .arg(&input)
545         .arg("--retry").arg("3");
546     build.run(&mut curl);
547 }
548
549 /// Creates a combined installer for the specified target in the provided stage.
550 pub fn extended(build: &Build, stage: u32, target: &str) {
551     println!("Dist extended stage{} ({})", stage, target);
552
553     let dist = distdir(build);
554     let rustc_installer = dist.join(format!("{}-{}.tar.gz",
555                                             pkgname(build, "rustc"),
556                                             target));
557     let cargo_installer = dist.join(format!("cargo-nightly-{}.tar.gz", target));
558     let docs_installer = dist.join(format!("{}-{}.tar.gz",
559                                            pkgname(build, "rust-docs"),
560                                            target));
561     let mingw_installer = dist.join(format!("{}-{}.tar.gz",
562                                             pkgname(build, "rust-mingw"),
563                                             target));
564     let std_installer = dist.join(format!("{}-{}.tar.gz",
565                                           pkgname(build, "rust-std"),
566                                           target));
567
568     let tmp = tmpdir(build);
569     let overlay = tmp.join("extended-overlay");
570     let etc = build.src.join("src/etc/installer");
571     let work = tmp.join("work");
572
573     let _ = fs::remove_dir_all(&overlay);
574     install(&build.src.join("COPYRIGHT"), &overlay, 0o644);
575     install(&build.src.join("LICENSE-APACHE"), &overlay, 0o644);
576     install(&build.src.join("LICENSE-MIT"), &overlay, 0o644);
577     let version = &build.version;
578     t!(t!(File::create(overlay.join("version"))).write_all(version.as_bytes()));
579     install(&etc.join("README.md"), &overlay, 0o644);
580
581     // When rust-std package split from rustc, we needed to ensure that during
582     // upgrades rustc was upgraded before rust-std. To avoid rustc clobbering
583     // the std files during uninstall. To do this ensure that rustc comes
584     // before rust-std in the list below.
585     let mut input_tarballs = format!("{},{},{},{}",
586                                      sanitize_sh(&rustc_installer),
587                                      sanitize_sh(&cargo_installer),
588                                      sanitize_sh(&docs_installer),
589                                      sanitize_sh(&std_installer));
590     if target.contains("pc-windows-gnu") {
591         input_tarballs.push_str(",");
592         input_tarballs.push_str(&sanitize_sh(&mingw_installer));
593     }
594
595     let mut cmd = Command::new("sh");
596     cmd.arg(sanitize_sh(&build.src.join("src/rust-installer/combine-installers.sh")))
597        .arg("--product-name=Rust")
598        .arg("--rel-manifest-dir=rustlib")
599        .arg("--success-message=Rust-is-ready-to-roll.")
600        .arg(format!("--work-dir={}", sanitize_sh(&work)))
601        .arg(format!("--output-dir={}", sanitize_sh(&distdir(build))))
602        .arg(format!("--package-name={}-{}", pkgname(build, "rust"), target))
603        .arg("--legacy-manifest-dirs=rustlib,cargo")
604        .arg(format!("--input-tarballs={}", input_tarballs))
605        .arg(format!("--non-installed-overlay={}", sanitize_sh(&overlay)));
606     build.run(&mut cmd);
607
608     let mut license = String::new();
609     t!(t!(File::open(build.src.join("COPYRIGHT"))).read_to_string(&mut license));
610     license.push_str("\n");
611     t!(t!(File::open(build.src.join("LICENSE-APACHE"))).read_to_string(&mut license));
612     license.push_str("\n");
613     t!(t!(File::open(build.src.join("LICENSE-MIT"))).read_to_string(&mut license));
614
615     let rtf = r"{\rtf1\ansi\deff0{\fonttbl{\f0\fnil\fcharset0 Arial;}}\nowwrap\fs18";
616     let mut rtf = rtf.to_string();
617     rtf.push_str("\n");
618     for line in license.lines() {
619         rtf.push_str(line);
620         rtf.push_str("\\line ");
621     }
622     rtf.push_str("}");
623
624     if target.contains("apple-darwin") {
625         let pkg = tmp.join("pkg");
626         let _ = fs::remove_dir_all(&pkg);
627         t!(fs::create_dir_all(pkg.join("rustc")));
628         t!(fs::create_dir_all(pkg.join("cargo")));
629         t!(fs::create_dir_all(pkg.join("rust-docs")));
630         t!(fs::create_dir_all(pkg.join("rust-std")));
631
632         cp_r(&work.join(&format!("{}-{}", pkgname(build, "rustc"), target)),
633              &pkg.join("rustc"));
634         cp_r(&work.join(&format!("cargo-nightly-{}", target)),
635              &pkg.join("cargo"));
636         cp_r(&work.join(&format!("{}-{}", pkgname(build, "rust-docs"), target)),
637              &pkg.join("rust-docs"));
638         cp_r(&work.join(&format!("{}-{}", pkgname(build, "rust-std"), target)),
639              &pkg.join("rust-std"));
640
641         install(&etc.join("pkg/postinstall"), &pkg.join("rustc"), 0o755);
642         install(&etc.join("pkg/postinstall"), &pkg.join("cargo"), 0o755);
643         install(&etc.join("pkg/postinstall"), &pkg.join("rust-docs"), 0o755);
644         install(&etc.join("pkg/postinstall"), &pkg.join("rust-std"), 0o755);
645
646         let pkgbuild = |component: &str| {
647             let mut cmd = Command::new("pkgbuild");
648             cmd.arg("--identifier").arg(format!("org.rust-lang.{}", component))
649                .arg("--scripts").arg(pkg.join(component))
650                .arg("--nopayload")
651                .arg(pkg.join(component).with_extension("pkg"));
652             build.run(&mut cmd);
653         };
654         pkgbuild("rustc");
655         pkgbuild("cargo");
656         pkgbuild("rust-docs");
657         pkgbuild("rust-std");
658
659         // create an 'uninstall' package
660         install(&etc.join("pkg/postinstall"), &pkg.join("uninstall"), 0o755);
661         pkgbuild("uninstall");
662
663         t!(fs::create_dir_all(pkg.join("res")));
664         t!(t!(File::create(pkg.join("res/LICENSE.txt"))).write_all(license.as_bytes()));
665         install(&etc.join("gfx/rust-logo.png"), &pkg.join("res"), 0o644);
666         let mut cmd = Command::new("productbuild");
667         cmd.arg("--distribution").arg(etc.join("pkg/Distribution.xml"))
668            .arg("--resources").arg(pkg.join("res"))
669            .arg(distdir(build).join(format!("{}-{}.pkg",
670                                              pkgname(build, "rust"),
671                                              target)))
672            .arg("--package-path").arg(&pkg);
673         build.run(&mut cmd);
674     }
675
676     if target.contains("windows") {
677         let exe = tmp.join("exe");
678         let _ = fs::remove_dir_all(&exe);
679         t!(fs::create_dir_all(exe.join("rustc")));
680         t!(fs::create_dir_all(exe.join("cargo")));
681         t!(fs::create_dir_all(exe.join("rust-docs")));
682         t!(fs::create_dir_all(exe.join("rust-std")));
683         cp_r(&work.join(&format!("{}-{}", pkgname(build, "rustc"), target))
684                   .join("rustc"),
685              &exe.join("rustc"));
686         cp_r(&work.join(&format!("cargo-nightly-{}", target))
687                   .join("cargo"),
688              &exe.join("cargo"));
689         cp_r(&work.join(&format!("{}-{}", pkgname(build, "rust-docs"), target))
690                   .join("rust-docs"),
691              &exe.join("rust-docs"));
692         cp_r(&work.join(&format!("{}-{}", pkgname(build, "rust-std"), target))
693                   .join(format!("rust-std-{}", target)),
694              &exe.join("rust-std"));
695
696         t!(fs::remove_file(exe.join("rustc/manifest.in")));
697         t!(fs::remove_file(exe.join("cargo/manifest.in")));
698         t!(fs::remove_file(exe.join("rust-docs/manifest.in")));
699         t!(fs::remove_file(exe.join("rust-std/manifest.in")));
700
701         if target.contains("windows-gnu") {
702             t!(fs::create_dir_all(exe.join("rust-mingw")));
703             cp_r(&work.join(&format!("{}-{}", pkgname(build, "rust-mingw"), target))
704                       .join("rust-mingw"),
705                  &exe.join("rust-mingw"));
706             t!(fs::remove_file(exe.join("rust-mingw/manifest.in")));
707         }
708
709         install(&etc.join("exe/rust.iss"), &exe, 0o644);
710         install(&etc.join("exe/modpath.iss"), &exe, 0o644);
711         install(&etc.join("exe/upgrade.iss"), &exe, 0o644);
712         install(&etc.join("gfx/rust-logo.ico"), &exe, 0o644);
713         t!(t!(File::create(exe.join("LICENSE.txt"))).write_all(license.as_bytes()));
714
715         // Generate exe installer
716         let mut cmd = Command::new("iscc");
717         cmd.arg("rust.iss")
718            .current_dir(&exe);
719         if target.contains("windows-gnu") {
720             cmd.arg("/dMINGW");
721         }
722         add_env(build, &mut cmd, target);
723         build.run(&mut cmd);
724         install(&exe.join(format!("{}-{}.exe", pkgname(build, "rust"), target)),
725                 &distdir(build),
726                 0o755);
727
728         // Generate msi installer
729         let wix = PathBuf::from(env::var_os("WIX").unwrap());
730         let heat = wix.join("bin/heat.exe");
731         let candle = wix.join("bin/candle.exe");
732         let light = wix.join("bin/light.exe");
733
734         let heat_flags = ["-nologo", "-gg", "-sfrag", "-srd", "-sreg"];
735         build.run(Command::new(&heat)
736                         .current_dir(&exe)
737                         .arg("dir")
738                         .arg("rustc")
739                         .args(&heat_flags)
740                         .arg("-cg").arg("RustcGroup")
741                         .arg("-dr").arg("Rustc")
742                         .arg("-var").arg("var.RustcDir")
743                         .arg("-out").arg(exe.join("RustcGroup.wxs")));
744         build.run(Command::new(&heat)
745                         .current_dir(&exe)
746                         .arg("dir")
747                         .arg("rust-docs")
748                         .args(&heat_flags)
749                         .arg("-cg").arg("DocsGroup")
750                         .arg("-dr").arg("Docs")
751                         .arg("-var").arg("var.DocsDir")
752                         .arg("-out").arg(exe.join("DocsGroup.wxs"))
753                         .arg("-t").arg(etc.join("msi/squash-components.xsl")));
754         build.run(Command::new(&heat)
755                         .current_dir(&exe)
756                         .arg("dir")
757                         .arg("cargo")
758                         .args(&heat_flags)
759                         .arg("-cg").arg("CargoGroup")
760                         .arg("-dr").arg("Cargo")
761                         .arg("-var").arg("var.CargoDir")
762                         .arg("-out").arg(exe.join("CargoGroup.wxs"))
763                         .arg("-t").arg(etc.join("msi/remove-duplicates.xsl")));
764         build.run(Command::new(&heat)
765                         .current_dir(&exe)
766                         .arg("dir")
767                         .arg("rust-std")
768                         .args(&heat_flags)
769                         .arg("-cg").arg("StdGroup")
770                         .arg("-dr").arg("Std")
771                         .arg("-var").arg("var.StdDir")
772                         .arg("-out").arg(exe.join("StdGroup.wxs")));
773         if target.contains("windows-gnu") {
774             build.run(Command::new(&heat)
775                             .current_dir(&exe)
776                             .arg("dir")
777                             .arg("rust-mingw")
778                             .args(&heat_flags)
779                             .arg("-cg").arg("GccGroup")
780                             .arg("-dr").arg("Gcc")
781                             .arg("-var").arg("var.GccDir")
782                             .arg("-out").arg(exe.join("GccGroup.wxs")));
783         }
784
785         let candle = |input: &Path| {
786             let output = exe.join(input.file_stem().unwrap())
787                             .with_extension("wixobj");
788             let arch = if target.contains("x86_64") {"x64"} else {"x86"};
789             let mut cmd = Command::new(&candle);
790             cmd.current_dir(&exe)
791                .arg("-nologo")
792                .arg("-dRustcDir=rustc")
793                .arg("-dDocsDir=rust-docs")
794                .arg("-dCargoDir=cargo")
795                .arg("-dStdDir=rust-std")
796                .arg("-arch").arg(&arch)
797                .arg("-out").arg(&output)
798                .arg(&input);
799             add_env(build, &mut cmd, target);
800
801             if target.contains("windows-gnu") {
802                cmd.arg("-dGccDir=rust-mingw");
803             }
804             build.run(&mut cmd);
805         };
806         candle(&etc.join("msi/rust.wxs"));
807         candle(&etc.join("msi/ui.wxs"));
808         candle(&etc.join("msi/rustwelcomedlg.wxs"));
809         candle("RustcGroup.wxs".as_ref());
810         candle("DocsGroup.wxs".as_ref());
811         candle("CargoGroup.wxs".as_ref());
812         candle("StdGroup.wxs".as_ref());
813
814         if target.contains("windows-gnu") {
815             candle("GccGroup.wxs".as_ref());
816         }
817
818         t!(t!(File::create(exe.join("LICENSE.rtf"))).write_all(rtf.as_bytes()));
819         install(&etc.join("gfx/banner.bmp"), &exe, 0o644);
820         install(&etc.join("gfx/dialogbg.bmp"), &exe, 0o644);
821
822         let filename = format!("{}-{}.msi", pkgname(build, "rust"), target);
823         let mut cmd = Command::new(&light);
824         cmd.arg("-nologo")
825            .arg("-ext").arg("WixUIExtension")
826            .arg("-ext").arg("WixUtilExtension")
827            .arg("-out").arg(exe.join(&filename))
828            .arg("rust.wixobj")
829            .arg("ui.wixobj")
830            .arg("rustwelcomedlg.wixobj")
831            .arg("RustcGroup.wixobj")
832            .arg("DocsGroup.wixobj")
833            .arg("CargoGroup.wixobj")
834            .arg("StdGroup.wixobj")
835            .current_dir(&exe);
836
837         if target.contains("windows-gnu") {
838            cmd.arg("GccGroup.wixobj");
839         }
840         // ICE57 wrongly complains about the shortcuts
841         cmd.arg("-sice:ICE57");
842
843         build.run(&mut cmd);
844
845         t!(fs::rename(exe.join(&filename), distdir(build).join(&filename)));
846     }
847 }
848
849 fn add_env(build: &Build, cmd: &mut Command, target: &str) {
850     let mut parts = build.release_num.split('.');
851     cmd.env("CFG_RELEASE_INFO", &build.version)
852        .env("CFG_RELEASE_NUM", &build.release_num)
853        .env("CFG_RELEASE", &build.release)
854        .env("CFG_PRERELEASE_VERSION", &build.prerelease_version)
855        .env("CFG_VER_MAJOR", parts.next().unwrap())
856        .env("CFG_VER_MINOR", parts.next().unwrap())
857        .env("CFG_VER_PATCH", parts.next().unwrap())
858        .env("CFG_VER_BUILD", "0") // just needed to build
859        .env("CFG_PACKAGE_VERS", package_vers(build))
860        .env("CFG_PACKAGE_NAME", pkgname(build, "rust"))
861        .env("CFG_BUILD", target)
862        .env("CFG_CHANNEL", &build.config.channel);
863
864     if target.contains("windows-gnu") {
865        cmd.env("CFG_MINGW", "1")
866           .env("CFG_ABI", "GNU");
867     } else {
868        cmd.env("CFG_MINGW", "0")
869           .env("CFG_ABI", "MSVC");
870     }
871
872     if target.contains("x86_64") {
873        cmd.env("CFG_PLATFORM", "x64");
874     } else {
875        cmd.env("CFG_PLATFORM", "x86");
876     }
877 }
878
879 pub fn hash_and_sign(build: &Build) {
880     let compiler = Compiler::new(0, &build.config.build);
881     let mut cmd = build.tool_cmd(&compiler, "build-manifest");
882     let sign = build.config.dist_sign_folder.as_ref().unwrap_or_else(|| {
883         panic!("\n\nfailed to specify `dist.sign-folder` in `config.toml`\n\n")
884     });
885     let addr = build.config.dist_upload_addr.as_ref().unwrap_or_else(|| {
886         panic!("\n\nfailed to specify `dist.upload-addr` in `config.toml`\n\n")
887     });
888     let file = build.config.dist_gpg_password_file.as_ref().unwrap_or_else(|| {
889         panic!("\n\nfailed to specify `dist.gpg-password-file` in `config.toml`\n\n")
890     });
891     let mut pass = String::new();
892     t!(t!(File::open(&file)).read_to_string(&mut pass));
893
894     let today = output(Command::new("date").arg("+%Y-%m-%d"));
895
896     cmd.arg(sign);
897     cmd.arg(distdir(build));
898     cmd.arg(today.trim());
899     cmd.arg(package_vers(build));
900     cmd.arg(addr);
901
902     t!(fs::create_dir_all(distdir(build)));
903
904     let mut child = t!(cmd.stdin(Stdio::piped()).spawn());
905     t!(child.stdin.take().unwrap().write_all(pass.as_bytes()));
906     let status = t!(child.wait());
907     assert!(status.success());
908 }