// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. use std::io::{Read, Write}; use std::process::{self, ExitCode}; use crate::crates; use crate::manifest::CargoManifest; use crate::paths; /// Runs the download subcommand, which downloads a crate from crates.io and unpacks it into the /// Chromium tree. pub fn download( name: &str, version: semver::Version, security: bool, paths: &paths::ChromiumPaths, ) -> ExitCode { let vendored_crate = crates::ChromiumVendoredCrate { name: name.to_string(), epoch: crates::Epoch::from_version(&version), }; let build_path = paths.third_party.join(vendored_crate.build_path()); let crate_path = paths.third_party.join(vendored_crate.crate_path()); let url = format!( "{dir}/{name}/{name}-{version}.{suffix}", dir = CRATES_IO_DOWNLOAD_URL, suffix = CRATES_IO_DOWNLOAD_SUFFIX ); let curl_out = process::Command::new("curl") .arg("--fail") .arg(url.to_string()) .output() .expect("Failed to run curl"); if !curl_out.status.success() { eprintln!("gnrt: {}", String::from_utf8(curl_out.stderr).unwrap()); return ExitCode::FAILURE; } // Makes the directory where the build file will go. The crate's source code will go below it. // This directory and its parents are allowed to exist already. std::fs::create_dir_all(&build_path) .expect("Could not make the third-party directory '{build_path}' for the crate"); // Makes the directory where the source code will be unzipped. It should not exist or we'd be // clobbering existing files. std::fs::create_dir(&crate_path).expect("Crate directory '{crate_path}' already exists"); let mut untar = process::Command::new("tar") // Extract and unzip from stdin. .arg("xzf") .arg("-") // Drop the first path component, which is the crate's name-version. .arg("--strip-components=1") // Unzip into the crate's directory in third_party/rust. .arg(format!("--directory={}", crate_path.display())) // The input is the downloaded file. .stdin(process::Stdio::piped()) .stdout(process::Stdio::piped()) .stderr(process::Stdio::piped()) .spawn() .expect("Failed to run tar"); if untar.stdin.take().unwrap().write_all(&curl_out.stdout).is_err() { eprintln!("gnrt: Failed to pipe input to tar, it exited early"); } if !untar.wait().expect("Failed to wait for tar").success() { let mut stderr_buf = Vec::new(); untar.stderr.unwrap().read_to_end(&mut stderr_buf).expect("Failed to read stderr from tar"); eprintln!("gnrt: {}", String::from_utf8(stderr_buf).unwrap()); return ExitCode::FAILURE; } let cargo: CargoManifest = { let str = std::fs::read_to_string(crate_path.join("Cargo.toml")) .expect("Unable to open downloaded Cargo.toml"); toml::de::from_str(&str).expect("Unable to parse downloaded Cargo.toml") }; let (_, readme_license) = ALLOWED_LICENSES .iter() .find(|(allowed_license, _)| &cargo.package.license == *allowed_license) .expect("License in downloaded Cargo.toml is not in ALLOWED_LICENSES"); let readme = gen_readme_chromium_text(&cargo, readme_license, security); std::fs::write(build_path.join("README.chromium"), readme) .expect("Failed to write README.chromium"); println!("gnrt: Downloaded {name} {version} to {path}", path = crate_path.display()); ExitCode::SUCCESS } /// Generate the contents of the README.chromium file. fn gen_readme_chromium_text(manifest: &CargoManifest, license: &str, security: bool) -> String { format!( "Name: {crate_name}\n\ URL: {url}\n\ Description: {description}\n\ Version: {version}\n\ Security Critical: {security}\n\ License: {license}\n", crate_name = manifest.package.name, url = format!("{}/{}", CRATES_IO_VIEW_URL, manifest.package.name), description = manifest.package.description.as_ref().unwrap_or(&"".to_string()), version = manifest.package.version, ) } static CRATES_IO_DOWNLOAD_URL: &str = "https://static.crates.io/crates"; static CRATES_IO_DOWNLOAD_SUFFIX: &str = "crate"; static CRATES_IO_VIEW_URL: &str = "https://crates.io/crates"; // Allowed licenses, in the format they are specified in Cargo.toml files from // crates.io, and the format to write to README.chromium. static ALLOWED_LICENSES: [(&str, &str); 15] = [ // ("Cargo.toml string", "License for README.chromium") ("Apache-2.0", "Apache 2.0"), ("MIT OR Apache-2.0", "Apache 2.0"), ("MIT/Apache-2.0", "Apache 2.0"), ("Apache-2.0 / MIT", "Apache 2.0"), ("Apache-2.0 OR MIT", "Apache 2.0"), ("Apache-2.0/MIT", "Apache 2.0"), ("MIT", "MIT"), ("Unlicense OR MIT", "MIT"), ("Unlicense/MIT", "MIT"), ("Apache-2.0 OR BSL-1.0", "Apache 2.0"), ("BSD-3-Clause", "BSD 3-Clause"), ("ISC", "ISC"), ("MIT OR Zlib OR Apache-2.0", "Apache 2.0"), ("0BSD OR MIT OR Apache-2.0", "Apache 2.0"), ("Unicode-DFS-2016", "Unicode License Agreement - Data Files and Software (2016)"), ];