blob: 2ba4da70837fe036b59bf744c9b97449a8813748 [file] [log] [blame]
// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use gnrt_lib::*;
use crates::ThirdPartyCrate;
use manifest::*;
use std::collections::HashSet;
use std::fs;
use std::io::{self, Write};
use std::path::Path;
fn main() {
let args = clap::Command::new("gnrt")
.about("Generate GN build rules from third_party/rust crates")
.arg(
clap::Arg::new("output_cargo_toml")
.help("Output third_party/rust/Cargo.toml then exit immediately"),
)
.get_matches();
let paths = paths::ChromiumPaths::new().unwrap();
let manifest_contents =
String::from_utf8(fs::read(paths.third_party.join("third_party.toml")).unwrap()).unwrap();
let third_party_manifest: ThirdPartyManifest = toml::de::from_str(&manifest_contents).unwrap();
// Traverse our third-party directory to collect the set of vendored crates.
// Used to generate Cargo.toml [patch] sections, and later to check against
// `cargo metadata`'s dependency resolution to ensure we have all the crates
// we need. We sort `crates` for a stable ordering of [patch] sections.
let mut crates = crates::collect_third_party_crates(paths.third_party.clone()).unwrap();
crates.sort_unstable_by(|a, b| a.0.cmp(&b.0));
// Generate a fake Cargo.toml for dependency resolution.
let cargo_manifest = generate_fake_cargo_toml(
third_party_manifest,
crates.iter().map(|(c, _)| {
let crate_path = paths.third_party.join(c.crate_path()).canonicalize().unwrap();
manifest::PatchSpecification {
package_name: c.name.clone(),
patch_name: c.patch_name(),
path: crate_path,
}
}),
);
if args.is_present("output_cargo_toml") {
println!("{}", toml::ser::to_string(&cargo_manifest).unwrap());
return;
}
// Create a fake package: Cargo.toml and an empty main.rs. This allows cargo
// to construct a full dependency graph as if Chrome were a cargo package.
write!(
io::BufWriter::new(fs::File::create(paths.third_party.join("Cargo.toml")).unwrap()),
"# {}\n\n{}",
AUTOGENERATED_FILE_HEADER,
toml::to_string(&cargo_manifest).unwrap()
)
.unwrap();
create_dirs_if_needed(&paths.third_party.join("src")).unwrap();
write!(
io::BufWriter::new(fs::File::create(paths.third_party.join("src/main.rs")).unwrap()),
"// {}",
AUTOGENERATED_FILE_HEADER
)
.unwrap();
// Run `cargo metadata` and process the output to get a list of crates we
// depend on.
let mut command = cargo_metadata::MetadataCommand::new();
command.current_dir(paths.third_party.clone());
let dependencies = deps::collect_dependencies(&command.exec().unwrap());
// Compare cargo's dependency resolution with the crates we have on disk.
let present_crates: HashSet<&ThirdPartyCrate> = crates.iter().map(|(c, _)| c).collect();
let mut unused_crates: HashSet<&ThirdPartyCrate> = present_crates.clone();
let mut missing_deps: Vec<&deps::ThirdPartyDep> = Vec::new();
for dep in dependencies.iter() {
let dep_crate = ThirdPartyCrate { name: dep.package_name.clone(), epoch: dep.epoch };
if present_crates.contains(&dep_crate) {
assert!(unused_crates.remove(&dep_crate));
} else {
missing_deps.push(dep);
}
}
for dep in missing_deps.iter() {
println!("Missing dependency: {} {}", dep.package_name, dep.epoch);
for edge in dep.dependency_path.iter() {
println!(" {edge}");
}
}
for c in unused_crates.iter() {
println!("Crate not used: {c}");
}
for nonlocal_dep in dependencies.iter().filter(|dep| !dep.is_local) {
println!(
"Error: dependency {} {} resolved to an upstream source. The checked-in version likely \
does not satisfy another crate's dependency version requirement. Resolved \
version: {}",
nonlocal_dep.package_name, nonlocal_dep.epoch, nonlocal_dep.version
);
}
}
fn create_dirs_if_needed(path: &Path) -> io::Result<()> {
if path.is_dir() {
return Ok(());
}
if let Some(parent) = path.parent() {
create_dirs_if_needed(parent)?;
}
fs::create_dir(path)
}
/// A message prepended to autogenerated files. Notes this tool generated it and
/// not to edit directly.
static AUTOGENERATED_FILE_HEADER: &'static str = "!!! DO NOT EDIT -- Autogenerated by gnrt from third_party.toml. Edit that file instead. See tools/crates/gnrt.";