Enhance OSDK performance by reusing existing base, bundle and build

This commit is contained in:
Ruize Tang
2024-12-09 16:25:49 +08:00
committed by Tate, Hongliang Tian
parent 3bbdc68d39
commit 858e95ed4d
12 changed files with 174 additions and 76 deletions

View File

@ -8,7 +8,7 @@ use std::{
ffi::OsString,
path::{Path, PathBuf},
process,
time::{Duration, SystemTime},
time::SystemTime,
};
use bin::make_elf_for_qemu;
@ -19,6 +19,7 @@ use crate::{
base_crate::new_base_crate,
bundle::{
bin::{AsterBin, AsterBinType, AsterElfMeta},
file::BundleFile,
Bundle,
},
cli::BuildArgs,
@ -88,6 +89,31 @@ pub fn create_base_and_cached_build(
bundle
}
fn get_reusable_existing_bundle(
bundle_path: impl AsRef<Path>,
config: &Config,
action: ActionChoice,
) -> Option<Bundle> {
let existing_bundle = Bundle::load(&bundle_path);
let Some(existing_bundle) = existing_bundle else {
info!("Building a new bundle: No cached bundle found or validation of the existing bundle failed");
return None;
};
if let Err(e) = existing_bundle.can_run_with_config(config, action) {
info!("Building a new bundle: {}", e);
return None;
}
let workspace_root = {
let meta = get_cargo_metadata(None::<&str>, None::<&[&str]>).unwrap();
PathBuf::from(meta.get("workspace_root").unwrap().as_str().unwrap())
};
if existing_bundle.last_modified_time() < get_last_modified_time(&workspace_root) {
info!("Building a new bundle: workspace_root has been updated");
return None;
}
Some(existing_bundle)
}
/// If the source is not since modified and the last build is recent, we can reuse the existing bundle.
pub fn do_cached_build(
bundle_path: impl AsRef<Path>,
@ -97,54 +123,6 @@ pub fn do_cached_build(
action: ActionChoice,
rustflags: &[&str],
) -> Bundle {
let build_a_new_one = || {
do_build(
&bundle_path,
&osdk_output_directory,
&cargo_target_directory,
config,
action,
rustflags,
)
};
let existing_bundle = Bundle::load(&bundle_path);
let Some(existing_bundle) = existing_bundle else {
return build_a_new_one();
};
if existing_bundle.can_run_with_config(config, action).is_err() {
return build_a_new_one();
}
let Ok(built_since) = SystemTime::now().duration_since(existing_bundle.last_modified_time())
else {
return build_a_new_one();
};
if built_since > Duration::from_secs(600) {
return build_a_new_one();
}
let workspace_root = {
let meta = get_cargo_metadata(None::<&str>, None::<&[&str]>).unwrap();
PathBuf::from(meta.get("workspace_root").unwrap().as_str().unwrap())
};
if get_last_modified_time(workspace_root) < existing_bundle.last_modified_time() {
return existing_bundle;
}
build_a_new_one()
}
pub fn do_build(
bundle_path: impl AsRef<Path>,
osdk_output_directory: impl AsRef<Path>,
cargo_target_directory: impl AsRef<Path>,
config: &Config,
action: ActionChoice,
rustflags: &[&str],
) -> Bundle {
if bundle_path.as_ref().exists() {
std::fs::remove_dir_all(&bundle_path).unwrap();
}
let mut bundle = Bundle::new(&bundle_path, config, action);
let (build, boot) = match action {
ActionChoice::Run => (&config.run.build, &config.run.boot),
ActionChoice::Test => (&config.test.build, &config.test.boot),
@ -160,6 +138,21 @@ pub fn do_build(
rustflags,
);
// Check the existing bundle's reusability
if let Some(existing_bundle) = get_reusable_existing_bundle(&bundle_path, config, action) {
if get_last_modified_time(aster_elf.path()) < existing_bundle.last_modified_time() {
info!("Reusing existing bundle: aster_elf is unchanged");
return existing_bundle;
}
}
// Build a new bundle
info!("Building a new bundle");
if bundle_path.as_ref().exists() {
std::fs::remove_dir_all(&bundle_path).unwrap();
}
let mut bundle = Bundle::new(&bundle_path, config, action);
match boot.method {
BootMethod::GrubRescueIso | BootMethod::GrubQcow2 => {
info!("Building boot device image");
@ -274,6 +267,9 @@ fn build_kernel_elf(
}
fn get_last_modified_time(path: impl AsRef<Path>) -> SystemTime {
if path.as_ref().is_file() {
return path.as_ref().metadata().unwrap().modified().unwrap();
}
let mut last_modified = SystemTime::UNIX_EPOCH;
for entry in std::fs::read_dir(path).unwrap() {
let entry = entry.unwrap();

View File

@ -47,9 +47,10 @@ fn add_manifest_dependencies(cargo_metadata: &serde_json::Value, crate_name: &st
fs::write(manifest_path, content).unwrap();
}
// Add `target/osdk/base` to `exclude` array of the workspace manifest
// Add `target/osdk/base` and `target/osdk/test-base` to `exclude` array of the workspace manifest
fn exclude_osdk_base(metadata: &serde_json::Value) {
let osdk_base_path = "target/osdk/base";
let osdk_run_base_path = "target/osdk/base";
let osdk_test_base_path = "target/osdk/test-base";
let workspace_manifest_path = {
let workspace_root = metadata.get("workspace_root").unwrap().as_str().unwrap();
@ -64,17 +65,25 @@ fn exclude_osdk_base(metadata: &serde_json::Value) {
if let Some(exclude) = workspace.get_mut("exclude") {
let exclude = exclude.as_array_mut().unwrap();
if exclude.contains(&toml::Value::String(osdk_base_path.to_string())) {
if exclude.contains(&toml::Value::String(osdk_run_base_path.to_string()))
|| exclude.contains(&toml::Value::String(osdk_test_base_path.to_string()))
{
return;
}
exclude.push(toml::Value::String(osdk_base_path.to_string()));
exclude.push(toml::Value::String(osdk_run_base_path.to_string()));
exclude.push(toml::Value::String(osdk_test_base_path.to_string()));
} else {
let exclude = vec![toml::Value::String(osdk_base_path.to_string())];
let exclude = vec![
toml::Value::String(osdk_run_base_path.to_string()),
toml::Value::String(osdk_test_base_path.to_string()),
];
workspace.insert("exclude".to_string(), toml::Value::Array(exclude));
}
} else {
let exclude = toml::Table::from_str(r#"exclude = ["target/osdk/base"]"#).unwrap();
let exclude =
toml::Table::from_str(r#"exclude = ["target/osdk/base", "target/osdk/test-base"]"#)
.unwrap();
manifest_toml.insert("workspace".to_string(), toml::Value::Table(exclude));
}

View File

@ -26,7 +26,8 @@ pub fn test_current_crate(config: &Config, args: &TestArgs) {
let current_crate = get_current_crate_info();
let cargo_target_directory = get_target_directory();
let osdk_output_directory = cargo_target_directory.join(DEFAULT_TARGET_RELPATH);
let target_crate_dir = osdk_output_directory.join("base");
// Use a different name for better separation and reusability of `run` and `test`
let target_crate_dir = osdk_output_directory.join("test-base");
// A special case is that we use OSDK to test the OSDK test runner crate
// itself. We check it by name.