diff --git a/Cargo.lock b/Cargo.lock index 19c22e79..41e5bec1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -550,7 +550,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" dependencies = [ "memchr", - "regex-automata 0.4.6", + "regex-automata 0.4.7", "serde", ] @@ -2601,8 +2601,8 @@ dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata 0.4.6", - "regex-syntax 0.8.3", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", ] [[package]] @@ -2884,7 +2884,7 @@ dependencies = [ "globset", "log", "memchr", - "regex-automata 0.4.6", + "regex-automata 0.4.7", "same-file", "walkdir", "winapi-util", @@ -4469,7 +4469,7 @@ dependencies = [ "rand", "rand_chacha", "rand_xorshift", - "regex-syntax 0.8.3", + "regex-syntax 0.8.4", "unarray", ] @@ -4622,14 +4622,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.4" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.6", - "regex-syntax 0.8.3", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", ] [[package]] @@ -4643,13 +4643,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.3", + "regex-syntax 0.8.4", ] [[package]] @@ -4660,9 +4660,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "rfc6979" diff --git a/cargo/src/assets/mod.rs b/cargo/src/assets/mod.rs index 823b8d76..1ed07b82 100644 --- a/cargo/src/assets/mod.rs +++ b/cargo/src/assets/mod.rs @@ -780,7 +780,7 @@ fn deps_tree_metadata<'cfg: 'r, 't: 'r, 'r>(package: &'cfg Package, let mut packages = HashMap::new(); if let Some(metadata) = playdate_metadata(package) { // if explicitly allowed collect deps => scan deps-tree - if metadata.assets_options().dependencies { + if metadata.assets_options().dependencies() { log::debug!("inspecting deps-tree of {}", package.package_id()); packages.insert(package, metadata); diff --git a/cargo/src/assets/plan.rs b/cargo/src/assets/plan.rs index f8fa9e05..18c2bbe0 100644 --- a/cargo/src/assets/plan.rs +++ b/cargo/src/assets/plan.rs @@ -188,7 +188,7 @@ pub mod proto { root.package_id(), root.node().target().kind().description() ); - log::debug!(" dependencies are allowed: {}", options.dependencies); + log::debug!(" dependencies are allowed: {}", options.dependencies()); let plan_key = MultiKey::from(root); @@ -420,7 +420,7 @@ pub mod proto { let why = format!("but that's not allowed by the top-level crate {root_name}"); let msg = format!("{name}'s `{dev}assets.{target:?}` overrides {others}, {why}"); - if options.overwrite { + if options.overwrite() { cfg.log().warn(msg) } else { cfg.log().error(&msg); diff --git a/cargo/src/utils/cargo/meta_deps.rs b/cargo/src/utils/cargo/meta_deps.rs index 9a5cb2a3..ad7d7d86 100644 --- a/cargo/src/utils/cargo/meta_deps.rs +++ b/cargo/src/utils/cargo/meta_deps.rs @@ -6,6 +6,7 @@ use cargo::core::{PackageId, PackageIdSpecQuery}; use cargo::util::interning::InternedString; use cargo::CargoResult; use playdate::manifest::PackageSource; +use playdate::metadata::format::ws::WorkspaceMetadata; use playdate::metadata::source::MetadataSource; use playdate::metadata::format::Metadata as MainMetadata; use serde::Deserialize; @@ -39,6 +40,8 @@ pub struct MetaDeps<'cfg> { pub struct RootNode<'cfg> { node: Node<'cfg>, deps: Vec>, + + ws: Option<&'cfg WorkspaceMetadata>, } impl<'t> RootNode<'t> { @@ -81,6 +84,7 @@ impl<'t> MetaDeps<'t> { }; let is_sub_tk = |u: &&Unit| matches!(u.target.kind, TargetKind::Lib(_)); + let ws = meta.workspace_metadata.as_ref(); let mut roots = units.roots .iter() @@ -88,12 +92,12 @@ impl<'t> MetaDeps<'t> { .filter(mode_is_build) .filter(is_norm_tk) .map(|u| { - // let m = meta.packages.iter().find(|p| p.id == u.package_id); let m = meta.packages.iter().find(|p| p.id.matches(u.package_id)); Node::<'t> { meta: m, unit: u } }) .map(|node| { - RootNode::<'t> { node, + RootNode::<'t> { ws, + node, deps: Vec::with_capacity(0) } }) .collect::>(); @@ -107,7 +111,6 @@ impl<'t> MetaDeps<'t> { .filter(mode_is_build) .filter(is_sub_tk) .map(|u| { - // let m = meta.packages.iter().find(|p| p.id == u.package_id); let m = meta.packages.iter().find(|p| p.id.matches(u.package_id)); Node::<'t> { meta: m, unit: u } }) @@ -272,7 +275,7 @@ impl<'t> MetaDeps<'t> { .map(|m| m.assets_options()) }) .unwrap_or_default() - .dependencies + .dependencies() } } @@ -283,7 +286,7 @@ pub trait DependenciesAllowed { impl DependenciesAllowed for RootNode<'_> { - fn deps_allowed(&self) -> bool { self.node.deps_allowed() } + fn deps_allowed(&self) -> bool { self.node.deps_allowed() || self.as_source().assets_options().dependencies() } } impl DependenciesAllowed for Node<'_> { @@ -294,7 +297,7 @@ impl DependenciesAllowed for Node<'_> { .and_then(|m| m.inner.as_ref()) .map(|m| m.assets_options()) .unwrap_or_default() - .dependencies + .dependencies() } } @@ -308,7 +311,7 @@ impl DependenciesAllowed for cargo::core::Package { .ok() }) .and_then(|m| m.inner) - .map(|m| m.assets_options().dependencies) + .map(|m| m.assets_options().dependencies()) .unwrap_or_default() } } @@ -325,10 +328,10 @@ impl<'t> Node<'t> { impl<'t> RootNode<'t> { pub fn into_source(self) -> impl PackageSource> + 't { - CrateNode::from(self.node) + CrateNode::from(&self) } pub fn as_source(&self) -> impl PackageSource> + 't { - self.to_owned().into_source() + CrateNode::from(self) } } @@ -337,6 +340,8 @@ struct CrateNode<'t> { node: Node<'t>, bins: Vec<&'t str>, examples: Vec<&'t str>, + + ws: Option<&'t WorkspaceMetadata>, } impl<'t> From> for CrateNode<'t> { @@ -355,7 +360,30 @@ impl<'t> From> for CrateNode<'t> { .flat_map(|m| m.targets.iter()) .filter(|t| t.kind == TargetKind::Example) .map(|t| t.name.as_str()) - .collect() } + .collect(), + ws: None } + } +} + +impl<'t> From<&RootNode<'t>> for CrateNode<'t> { + fn from(root: &RootNode<'t>) -> Self { + let node = root.node; + Self { node, + bins: node.meta + .as_ref() + .into_iter() + .flat_map(|m| m.targets.iter()) + .filter(|t| t.kind == TargetKind::Bin) + .map(|t| t.name.as_str()) + .collect(), + examples: node.meta + .as_ref() + .into_iter() + .flat_map(|m| m.targets.iter()) + .filter(|t| t.kind == TargetKind::Example) + .map(|t| t.name.as_str()) + .collect(), + ws: root.ws } } } @@ -408,4 +436,13 @@ impl PackageSource for CrateNode<'_> { .map(|m| m.manifest_path.as_path().into()) .unwrap_or_default() } + + + // from ws metadata: + fn default_options(&self) -> Option<&playdate::metadata::format::ws::OptionsDefault> { + self.ws + .and_then(|m| m.inner.as_ref()) + .as_ref() + .and_then(|m| m.options.as_ref()) + } } diff --git a/support/build/README.md b/support/build/README.md index 7a8c0c38..54c590eb 100644 --- a/support/build/README.md +++ b/support/build/README.md @@ -144,8 +144,13 @@ Package build options, instruction for Playdate Package Build System such as car ```toml [package.metadata.playdate.options] +workspace = true # use `workspace.metadata.playdate.options` as defaults (default is `false`) +assets.dependencies = true # just set or override corresponding value from `workspace.metadata` ``` +Field `workspace` works like the cargo's feature [inheriting a dependency from a workspace][cargo-inheriting-dep-ws], turning on structural inheritance of `package.metadata.playdate.options` by `workspace.metadata.playdate.options`. + + Available options is `assets`, see [Assets Options](#assets-options). _Currently there is no more options, it's just reserved for future use._ @@ -153,9 +158,11 @@ _Currently there is no more options, it's just reserved for future use._ This configuration is used for primary packages only. Primary packages are the ones the user selected on the command-line, either with `-p` flags or the defaults based on the current directory and the default workspace members. So, `options` from top-level package are applying to entire dependency tree ignoring `options` of dependencies. Thus, only the end user controls how the assets will be collected & built. -Note: this is depends on implementation, above is how it works in the reference impl `cargo-playdate`. +_Note: this is depends on implementation, above is how it works in the reference impl `cargo-playdate`._ +[cargo-inheriting-dep-ws]: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#inheriting-a-dependency-from-a-workspace + #### Assets Options @@ -164,12 +171,12 @@ This is how assets will be collected for your package. ```toml [package.metadata.playdate.options.assets] dependencies = true # allow to build assets for dependencies (default is `false`) -overwrite = true # overwrite existing assets in build dir (default is `true`) +overwrite = true # overwrite existing assets in build dir (default is `true`, alias: `override`) method = "link" # "copy" or "link" (default is `link`) - how assets should be collected, make symlinks or copy files follow-symlinks = true # follow symlinks (default is `true`) ``` - +Field `overwrite` also allows higher dependencies to overwrite assets of deeper dependency. - - - diff --git a/support/build/src/assets/mod.rs b/support/build/src/assets/mod.rs index a9236a85..bde694af 100644 --- a/support/build/src/assets/mod.rs +++ b/support/build/src/assets/mod.rs @@ -23,8 +23,8 @@ pub fn apply_build_plan<'l, 'r, P: AsRef>(plan: BuildPlan<'l, 'r>, use crate::fs::ensure_dir_exists; let target_root = target_root.as_ref(); - let build_method = assets_options.method; - let overwrite = assets_options.overwrite; + let build_method = assets_options.method(); + let overwrite = assets_options.overwrite(); info!("collecting assets:"); debug!("assets build method: {build_method:?}, overwrite: {overwrite}"); @@ -171,7 +171,7 @@ pub enum OpRes { impl AssetsOptions { fn link_behavior(&self) -> LinkBehavior { - if self.follow_symlinks { + if self.follow_symlinks() { LinkBehavior::ReadTarget } else { LinkBehavior::ReadFile diff --git a/support/build/src/metadata/format.rs b/support/build/src/metadata/format.rs index 0e5a2910..505a8e2b 100644 --- a/support/build/src/metadata/format.rs +++ b/support/build/src/metadata/format.rs @@ -47,7 +47,8 @@ pub mod ws { #[cfg_attr(feature = "serde", derive(super::Deserialize))] #[cfg_attr(feature = "serde", serde(deny_unknown_fields))] pub struct OptionsDefault { - pub assets: Option, + #[cfg_attr(feature = "serde", serde(default))] + pub assets: super::AssetsOptions, } } @@ -100,13 +101,7 @@ impl MetadataSource for Metadata fn options(&self) -> &Options { &self.inner.options } - - fn assets_options(&self) -> Cow<'_, AssetsOptions> { - self.options() - .assets - .as_ref() - .map_or_else(Default::default, Cow::Borrowed) - } + fn assets_options(&self) -> Cow<'_, AssetsOptions> { Cow::Borrowed(&self.options().assets) } fn support(&self) -> &Support { &self.inner.support } } @@ -569,47 +564,78 @@ impl ManifestSourceOptExt for Override where Manifest: ManifestSourceOp pub struct Options { /// Use [`PackageSource::default_options`] as defaults for this. #[cfg_attr(feature = "serde", serde(default))] - pub workspace: bool, // not implemented yet - pub assets: Option, + pub workspace: bool, + #[cfg_attr(feature = "serde", serde(default))] + pub assets: AssetsOptions, // Output layout ctrl, temporary removed. } +impl Options { + pub fn with_workspace(&self, def: Option<&ws::OptionsDefault>) -> Cow<'_, Options> { + let merge_assets = |assets: &AssetsOptions| { + if def.is_some() { + log::debug!("merge options.assets with ws.defaults") + } -#[derive(Debug, Clone, PartialEq)] + let overwrite = assets.overwrite + .or_else(|| def.and_then(|d| d.assets.overwrite)) + .unwrap_or(AssetsOptions::default_overwrite()); + let follow_symlinks = assets.follow_symlinks + .or_else(|| def.and_then(|d| d.assets.follow_symlinks)) + .unwrap_or(AssetsOptions::default_follow_symlinks()); + let method = assets.method + .or_else(|| def.and_then(|d| d.assets.method)) + .unwrap_or_default(); + let dependencies = assets.dependencies + .or_else(|| def.and_then(|d| d.assets.dependencies)) + .unwrap_or(AssetsOptions::default_dependencies()); + + AssetsOptions { overwrite: Some(overwrite), + follow_symlinks: Some(follow_symlinks), + method: Some(method), + dependencies: Some(dependencies) } + }; + + + if self.workspace { + let res = Self { workspace: self.workspace, + assets: merge_assets(&self.assets) }; + Cow::Owned(res) + } else { + Cow::Borrowed(self) + } + } +} + + +#[derive(Default, Debug, Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(Deserialize))] #[cfg_attr(feature = "serde", serde(deny_unknown_fields))] pub struct AssetsOptions { #[cfg_attr(feature = "serde", serde(alias = "override"))] - #[cfg_attr(feature = "serde", serde(default = "AssetsOptions::default_overwrite"))] - pub overwrite: bool, + overwrite: Option, #[cfg_attr(feature = "serde", serde(alias = "follow-symlinks"))] - #[cfg_attr(feature = "serde", serde(default = "AssetsOptions::default_follow_symlinks"))] - pub follow_symlinks: bool, + follow_symlinks: Option, - #[cfg_attr(feature = "serde", serde(alias = "build-method", default))] - pub method: AssetsBuildMethod, + #[cfg_attr(feature = "serde", serde(alias = "build-method"))] + method: Option, /// Allow building assets for dependencies - #[cfg_attr(feature = "serde", serde(default = "AssetsOptions::default_dependencies"))] - pub dependencies: bool, + dependencies: Option, } impl AssetsOptions { + pub fn overwrite(&self) -> bool { self.overwrite.unwrap_or(Self::default_overwrite()) } + pub fn dependencies(&self) -> bool { self.dependencies.unwrap_or(Self::default_dependencies()) } + pub fn follow_symlinks(&self) -> bool { self.follow_symlinks.unwrap_or(Self::default_follow_symlinks()) } + pub fn method(&self) -> AssetsBuildMethod { self.method.unwrap_or_default() } + const fn default_overwrite() -> bool { true } const fn default_follow_symlinks() -> bool { true } const fn default_dependencies() -> bool { false } } -impl Default for AssetsOptions { - fn default() -> Self { - Self { overwrite: Self::default_overwrite(), - follow_symlinks: Self::default_follow_symlinks(), - dependencies: Self::default_dependencies(), - method: Default::default() } - } -} - #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Deserialize))] @@ -995,7 +1021,7 @@ mod tests { #[test] fn options_empty() { let m = toml::from_str::("").unwrap(); - assert!(m.assets.is_none()); + assert_eq!(Options::default(), m); } #[test] @@ -1006,8 +1032,8 @@ mod tests { let m = toml::from_str::(src).unwrap(); assert_matches!( m.assets, - Some(AssetsOptions { dependencies: false, - .. }) + AssetsOptions { dependencies: None, + .. } ); // overrides default @@ -1018,8 +1044,8 @@ mod tests { let m = toml::from_str::(src).unwrap(); assert_matches!( m.assets, - Some(AssetsOptions { dependencies: true, - .. }) + AssetsOptions { dependencies: Some(true), + .. } ); } @@ -1133,8 +1159,8 @@ mod tests { assert!(m.assets.is_empty()); assert_matches!( m.options.assets, - Some(AssetsOptions { dependencies: true, - .. }) + AssetsOptions { dependencies: Some(true), + .. } ); } @@ -1341,7 +1367,7 @@ mod tests { let opts = m.assets_options(); - assert!(opts.dependencies); + assert!(opts.dependencies()); assert!(!AssetsOptions::default_dependencies()); assert_matches!(m.assets(), AssetsRules::Map(_)); diff --git a/support/build/src/metadata/source.rs b/support/build/src/metadata/source.rs index 4aa82e3e..aead97a6 100644 --- a/support/build/src/metadata/source.rs +++ b/support/build/src/metadata/source.rs @@ -35,17 +35,11 @@ pub trait PackageSource { /// merged with `default_options`, /// if `metadata.options.workspace` is `true`. fn assets_options(&self) -> AssetsOptions { - // TODO: impl assets-options merge instead just choosing one self.metadata() - .and_then(|m| { - m.options().assets.clone().or_else(|| { - m.options() - .workspace - .then(|| self.default_options().and_then(|o| o.assets.clone())) - .flatten() - }) - }) + .map(|m| m.options().with_workspace(self.default_options())) .unwrap_or_default() + .assets + .to_owned() } /// Names of `bin` cargo-targets.