diff options
author | Lars Wirzenius <liw@noreply.codeberg.org> | 2023-09-02 12:30:32 +0000 |
---|---|---|
committer | Lars Wirzenius <liw@noreply.codeberg.org> | 2023-09-02 12:30:32 +0000 |
commit | fc56a38d96372011731ee875500b2f15d6918077 (patch) | |
tree | ee9470ec1ba697cd3294427500f0fa9e2b312e79 | |
parent | aec463dfe8b842d7831cdcba5954ac0689168ae0 (diff) | |
parent | 07b75366442bedbd7d5931f864c3289fa5258c7a (diff) | |
download | ambient-run-fc56a38d96372011731ee875500b2f15d6918077.tar.gz |
Merge pull request 'feat: persist a build artifact cache between builds' (#7) from liw/cache into main
Reviewed-on: https://codeberg.org/ambient/ambient-run/pulls/7
-rw-r--r-- | ambient-run.md | 42 | ||||
-rw-r--r-- | src/bin/ambient-run.rs | 3 | ||||
-rw-r--r-- | src/project.rs | 8 | ||||
-rw-r--r-- | src/qemu.rs | 34 |
4 files changed, 84 insertions, 3 deletions
diff --git a/ambient-run.md b/ambient-run.md index 76fca3f..db16dfb 100644 --- a/ambient-run.md +++ b/ambient-run.md @@ -168,6 +168,7 @@ image: /my/image.qcow2 artifact: null artifact_max_size: null dependencies: null +cache: null ~~~ ### Show per-build configuration @@ -346,6 +347,47 @@ dependencies: foo-deps ~~~ ### Cache is persistent between builds + +_Requirement:_ The build can persist files between builds in a cache +device. + +_Justification:_ This allows incremental builds and caching of build +artifacts. + +_Stakeholder:_ Lars. + +~~~scenario +given an installed ambient-run +given file cache-project.yaml +given file foo/README.md from cache-project.yaml +given a directory foo-cache +given image file image.qcow2 specified for test suite +then file foo-cache/cached.file does not exist +when I run ambient-run build cache-project.yaml --log foo.log +then file foo.log contains "creating cached file" +then file foo-cache/cached.file exists +when I run ambient-run build cache-project.yaml --log foo2.log +then file foo2.log contains "cache has file" +~~~ + + +~~~{#cache-project.yaml .file .yaml} +source: foo +shell: | + #!/bin/bash + set -xeuo pipefail + find /workspace -ls + file="/workspace/cache/cached.file" + if [ -e "$file" ]; then + echo "cache has file" + else + echo "creating cached file" + touch "$file" + fi +image: image.qcow2 +cache: foo-cache +~~~ + ### Build gets the resources is demands ## Utility functionality diff --git a/src/bin/ambient-run.rs b/src/bin/ambient-run.rs index dbe7bfa..8157de7 100644 --- a/src/bin/ambient-run.rs +++ b/src/bin/ambient-run.rs @@ -218,7 +218,8 @@ impl BuildCommand { .with_source(&project.source()) .with_artifact(project.artifact()) .with_artifact_max_size(project.artifact_max_size()) - .with_dependencies(project.dependencies()); + .with_dependencies(project.dependencies()) + .with_cache(project.cache()); if let Some(log) = &self.log { qemu = qemu.with_log(log); } diff --git a/src/project.rs b/src/project.rs index c9b2c38..19ca947 100644 --- a/src/project.rs +++ b/src/project.rs @@ -15,6 +15,7 @@ pub struct Project { artifact: Option<PathBuf>, artifact_max_size: Option<u64>, dependencies: Option<PathBuf>, + cache: Option<PathBuf>, } impl Project { @@ -74,6 +75,7 @@ impl Project { self.artifact = snippet.artifact; self.artifact_max_size = snippet.artifact_max_size; self.dependencies = snippet.dependencies; + self.cache = snippet.cache; Ok(()) } @@ -111,6 +113,11 @@ impl Project { pub fn dependencies(&self) -> &Option<PathBuf> { &self.dependencies } + + /// Cache directory. + pub fn cache(&self) -> &Option<PathBuf> { + &self.cache + } } #[derive(Debug, Deserialize)] @@ -122,6 +129,7 @@ struct ProjectSnippet { artifact: Option<PathBuf>, artifact_max_size: Option<u64>, dependencies: Option<PathBuf>, + cache: Option<PathBuf>, } /// Possible errors from configuration file handling. diff --git a/src/qemu.rs b/src/qemu.rs index 2853e52..2a7a6b6 100644 --- a/src/qemu.rs +++ b/src/qemu.rs @@ -21,6 +21,7 @@ pub struct Qemu { artifact: Option<PathBuf>, artifact_max_xize: u64, dependencies: Option<PathBuf>, + cache: Option<PathBuf>, } impl Qemu { @@ -70,6 +71,12 @@ impl Qemu { self } + /// Set directory where project build cache is. + pub fn with_cache(mut self, dirname: &Option<PathBuf>) -> Self { + self.cache = dirname.clone(); + self + } + /// Run QEMU in the specified way. pub fn run(&self) -> Result<(), QemuError> { eprintln!("qemu run"); @@ -97,8 +104,13 @@ impl Qemu { }; eprintln!("cache drive"); - let cache_drive = - Self::create_tar_with_size(tmp.path().join("cache"), empty.path(), MAX_OUTPUT_SIZE)?; + let cache_drive = if let Some(dirname) = &self.cache { + eprintln!("cache {}", dirname.display()); + Self::create_tar_with_size(tmp.path().join("cache"), dirname, MAX_OUTPUT_SIZE)? + } else { + eprintln!("no cache"); + Self::create_tar(tmp.path().join("cache"), empty.path())? + }; eprintln!("deps drive"); let deps_drive = if let Some(dirname) = &self.dependencies { @@ -154,6 +166,18 @@ impl Qemu { let output = child.wait_with_output().map_err(QemuError::Run)?; if output.status.success() { eprintln!("kvm OK"); + if let Some(dirname) = &self.cache { + eprintln!("persisting cache"); + if dirname.exists() { + eprintln!("removing old cache"); + std::fs::remove_dir_all(dirname) + .map_err(|e| QemuError::RemoveCache(dirname.into(), e))? + } + eprintln!("extracting cache"); + cache_drive + .extract_to(dirname) + .map_err(|e| QemuError::ExtractCache(dirname.into(), e))?; + } Ok(()) } else { let out = String::from_utf8_lossy(&output.stdout); @@ -285,6 +309,12 @@ pub enum QemuError { #[error("failed to create a tar archive from {0}")] Tar(PathBuf, #[source] VirtualDriveError), + + #[error("failed to remove cache (so it can be replaced): {0}")] + RemoveCache(PathBuf, #[source] std::io::Error), + + #[error("failed to extract cache drive to {0}")] + ExtractCache(PathBuf, #[source] VirtualDriveError), } #[cfg(test)] |