blob: 5d3a5d3eaac93507a1110c5777723eb9bf4a9344 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Generate a build archive from a product bundle.
use anyhow::{bail, Context, Result};
use argh::FromArgs;
use assembly_manifest::{AssemblyManifest, Image};
use camino::Utf8PathBuf;
use sdk_metadata::ProductBundle;
/// Generate a build archive using the specified `args`.
#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand, name = "generate-build-archive")]
pub struct GenerateBuildArchive {
/// path to a product bundle.
#[argh(option)]
product_bundle: Utf8PathBuf,
/// path to the directory to write a build archive into.
#[argh(option)]
out_dir: Utf8PathBuf,
}
impl GenerateBuildArchive {
pub fn generate(self) -> Result<()> {
println!("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
println!("@");
println!("@ The `pbtool generate-build-archive` is deprecated.");
println!("@");
println!("@ Please flash using a product bundle (v2) instead.");
println!("@");
println!("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
let product_bundle = ProductBundle::try_load_from(&self.product_bundle)?;
let product_bundle = match product_bundle {
ProductBundle::V1(_) => bail!("Only v2 product bundles are supported"),
ProductBundle::V2(pb) => pb,
};
// Find the first assembly in the preferential order of A, B, then R.
let assembly = if let Some(a) = product_bundle.system_a {
a
} else if let Some(b) = product_bundle.system_b {
b
} else if let Some(r) = product_bundle.system_r {
r
} else {
bail!("The product bundle does not have any assembly systems");
};
// Ensure the `out_dir` exists.
std::fs::create_dir_all(&self.out_dir)
.with_context(|| format!("Creating the out_dir: {}", &self.out_dir))?;
// Collect the Images with the final destinations to add to an images manifest later.
let mut images = vec![];
// Pull out the relevant files.
for image in assembly.iter() {
let entry = match &image {
Image::ZBI { path, signed: _ } => Some((path, "zircon-a.zbi")),
Image::FVM(path) => Some((path, "storage-full.blk")),
Image::QemuKernel(path) => Some((path, "qemu-kernel.kernel")),
_ => None,
};
if let Some((path, name)) = entry {
// Copy the image to the out_dir.
let destination = self.out_dir.join(name);
std::fs::copy(&path, &destination)
.with_context(|| format!("Copying image {} to {}", path, destination))?;
// Create a new Image with the new path.
let mut new_image = image.clone();
new_image.set_source(destination);
images.push(new_image);
}
}
// Write the images manifest with the rebased image paths.
let images_manifest = AssemblyManifest { images };
let images_manifest_path = self.out_dir.join("images.json");
images_manifest.write(images_manifest_path).context("Writing images manifest")?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use assembly_manifest::Image;
use assembly_partitions_config::PartitionsConfig;
use camino::Utf8Path;
use sdk_metadata::ProductBundleV2;
use serde_json::Value;
use std::fs::File;
use std::io::Write;
use tempfile::tempdir;
#[test]
fn test_generate_build_archive() {
let tmp = tempdir().unwrap();
let tempdir = Utf8Path::from_path(tmp.path()).unwrap();
let create_temp_file = |name: &str| {
let path = tempdir.join(name);
let mut file = File::create(path).unwrap();
write!(file, "{}", name).unwrap();
};
create_temp_file("zbi");
create_temp_file("fvm");
create_temp_file("kernel");
let pb = ProductBundle::V2(ProductBundleV2 {
product_name: "".to_string(),
product_version: "".to_string(),
partitions: PartitionsConfig::default(),
sdk_version: "".to_string(),
system_a: Some(vec![
Image::ZBI { path: tempdir.join("zbi"), signed: false },
Image::FVM(tempdir.join("fvm")),
Image::QemuKernel(tempdir.join("kernel")),
]),
system_b: None,
system_r: None,
repositories: vec![],
update_package_hash: None,
virtual_devices_path: None,
});
let pb_path = tempdir.join("product_bundle");
std::fs::create_dir_all(&pb_path).unwrap();
pb.write(&pb_path).unwrap();
let ba_path = tempdir.join("build_archive");
let cmd =
GenerateBuildArchive { product_bundle: pb_path.clone(), out_dir: ba_path.clone() };
cmd.generate().unwrap();
assert!(ba_path.join("zircon-a.zbi").exists());
assert!(ba_path.join("storage-full.blk").exists());
assert!(ba_path.join("qemu-kernel.kernel").exists());
let images_manifest_file = File::open(ba_path.join("images.json")).unwrap();
let images_manifest: Value = serde_json::from_reader(images_manifest_file).unwrap();
assert_eq!(
images_manifest,
serde_json::from_str::<Value>(
r#"
[
{
"name": "zircon-a",
"type": "zbi",
"path": "zircon-a.zbi",
"signed": false
},
{
"type": "blk",
"name": "storage-full",
"path": "storage-full.blk"
},
{
"type": "kernel",
"name": "qemu-kernel",
"path": "qemu-kernel.kernel"
}
]
"#
)
.unwrap()
);
}
}