1 // Copyright 2017 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.
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.
13 extern crate serde_derive
;
15 use std
::collections
::BTreeMap
;
18 use std
::io
::{self, Read, Write}
;
19 use std
::path
::{PathBuf, Path}
;
20 use std
::process
::{Command, Stdio}
;
22 static HOSTS
: &'
static [&'
static str] = &[
23 "aarch64-unknown-linux-gnu",
24 "arm-unknown-linux-gnueabi",
25 "arm-unknown-linux-gnueabihf",
26 "armv7-unknown-linux-gnueabihf",
28 "i686-pc-windows-gnu",
29 "i686-pc-windows-msvc",
30 "i686-unknown-linux-gnu",
31 "mips-unknown-linux-gnu",
32 "mips64-unknown-linux-gnuabi64",
33 "mips64el-unknown-linux-gnuabi64",
34 "mipsel-unknown-linux-gnu",
35 "powerpc-unknown-linux-gnu",
36 "powerpc64-unknown-linux-gnu",
37 "powerpc64le-unknown-linux-gnu",
38 "s390x-unknown-linux-gnu",
39 "x86_64-apple-darwin",
40 "x86_64-pc-windows-gnu",
41 "x86_64-pc-windows-msvc",
42 "x86_64-unknown-freebsd",
43 "x86_64-unknown-linux-gnu",
44 "x86_64-unknown-netbsd",
47 static TARGETS
: &'
static [&'
static str] = &[
49 "aarch64-linux-android",
50 "aarch64-unknown-cloudabi",
51 "aarch64-unknown-fuchsia",
52 "aarch64-unknown-linux-gnu",
53 "aarch64-unknown-linux-musl",
54 "arm-linux-androideabi",
55 "arm-unknown-linux-gnueabi",
56 "arm-unknown-linux-gnueabihf",
57 "arm-unknown-linux-musleabi",
58 "arm-unknown-linux-musleabihf",
59 "armv5te-unknown-linux-gnueabi",
61 "armv7-linux-androideabi",
62 "armv7-unknown-cloudabi-eabihf",
63 "armv7-unknown-linux-gnueabihf",
64 "armv7-unknown-linux-musleabihf",
66 "asmjs-unknown-emscripten",
68 "i586-pc-windows-msvc",
69 "i586-unknown-linux-gnu",
72 "i686-pc-windows-gnu",
73 "i686-pc-windows-msvc",
74 "i686-unknown-cloudabi",
75 "i686-unknown-freebsd",
76 "i686-unknown-linux-gnu",
77 "i686-unknown-linux-musl",
78 "mips-unknown-linux-gnu",
79 "mips-unknown-linux-musl",
80 "mips64-unknown-linux-gnuabi64",
81 "mips64el-unknown-linux-gnuabi64",
82 "mipsel-unknown-linux-gnu",
83 "mipsel-unknown-linux-musl",
84 "powerpc-unknown-linux-gnu",
85 "powerpc64-unknown-linux-gnu",
86 "powerpc64le-unknown-linux-gnu",
87 "s390x-unknown-linux-gnu",
88 "sparc64-unknown-linux-gnu",
89 "sparcv9-sun-solaris",
90 "wasm32-unknown-emscripten",
91 "wasm32-unknown-unknown",
92 "x86_64-apple-darwin",
94 "x86_64-linux-android",
95 "x86_64-pc-windows-gnu",
96 "x86_64-pc-windows-msvc",
97 "x86_64-rumprun-netbsd",
99 "x86_64-unknown-cloudabi",
100 "x86_64-unknown-freebsd",
101 "x86_64-unknown-fuchsia",
102 "x86_64-unknown-linux-gnu",
103 "x86_64-unknown-linux-gnux32",
104 "x86_64-unknown-linux-musl",
105 "x86_64-unknown-netbsd",
106 "x86_64-unknown-redox",
109 static MINGW
: &'
static [&'
static str] = &[
110 "i686-pc-windows-gnu",
111 "x86_64-pc-windows-gnu",
115 #[serde(rename_all = "kebab-case")]
117 manifest_version
: String
,
119 pkg
: BTreeMap
<String
, Package
>,
120 renames
: BTreeMap
<String
, Rename
>
126 git_commit_hash
: Option
<String
>,
127 target
: BTreeMap
<String
, Target
>,
139 hash
: Option
<String
>,
140 xz_url
: Option
<String
>,
141 xz_hash
: Option
<String
>,
142 components
: Option
<Vec
<Component
>>,
143 extensions
: Option
<Vec
<Component
>>,
147 fn unavailable() -> Target
{
167 ($e
:expr
) => (match $e
{
169 Err(e
) => panic
!("{} failed with {}", stringify
!($e
), e
),
174 rust_release
: String
,
175 cargo_release
: String
,
177 rustfmt_release
: String
,
181 gpg_passphrase
: String
,
182 digests
: BTreeMap
<String
, String
>,
186 rust_version
: Option
<String
>,
187 cargo_version
: Option
<String
>,
188 rls_version
: Option
<String
>,
189 rustfmt_version
: Option
<String
>,
191 rust_git_commit_hash
: Option
<String
>,
192 cargo_git_commit_hash
: Option
<String
>,
193 rls_git_commit_hash
: Option
<String
>,
194 rustfmt_git_commit_hash
: Option
<String
>,
198 let mut args
= env
::args().skip(1);
199 let input
= PathBuf
::from(args
.next().unwrap());
200 let output
= PathBuf
::from(args
.next().unwrap());
201 let date
= args
.next().unwrap();
202 let rust_release
= args
.next().unwrap();
203 let cargo_release
= args
.next().unwrap();
204 let rls_release
= args
.next().unwrap();
205 let rustfmt_release
= args
.next().unwrap();
206 let s3_address
= args
.next().unwrap();
207 let mut passphrase
= String
::new();
208 t
!(io
::stdin().read_to_string(&mut passphrase
));
218 gpg_passphrase
: passphrase
,
219 digests
: BTreeMap
::new(),
226 rustfmt_version
: None
,
228 rust_git_commit_hash
: None
,
229 cargo_git_commit_hash
: None
,
230 rls_git_commit_hash
: None
,
231 rustfmt_git_commit_hash
: None
,
236 fn build(&mut self) {
237 self.rust_version
= self.version("rust", "x86_64-unknown-linux-gnu");
238 self.cargo_version
= self.version("cargo", "x86_64-unknown-linux-gnu");
239 self.rls_version
= self.version("rls", "x86_64-unknown-linux-gnu");
240 self.rustfmt_version
= self.version("rustfmt", "x86_64-unknown-linux-gnu");
242 self.rust_git_commit_hash
= self.git_commit_hash("rust", "x86_64-unknown-linux-gnu");
243 self.cargo_git_commit_hash
= self.git_commit_hash("cargo", "x86_64-unknown-linux-gnu");
244 self.rls_git_commit_hash
= self.git_commit_hash("rls", "x86_64-unknown-linux-gnu");
245 self.rustfmt_git_commit_hash
= self.git_commit_hash("rustfmt", "x86_64-unknown-linux-gnu");
247 self.digest_and_sign();
248 let manifest
= self.build_manifest();
249 self.write_channel_files(&self.rust_release
, &manifest
);
251 if self.rust_release
!= "beta" && self.rust_release
!= "nightly" {
252 self.write_channel_files("stable", &manifest
);
256 fn digest_and_sign(&mut self) {
257 for file
in t
!(self.input
.read_dir()).map(|e
| t
!(e
).path()) {
258 let filename
= file
.file_name().unwrap().to_str().unwrap();
259 let digest
= self.hash(&file
);
261 assert
!(self.digests
.insert(filename
.to_string(), digest
).is_none());
265 fn build_manifest(&mut self) -> Manifest
{
266 let mut manifest
= Manifest
{
267 manifest_version
: "2".to_string(),
268 date
: self.date
.to_string(),
269 pkg
: BTreeMap
::new(),
270 renames
: BTreeMap
::new(),
273 self.package("rustc", &mut manifest
.pkg
, HOSTS
);
274 self.package("cargo", &mut manifest
.pkg
, HOSTS
);
275 self.package("rust-mingw", &mut manifest
.pkg
, MINGW
);
276 self.package("rust-std", &mut manifest
.pkg
, TARGETS
);
277 self.package("rust-docs", &mut manifest
.pkg
, TARGETS
);
278 self.package("rust-src", &mut manifest
.pkg
, &["*"]);
279 self.package("rls-preview", &mut manifest
.pkg
, HOSTS
);
280 self.package("rustfmt-preview", &mut manifest
.pkg
, HOSTS
);
281 self.package("rust-analysis", &mut manifest
.pkg
, TARGETS
);
283 let rls_present
= manifest
.pkg
.contains_key("rls-preview");
284 let rustfmt_present
= manifest
.pkg
.contains_key("rustfmt-preview");
287 manifest
.renames
.insert("rls".to_owned(), Rename { to: "rls-preview".to_owned() }
);
290 let mut pkg
= Package
{
291 version
: self.cached_version("rust")
293 .expect("Couldn't find Rust version")
295 git_commit_hash
: self.cached_git_commit_hash("rust").clone(),
296 target
: BTreeMap
::new(),
299 let filename
= self.filename("rust", host
);
300 let digest
= match self.digests
.remove(&filename
) {
301 Some(digest
) => digest
,
303 pkg
.target
.insert(host
.to_string(), Target
::unavailable());
307 let xz_filename
= filename
.replace(".tar.gz", ".tar.xz");
308 let xz_digest
= self.digests
.remove(&xz_filename
);
309 let mut components
= Vec
::new();
310 let mut extensions
= Vec
::new();
312 // rustc/rust-std/cargo/docs are all required, and so is rust-mingw
313 // if it's available for the target.
314 components
.extend(vec
![
315 Component { pkg: "rustc".to_string(), target: host.to_string() }
,
316 Component { pkg: "rust-std".to_string(), target: host.to_string() }
,
317 Component { pkg: "cargo".to_string(), target: host.to_string() }
,
318 Component { pkg: "rust-docs".to_string(), target: host.to_string() }
,
320 if host
.contains("pc-windows-gnu") {
321 components
.push(Component
{
322 pkg
: "rust-mingw".to_string(),
323 target
: host
.to_string(),
328 extensions
.push(Component
{
329 pkg
: "rls-preview".to_string(),
330 target
: host
.to_string(),
334 extensions
.push(Component
{
335 pkg
: "rustfmt-preview".to_string(),
336 target
: host
.to_string(),
339 extensions
.push(Component
{
340 pkg
: "rust-analysis".to_string(),
341 target
: host
.to_string(),
343 for target
in TARGETS
{
345 extensions
.push(Component
{
346 pkg
: "rust-std".to_string(),
347 target
: target
.to_string(),
351 extensions
.push(Component
{
352 pkg
: "rust-src".to_string(),
353 target
: "*".to_string(),
356 pkg
.target
.insert(host
.to_string(), Target
{
358 url
: Some(self.url(&filename
)),
360 xz_url
: xz_digest
.as_ref().map(|_
| self.url(&xz_filename
)),
362 components
: Some(components
),
363 extensions
: Some(extensions
),
366 manifest
.pkg
.insert("rust".to_string(), pkg
);
371 fn package(&mut self,
373 dst
: &mut BTreeMap
<String
, Package
>,
375 let version
= match *self.cached_version(pkgname
) {
376 Some(ref version
) => version
.clone(),
378 println
!("Skipping package {}", pkgname
);
383 let targets
= targets
.iter().map(|name
| {
384 let filename
= self.filename(pkgname
, name
);
385 let digest
= match self.digests
.remove(&filename
) {
386 Some(digest
) => digest
,
387 None
=> return (name
.to_string(), Target
::unavailable()),
389 let xz_filename
= filename
.replace(".tar.gz", ".tar.xz");
390 let xz_digest
= self.digests
.remove(&xz_filename
);
392 (name
.to_string(), Target
{
394 url
: Some(self.url(&filename
)),
396 xz_url
: xz_digest
.as_ref().map(|_
| self.url(&xz_filename
)),
403 dst
.insert(pkgname
.to_string(), Package
{
405 git_commit_hash
: self.cached_git_commit_hash(pkgname
).clone(),
410 fn url(&self, filename
: &str) -> String
{
417 fn filename(&self, component
: &str, target
: &str) -> String
{
418 if component
== "rust-src" {
419 format
!("rust-src-{}.tar.gz", self.rust_release
)
420 } else if component
== "cargo" {
421 format
!("cargo-{}-{}.tar.gz", self.cargo_release
, target
)
422 } else if component
== "rls" || component
== "rls-preview" {
423 format
!("rls-{}-{}.tar.gz", self.rls_release
, target
)
424 } else if component
== "rustfmt" || component
== "rustfmt-preview" {
425 format
!("rustfmt-{}-{}.tar.gz", self.rustfmt_release
, target
)
427 format
!("{}-{}-{}.tar.gz", component
, self.rust_release
, target
)
431 fn cached_version(&self, component
: &str) -> &Option
<String
> {
432 if component
== "cargo" {
434 } else if component
== "rls" || component
== "rls-preview" {
436 } else if component
== "rustfmt" || component
== "rustfmt-preview" {
437 &self.rustfmt_version
443 fn cached_git_commit_hash(&self, component
: &str) -> &Option
<String
> {
444 if component
== "cargo" {
445 &self.cargo_git_commit_hash
446 } else if component
== "rls" || component
== "rls-preview" {
447 &self.rls_git_commit_hash
448 } else if component
== "rustfmt" || component
== "rustfmt-preview" {
449 &self.rustfmt_git_commit_hash
451 &self.rust_git_commit_hash
455 fn version(&self, component
: &str, target
: &str) -> Option
<String
> {
456 let mut cmd
= Command
::new("tar");
457 let filename
= self.filename(component
, target
);
459 .arg(self.input
.join(&filename
))
460 .arg(format
!("{}/version", filename
.replace(".tar.gz", "")))
462 let output
= t
!(cmd
.output());
463 if output
.status
.success() {
464 Some(String
::from_utf8_lossy(&output
.stdout
).trim().to_string())
466 // Perhaps we didn't build this package.
471 fn git_commit_hash(&self, component
: &str, target
: &str) -> Option
<String
> {
472 let mut cmd
= Command
::new("tar");
473 let filename
= self.filename(component
, target
);
475 .arg(self.input
.join(&filename
))
476 .arg(format
!("{}/git-commit-hash", filename
.replace(".tar.gz", "")))
478 let output
= t
!(cmd
.output());
479 if output
.status
.success() {
480 Some(String
::from_utf8_lossy(&output
.stdout
).trim().to_string())
486 fn hash(&self, path
: &Path
) -> String
{
487 let sha
= t
!(Command
::new("shasum")
488 .arg("-a").arg("256")
489 .arg(path
.file_name().unwrap())
490 .current_dir(path
.parent().unwrap())
492 assert
!(sha
.status
.success());
494 let filename
= path
.file_name().unwrap().to_str().unwrap();
495 let sha256
= self.output
.join(format
!("{}.sha256", filename
));
496 t
!(t
!(File
::create(&sha256
)).write_all(&sha
.stdout
));
498 let stdout
= String
::from_utf8_lossy(&sha
.stdout
);
499 stdout
.split_whitespace().next().unwrap().to_string()
502 fn sign(&self, path
: &Path
) {
503 let filename
= path
.file_name().unwrap().to_str().unwrap();
504 let asc
= self.output
.join(format
!("{}.asc", filename
));
505 println
!("signing: {:?}", path
);
506 let mut cmd
= Command
::new("gpg");
509 .arg("--passphrase-fd").arg("0")
510 .arg("--personal-digest-preferences").arg("SHA512")
512 .arg("--output").arg(&asc
)
513 .arg("--detach-sign").arg(path
)
514 .stdin(Stdio
::piped());
515 let mut child
= t
!(cmd
.spawn());
516 t
!(child
.stdin
.take().unwrap().write_all(self.gpg_passphrase
.as_bytes()));
517 assert
!(t
!(child
.wait()).success());
520 fn write_channel_files(&self, channel_name
: &str, manifest
: &Manifest
) {
521 self.write(&toml
::to_string(&manifest
).unwrap(), channel_name
, ".toml");
522 self.write(&manifest
.date
, channel_name
, "-date.txt");
523 self.write(manifest
.pkg
["rust"].git_commit_hash
.as_ref().unwrap(),
524 channel_name
, "-git-commit-hash.txt");
527 fn write(&self, contents
: &str, channel_name
: &str, suffix
: &str) {
528 let dst
= self.output
.join(format
!("channel-rust-{}{}", channel_name
, suffix
));
529 t
!(t
!(File
::create(&dst
)).write_all(contents
.as_bytes()));