From 08df7e6d959052ced9daaf84a4ca24c0554be14f Mon Sep 17 00:00:00 2001 From: Sean Klein Date: Fri, 12 Jan 2024 09:50:42 -0800 Subject: [PATCH] Package with topological sorting --- Cargo.lock | 7 +++++ Cargo.toml | 1 + package/Cargo.toml | 1 + package/src/main.rs | 66 +++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 70 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f984979cb..67d5a7dde 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1035,6 +1035,7 @@ dependencies = [ "crucible-workspace-hack", "omicron-zone-package", "tokio", + "topological-sort", ] [[package]] @@ -4922,6 +4923,12 @@ dependencies = [ "winnow", ] +[[package]] +name = "topological-sort" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d" + [[package]] name = "tower-service" version = "0.3.2" diff --git a/Cargo.toml b/Cargo.toml index 59847167a..d034b9944 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -97,6 +97,7 @@ tokio-rustls = { version = "0.24.1" } tokio-test = "*" tokio-util = { version = "0.7", features = ["codec"]} toml = "0.8" +topological-sort = "0.2.2" tracing = "0.1" tracing-opentelemetry = "0.18.0" tracing-subscriber = "0.3.18" diff --git a/package/Cargo.toml b/package/Cargo.toml index 38fe92837..523492225 100644 --- a/package/Cargo.toml +++ b/package/Cargo.toml @@ -7,4 +7,5 @@ edition = "2021" anyhow.workspace = true omicron-zone-package.workspace = true tokio.workspace = true +topological-sort.workspace = true crucible-workspace-hack.workspace = true diff --git a/package/src/main.rs b/package/src/main.rs index 2b905386b..7f31b0a11 100644 --- a/package/src/main.rs +++ b/package/src/main.rs @@ -1,10 +1,13 @@ // Copyright 2022 Oxide Computer Company -use anyhow::Result; +use anyhow::{bail, Result}; use omicron_zone_package::config; +use omicron_zone_package::package::{PackageOutput, PackageSource}; use omicron_zone_package::target::Target; +use std::collections::BTreeMap; use std::fs::create_dir_all; use std::path::Path; +use topological_sort::TopologicalSort; #[tokio::main] async fn main() -> Result<()> { @@ -13,10 +16,63 @@ async fn main() -> Result<()> { let output_dir = Path::new("out"); create_dir_all(output_dir)?; - for (name, package) in cfg.packages { - package - .create_for_target(&Target::default(), &name, output_dir) - .await?; + // Reverse lookup of "output" -> "package creating this output". + let all_packages = cfg + .packages + .iter() + .map(|(name, package)| (package.get_output_file(name), (name, package))) + .collect::>(); + + // Collect all packages, and sort them in dependency order, + // so we know which ones to build first. + let mut outputs = TopologicalSort::::new(); + for (package_output, (_, package)) in &all_packages { + match &package.source { + PackageSource::Local { .. } + | PackageSource::Prebuilt { .. } + | PackageSource::Manual => { + // Skip intermediate leaf packages; if necessary they'll be + // added to the dependency graph by whatever composite package + // actually depends on them. + if !matches!( + package.output, + PackageOutput::Zone { + intermediate_only: true + } + ) { + outputs.insert(package_output); + } + } + PackageSource::Composite { packages: deps } => { + for dep in deps { + outputs.add_dependency(dep, package_output); + } + } + } + } + + while !outputs.is_empty() { + let batch = outputs.pop_all(); + assert!( + !batch.is_empty() || outputs.is_empty(), + "cyclic dependency in package manifest!" + ); + + for output in &batch { + println!("Creating '{output}'"); + + let Some((name, package)) = all_packages.get(output) else { + bail!( + "Cannot find a package to create output: '{output}' \n\ + \tThis can happen when building a composite package, where one of \n\ + \tthe 'source.packages' has not been found." + ); + }; + + package + .create_for_target(&Target::default(), &name, output_dir) + .await?; + } } Ok(())