Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ watchexec-filterer-globset = "8.0"
spin-app = { path = "crates/app" }
spin-build = { path = "crates/build" }
spin-common = { path = "crates/common" }
spin-dependency-wit = { path = "crates/dependency-wit" }
spin-factors-executor = { path = "crates/factors-executor" }
spin-doctor = { path = "crates/doctor" }
spin-environments = { path = "crates/environments" }
Expand Down
1 change: 1 addition & 0 deletions crates/build/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ edition = { workspace = true }
anyhow = { workspace = true }
serde = { workspace = true }
spin-common = { path = "../common" }
spin-dependency-wit = { path = "../dependency-wit" }
spin-environments = { path = "../environments" }
spin-manifest = { path = "../manifest" }
spin-serde = { path = "../serde" }
Expand Down
104 changes: 89 additions & 15 deletions crates/build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub async fn build(
manifest_file: &Path,
component_ids: &[String],
target_checks: TargetChecking,
wit_generation: GenerateDependencyWits,
cache_root: Option<PathBuf>,
) -> Result<()> {
let build_info = component_build_configs(manifest_file)
Expand All @@ -33,7 +34,13 @@ pub async fn build(
})?;
let app_dir = parent_dir(manifest_file)?;

let build_result = build_components(component_ids, build_info.components(), &app_dir);
let components_to_build = components_to_build(component_ids, build_info.components())?;

if wit_generation.generate() {
regenerate_wits(&components_to_build, &app_dir).await?;
}

let build_result = build_components(components_to_build, &app_dir);

// Emit any required warnings now, so that they don't bury any errors.
if let Some(e) = build_info.load_error() {
Expand Down Expand Up @@ -91,14 +98,20 @@ pub async fn build(
/// components, perform target checking). We run a "default build" in several
/// places and this centralises the logic of what such a "default build" means.
pub async fn build_default(manifest_file: &Path, cache_root: Option<PathBuf>) -> Result<()> {
build(manifest_file, &[], TargetChecking::Check, cache_root).await
build(
manifest_file,
&[],
TargetChecking::Check,
GenerateDependencyWits::Generate,
cache_root,
)
.await
}

fn build_components(
fn components_to_build(
component_ids: &[String],
components: Vec<ComponentBuildInfo>,
app_dir: &Path,
) -> Result<(), anyhow::Error> {
) -> anyhow::Result<Vec<ComponentBuildInfo>> {
let components_to_build = if component_ids.is_empty() {
components
} else {
Expand All @@ -119,6 +132,34 @@ fn build_components(
.collect()
};

Ok(components_to_build)
}

async fn regenerate_wits(
components_to_build: &[ComponentBuildInfo],
app_root: &Path,
) -> anyhow::Result<()> {
for component in components_to_build {
let component_dir = match component.build.as_ref().and_then(|b| b.workdir.as_ref()) {
None => app_root.to_owned(),
Some(d) => app_root.join(d),
};
let dest_file = component_dir.join("spin-dependencies.wit");
spin_dependency_wit::extract_wits_into(
component.dependencies.inner.iter(),
app_root,
dest_file,
)
.await?;
}

Ok(())
}

fn build_components(
components_to_build: Vec<ComponentBuildInfo>,
app_dir: &Path,
) -> anyhow::Result<()> {
if components_to_build.iter().all(|c| c.build.is_none()) {
println!("None of the components have a build command.");
println!("For information on specifying a build command, see https://spinframework.dev/build#setting-up-for-spin-build.");
Expand Down Expand Up @@ -331,6 +372,21 @@ impl TargetChecking {
}
}

/// Specifies dependency WIT generation behaviour
pub enum GenerateDependencyWits {
/// The build should generate WITs for component dependencies.
Generate,
/// The build should not generate WITs.
Skip,
}

impl GenerateDependencyWits {
/// Should the build generate dependency WITs?
fn generate(&self) -> bool {
matches!(self, Self::Generate)
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -343,26 +399,44 @@ mod tests {
#[tokio::test]
async fn can_load_even_if_trigger_invalid() {
let bad_trigger_file = test_data_root().join("bad_trigger.toml");
build(&bad_trigger_file, &[], TargetChecking::Skip, None)
.await
.unwrap();
build(
&bad_trigger_file,
&[],
TargetChecking::Skip,
GenerateDependencyWits::Skip,
None,
)
.await
.unwrap();
}

#[tokio::test]
async fn succeeds_if_target_env_matches() {
let manifest_path = test_data_root().join("good_target_env.toml");
build(&manifest_path, &[], TargetChecking::Check, None)
.await
.unwrap();
build(
&manifest_path,
&[],
TargetChecking::Check,
GenerateDependencyWits::Skip,
None,
)
.await
.unwrap();
}

#[tokio::test]
async fn fails_if_target_env_does_not_match() {
let manifest_path = test_data_root().join("bad_target_env.toml");
let err = build(&manifest_path, &[], TargetChecking::Check, None)
.await
.expect_err("should have failed")
.to_string();
let err = build(
&manifest_path,
&[],
TargetChecking::Check,
GenerateDependencyWits::Skip,
None,
)
.await
.expect_err("should have failed")
.to_string();

// build prints validation errors rather than returning them to top level
// (because there could be multiple errors) - see has_meaningful_error_if_target_env_does_not_match
Expand Down
21 changes: 21 additions & 0 deletions crates/dependency-wit/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "spin-dependency-wit"
version = { workspace = true }
authors = { workspace = true }
edition = { workspace = true }

[dependencies]
anyhow = { workspace = true }
indexmap = { workspace = true }
spin-loader = { path = "../loader" }
spin-manifest = { path = "../manifest" }
spin-serde = { path = "../serde" }
tokio = { workspace = true, features = ["rt", "time"] }
wasmparser = { workspace = true }
wit-component = { workspace = true }
wit-parser = { workspace = true }

[dev-dependencies]
tempfile = { workspace = true }
wasm-pkg-common = { workspace = true }
wit-component = { workspace = true, features = ["dummy-module"] }
Loading
Loading