Skip to content

Commit 7165442

Browse files
authored
Overhaul the packaging process (#769)
- Builds all the packages we need concurrently, before starting the rest of the packaging process - Execute the packaging process concurrently - Makes use of Add API for tracking progress during package creation omicron-package#6 to add a progress bar for the packaging process
1 parent c66d2f6 commit 7165442

File tree

3 files changed

+214
-25
lines changed

3 files changed

+214
-25
lines changed

Cargo.lock

Lines changed: 54 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ license = "MPL-2.0"
88
anyhow = "1.0"
99
crossbeam = "0.8"
1010
flate2 = "1.0.22"
11+
futures = "0.3.21"
12+
indicatif = { version = "0.17.0-rc.9", features = ["rayon"] }
1113
omicron-common = { path = "../common" }
12-
omicron-zone-package = { version = "0.1.2" }
14+
omicron-zone-package = { version = "0.2.0" }
1315
rayon = "1.5"
1416
reqwest = { version = "0.11", default-features = false, features = ["rustls-tls"] }
1517
serde = { version = "1.0", features = [ "derive" ] }

package/src/bin/omicron-package.rs

Lines changed: 157 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,19 @@
55
//! Utility for bundling target binaries as tarfiles.
66
77
use anyhow::{anyhow, bail, Context, Result};
8+
use futures::stream::{self, StreamExt, TryStreamExt};
9+
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
810
use omicron_package::{parse, SubCommand};
9-
use omicron_zone_package::package::Package;
11+
use omicron_zone_package::package::{Package, Progress};
1012
use rayon::prelude::*;
1113
use serde_derive::Deserialize;
1214
use std::collections::BTreeMap;
1315
use std::env;
1416
use std::fs::create_dir_all;
1517
use std::path::{Path, PathBuf};
16-
use std::process::Command;
18+
use std::sync::Arc;
1719
use structopt::StructOpt;
20+
use tokio::process::Command;
1821

1922
/// Describes the configuration for a set of packages.
2023
#[derive(Deserialize, Debug)]
@@ -47,50 +50,90 @@ struct Args {
4750
subcommand: SubCommand,
4851
}
4952

50-
fn run_cargo_on_package(
53+
async fn run_cargo_on_packages<I, S>(
5154
subcmd: &str,
52-
package: &str,
55+
packages: I,
5356
release: bool,
54-
) -> Result<()> {
57+
) -> Result<()>
58+
where
59+
I: IntoIterator<Item = S>,
60+
S: AsRef<std::ffi::OsStr>,
61+
{
5562
let mut cmd = Command::new("cargo");
5663
// We rely on the rust-toolchain.toml file for toolchain information,
5764
// rather than specifying one within the packaging tool.
58-
cmd.arg(subcmd).arg("-p").arg(package);
65+
cmd.arg(subcmd);
66+
for package in packages {
67+
cmd.arg("-p").arg(package);
68+
}
5969
if release {
6070
cmd.arg("--release");
6171
}
62-
let status =
63-
cmd.status().context(format!("Failed to run command: ({:?})", cmd))?;
72+
let status = cmd
73+
.status()
74+
.await
75+
.context(format!("Failed to run command: ({:?})", cmd))?;
6476
if !status.success() {
65-
bail!("Failed to build package: {}", package);
77+
bail!("Failed to build packages");
6678
}
6779

6880
Ok(())
6981
}
7082

71-
async fn do_check(config: &Config) -> Result<()> {
72-
for (package_name, package) in &config.packages {
73-
if let Some(rust_pkg) = &package.rust {
74-
println!("Checking {}", package_name);
75-
run_cargo_on_package("check", &package_name, rust_pkg.release)?;
76-
}
83+
async fn do_for_all_rust_packages(
84+
config: &Config,
85+
command: &str,
86+
) -> Result<()> {
87+
// First, filter out all Rust packages from the configuration that should be
88+
// built, and partition them into "release" and "debug" categories.
89+
let (release_pkgs, debug_pkgs): (Vec<_>, _) = config
90+
.packages
91+
.iter()
92+
.filter_map(|(name, pkg)| {
93+
pkg.rust.as_ref().map(|rust_pkg| (name, rust_pkg.release))
94+
})
95+
.partition(|(_, release)| *release);
96+
97+
// Execute all the release / debug packages at the same time.
98+
if !release_pkgs.is_empty() {
99+
run_cargo_on_packages(
100+
command,
101+
release_pkgs.iter().map(|(name, _)| name),
102+
true,
103+
)
104+
.await?;
105+
}
106+
if !debug_pkgs.is_empty() {
107+
run_cargo_on_packages(
108+
command,
109+
debug_pkgs.iter().map(|(name, _)| name),
110+
false,
111+
)
112+
.await?;
77113
}
78114
Ok(())
79115
}
80116

117+
async fn do_check(config: &Config) -> Result<()> {
118+
do_for_all_rust_packages(config, "check").await
119+
}
120+
121+
async fn do_build(config: &Config) -> Result<()> {
122+
do_for_all_rust_packages(config, "build").await
123+
}
124+
81125
async fn do_package(config: &Config, output_directory: &Path) -> Result<()> {
82126
create_dir_all(&output_directory)
83127
.map_err(|err| anyhow!("Cannot create output directory: {}", err))?;
84128

85-
for (package_name, package) in &config.packages {
86-
if let Some(rust_pkg) = &package.rust {
87-
println!("Building: {}", package_name);
88-
run_cargo_on_package("build", package_name, rust_pkg.release)?;
89-
}
90-
package.create(&output_directory).await?;
91-
}
129+
let ui = ProgressUI::new();
130+
let ui_refs = vec![ui.clone(); config.packages.len()];
131+
132+
do_build(&config).await?;
92133

93134
for (package_name, package) in &config.external_packages {
135+
let progress = ui.add_package(package_name.to_string(), 1);
136+
progress.set_message("finding package".to_string());
94137
let path = package.get_output_path(&output_directory);
95138
if !path.exists() {
96139
bail!(
@@ -108,8 +151,34 @@ in improving the cross-repository meta-build system, please contact sean@.",
108151
.to_string_lossy(),
109152
);
110153
}
154+
progress.finish();
111155
}
112156

157+
stream::iter(&config.packages)
158+
// It's a pain to clone a value into closures - see
159+
// https://github.com/rust-lang/rfcs/issues/2407 - so in the meantime,
160+
// we explicitly create the references to the UI we need for each
161+
// package.
162+
.zip(stream::iter(ui_refs))
163+
// We convert the stream type to operate on Results, so we may invoke
164+
// "try_for_each_concurrent" more easily.
165+
.map(Ok::<_, anyhow::Error>)
166+
.try_for_each_concurrent(
167+
None,
168+
|((package_name, package), ui)| async move {
169+
let total_work = package.get_total_work();
170+
let progress =
171+
ui.add_package(package_name.to_string(), total_work);
172+
progress.set_message("bundle package".to_string());
173+
package
174+
.create_with_progress(&progress, &output_directory)
175+
.await?;
176+
progress.finish();
177+
Ok(())
178+
},
179+
)
180+
.await?;
181+
113182
Ok(())
114183
}
115184

@@ -221,6 +290,72 @@ fn do_uninstall(
221290
Ok(())
222291
}
223292

293+
fn in_progress_style() -> ProgressStyle {
294+
ProgressStyle::default_bar()
295+
.template(
296+
"[{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {msg}",
297+
)
298+
.unwrap()
299+
.progress_chars("#>.")
300+
}
301+
302+
fn completed_progress_style() -> ProgressStyle {
303+
ProgressStyle::default_bar()
304+
.template("[{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {msg:.green}")
305+
.unwrap()
306+
.progress_chars("#>.")
307+
}
308+
309+
// Struct managing display of progress to UI.
310+
struct ProgressUI {
311+
multi: MultiProgress,
312+
style: ProgressStyle,
313+
}
314+
315+
struct PackageProgress {
316+
pb: ProgressBar,
317+
service_name: String,
318+
}
319+
320+
impl PackageProgress {
321+
fn finish(&self) {
322+
self.pb.set_style(completed_progress_style());
323+
self.pb.finish_with_message(format!("{}: done", self.service_name));
324+
self.pb.tick();
325+
}
326+
}
327+
328+
impl Progress for PackageProgress {
329+
fn set_message(&self, message: impl Into<std::borrow::Cow<'static, str>>) {
330+
self.pb.set_message(format!(
331+
"{}: {}",
332+
self.service_name,
333+
message.into()
334+
));
335+
}
336+
337+
fn increment(&self, delta: u64) {
338+
self.pb.inc(delta);
339+
}
340+
}
341+
342+
impl ProgressUI {
343+
fn new() -> Arc<Self> {
344+
Arc::new(Self {
345+
multi: MultiProgress::new(),
346+
style: in_progress_style(),
347+
})
348+
}
349+
350+
fn add_package(&self, service_name: String, total: u64) -> PackageProgress {
351+
let pb = self.multi.add(ProgressBar::new(total));
352+
pb.set_style(self.style.clone());
353+
pb.set_message(service_name.clone());
354+
pb.tick();
355+
PackageProgress { pb, service_name }
356+
}
357+
}
358+
224359
#[tokio::main]
225360
async fn main() -> Result<()> {
226361
let args = Args::from_args_safe().map_err(|err| anyhow!(err))?;

0 commit comments

Comments
 (0)