| // 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. |
| |
| use anyhow::{anyhow, Context, Result}; |
| use assembly_util::{DuplicateKeyError, InsertUniqueExt, MapEntry, NamedMap, PackageDestination}; |
| use camino::{Utf8Path, Utf8PathBuf}; |
| use fuchsia_pkg::{PackageBuilder, RelativeTo}; |
| use std::collections::BTreeMap; |
| |
| /// The mapping of a config_data entry's file-path in the package's config_data |
| /// to it's source path on the filesystem. |
| type FileEntryMap = BTreeMap<Utf8PathBuf, Utf8PathBuf>; |
| |
| // The config_data entries for each package, by package name |
| type ConfigDataMap = NamedMap<String, FileEntryMap>; |
| |
| /// A builder for the config_data package. |
| pub struct ConfigDataBuilder { |
| /// A map of the files to put into config_data, by the name of the package |
| /// that they are config_data for. |
| for_packages: ConfigDataMap, |
| } |
| |
| impl ConfigDataBuilder { |
| /// Construct a default ConfigDataBuilder. |
| pub fn default() -> Self { |
| ConfigDataBuilder { for_packages: ConfigDataMap::new("config data") } |
| } |
| |
| /// Add a file from the filesystem to config_data for a given package, at a |
| /// particular path in the package's namespace. |
| pub fn add_entry( |
| &mut self, |
| package_name: &str, |
| destination: Utf8PathBuf, |
| source: Utf8PathBuf, |
| ) -> Result<()> { |
| let package_entries = self.for_packages.entry(package_name.into()).or_default(); |
| package_entries.try_insert_unique(MapEntry(destination, source)).map_err(|error| |
| anyhow!( |
| "Found a duplicate config_data entry for package '{}' at path: '{}': '{}' and was already '{}'", |
| package_name, |
| error.key(), |
| error.new_value(), |
| error.previous_value())) |
| } |
| |
| /// Build the config_data package, in the specified outdir, and return the |
| /// path to the `config_data` package's manifest. |
| pub fn build(self, outdir: impl AsRef<Utf8Path>) -> Result<Utf8PathBuf> { |
| let outdir = outdir.as_ref().join("config_data"); |
| // config_data is never produced by assembly tools from one Fuchsia |
| // release and then read by binaries from another Fuchsia release. Give |
| // it the platform ABI revision. |
| let mut package_builder = PackageBuilder::new_platform_internal_package( |
| PackageDestination::ConfigData.to_string(), |
| ); |
| |
| for (package_name, entries) in self.for_packages.entries { |
| for (destination_path, source_file) in entries { |
| let config_data_package_path = |
| Utf8PathBuf::from("meta/data").join(&package_name).join(destination_path); |
| package_builder |
| .add_file_to_far(config_data_package_path, source_file.to_string())?; |
| } |
| } |
| |
| let metafar_path = outdir.join("meta.far"); |
| let manifest_path = outdir.join("package_manifest.json"); |
| |
| package_builder.manifest_path(&manifest_path); |
| package_builder.manifest_blobs_relative_to(RelativeTo::File); |
| package_builder.repository("fuchsia.com"); |
| |
| package_builder |
| .build(outdir, &metafar_path) |
| .context(format!("Building `config_data` package at path '{}'", metafar_path))?; |
| |
| Ok(manifest_path) |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use fuchsia_pkg::PackageManifest; |
| use std::fs::File; |
| use tempfile::{NamedTempFile, TempDir}; |
| |
| #[test] |
| fn test_builder() { |
| let tmp = TempDir::new().unwrap(); |
| let outdir = Utf8Path::from_path(tmp.path()).unwrap(); |
| |
| let config_data_metafar_path = outdir.join("config_data").join("meta.far"); |
| |
| // Create a file to write to the package. |
| let source_file_path = NamedTempFile::new().unwrap(); |
| std::fs::write(&source_file_path, "some data").unwrap(); |
| |
| let source_file_path = source_file_path.into_temp_path(); |
| |
| // Add the file. |
| let mut builder = ConfigDataBuilder::default(); |
| builder |
| .add_entry( |
| &"foo".to_string(), |
| "dest/path".into(), |
| Utf8Path::from_path(source_file_path.as_ref()).unwrap().to_owned(), |
| ) |
| .unwrap(); |
| |
| // Build the package. |
| let manifest_path = builder.build(&outdir).unwrap(); |
| |
| // Read the package manifest back in. |
| let config_data_manifest = PackageManifest::try_load_from(manifest_path).unwrap(); |
| assert_eq!(config_data_manifest.name().as_ref(), "config-data"); |
| |
| let mut config_data_metafar = File::open(config_data_metafar_path).unwrap(); |
| let mut far_reader = fuchsia_archive::Utf8Reader::new(&mut config_data_metafar).unwrap(); |
| let config_file_data = far_reader.read_file("meta/data/foo/dest/path").unwrap(); |
| assert_eq!(config_file_data, "some data".as_bytes()); |
| } |
| |
| #[test] |
| fn test_builder_rejects_duplicates() { |
| let mut builder = ConfigDataBuilder::default(); |
| let _ = builder.add_entry(&"foo".to_string(), "dest/path".into(), "source/path".into()); |
| builder |
| .add_entry(&"foo".to_string(), "dest/path".into(), "source/path".into()) |
| .unwrap_err(); |
| } |
| } |