diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index 6ffae639bf14..d590f5926c49 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -316,11 +316,11 @@ def features(self, value: T.List[str]) -> None: def get_build_def_files(self) -> T.List[str]: return self.build_def_files - def load_workspace(self, subdir: str) -> WorkspaceState: + def load_workspace(self, subdir: str, extra_members: T.Optional[T.List[str]]) -> WorkspaceState: """Load the root Cargo.toml package and prepare it with features and dependencies.""" subdir = os.path.normpath(subdir) manifest, cached = self._load_manifest(subdir) - ws = self._get_workspace(manifest, subdir, False) + ws = self._get_workspace(manifest, subdir, extra_members, False) if not cached: self._prepare_entry_point(ws) return ws @@ -356,7 +356,7 @@ def interpret(self, subdir: str, project_root: T.Optional[str] = None) -> mparse assert isinstance(manifest, Manifest) return self.interpret_package(manifest, build, subdir, project_root) else: - ws = self.load_workspace(subdir) + ws = self.load_workspace(subdir, None) return self.interpret_workspace(ws, build, subdir) def interpret_package(self, manifest: Manifest, build: builder.Builder, subdir: str, project_root: str) -> mparser.CodeBlockNode: @@ -447,7 +447,28 @@ def _add_workspace_member(self, manifest_: Manifest, ws: WorkspaceState, m: str) else: ws.packages[m] = PackageState(manifest_, ws_subdir=ws.subdir, ws_member=m, downloaded=ws.downloaded) - def _get_workspace(self, manifest: T.Union[Workspace, Manifest], subdir: str, downloaded: bool) -> WorkspaceState: + # Returns the expansion of a glob expression with a single + # cannonical result, otherwise (multiple results) returns None. + @staticmethod + def _unglobify(pat: str) -> T.Optional[str]: + ret = '' + iterator = iter(pat) + for char in iterator: + if char in {'?', '*'}: + return None + elif char == '[': + try: + ret += next(iterator) + if next(iterator) != ']': + return None + except StopIteration: + mlog.warning(f'Encountered invalid glob pattern "{pat}"') + return None + else: + ret += char + return ret + + def _get_workspace(self, manifest: T.Union[Workspace, Manifest], subdir: str, extra_members: T.Optional[T.List[str]], downloaded: bool) -> WorkspaceState: ws = self.workspaces.get(subdir) if ws: return ws @@ -456,7 +477,18 @@ def _get_workspace(self, manifest: T.Union[Workspace, Manifest], subdir: str, do ws = WorkspaceState(workspace, subdir, downloaded=downloaded) if workspace.root_package: self._add_workspace_member(workspace.root_package, ws, '.') - for m in workspace.members: + + members = [self._unglobify(entry) for entry in workspace.members] + if None in members: + if extra_members is None: + mlog.warning('Cargo workspace contains glob members. Please specify them manually using the \'extra_members\' kwarg') + members = [member for member in members if member is not None] + if extra_members is not None: + members += extra_members + # set ws default members if not set + if workspace.default_members is None: + workspace.default_members = members + for m in members: self._load_workspace_member(ws, m) self.workspaces[subdir] = ws return ws @@ -530,7 +562,7 @@ def _fetch_package_from_subproject(self, package_name: str, subp_name: str) -> P subp_name in self.environment.wrap_resolver.wraps and \ self.environment.wrap_resolver.wraps[subp_name].type is not None - ws = self._get_workspace(manifest, subdir, downloaded=downloaded) + ws = self._get_workspace(manifest, subdir, None, downloaded=downloaded) member = ws.packages_to_member[package_name] pkg = self._require_workspace_member(ws, member) pkg.subproject_name = subp_name diff --git a/mesonbuild/cargo/manifest.py b/mesonbuild/cargo/manifest.py index 0d1c0f279ff7..c43d7e60c9c3 100644 --- a/mesonbuild/cargo/manifest.py +++ b/mesonbuild/cargo/manifest.py @@ -618,7 +618,7 @@ def from_raw(cls, raw: raw.Manifest, path: str) -> Self: if 'package' in raw: ws.root_package = Manifest.from_raw(raw, path, ws, '.') if not ws.default_members: - ws.default_members = ['.'] if ws.root_package else ws.members + ws.default_members = ['.'] if ws.root_package else None return ws diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index 4873843c0d9c..463665e9c81d 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -74,6 +74,7 @@ class FuncBindgen(TypedDict): class FuncWorkspace(TypedDict): default_features: T.Optional[bool] features: T.List[str] + extra_members: T.List[str] class FuncDependency(TypedDict): rust_abi: T.Optional[RUST_ABI] @@ -996,6 +997,12 @@ def to_system_dependency(self, state: ModuleState, args: T.Tuple[Dependency, T.O default=None, listify=True, ), + KwargInfo( + 'extra_members', + (ContainerTypeInfo(list, str), NoneType), + default=None, + listify=True, + ), ) def workspace(self, state: ModuleState, args: T.List, kwargs: FuncWorkspace) -> RustWorkspace: """Creates a Rust workspace object, controlling the build of @@ -1018,7 +1025,7 @@ def workspace(self, state: ModuleState, args: T.List, kwargs: FuncWorkspace) -> cargo_features.extend(features) self.interpreter.cargo.features = cargo_features - ws = self.interpreter.cargo.load_workspace(state.root_subdir) + ws = self.interpreter.cargo.load_workspace(state.root_subdir, kwargs['extra_members']) # Cargo projects may not have a subprojects directory, because # dependencies are declared in Cargo.toml rather than .wrap files. diff --git a/test cases/rust/43 extra_members/Cargo.lock b/test cases/rust/43 extra_members/Cargo.lock new file mode 100644 index 000000000000..a432bf6f8b6f --- /dev/null +++ b/test cases/rust/43 extra_members/Cargo.lock @@ -0,0 +1,15 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "alpha" +version = "0.0.0" + +[[package]] +name = "beta" +version = "0.0.0" + +[[package]] +name = "gamma" +version = "0.0.0" diff --git a/test cases/rust/43 extra_members/Cargo.toml b/test cases/rust/43 extra_members/Cargo.toml new file mode 100644 index 000000000000..634dc6045336 --- /dev/null +++ b/test cases/rust/43 extra_members/Cargo.toml @@ -0,0 +1,6 @@ +[workspace] +resolver = "3" +members = [ + "subdir/*", + "g[*]mma" +] diff --git a/test cases/rust/43 extra_members/g*mma/Cargo.toml b/test cases/rust/43 extra_members/g*mma/Cargo.toml new file mode 100644 index 000000000000..9b6d3d506006 --- /dev/null +++ b/test cases/rust/43 extra_members/g*mma/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "gamma" +version = "0.0.0" +edition = "2021" diff --git a/test cases/rust/43 extra_members/g*mma/src/main.rs b/test cases/rust/43 extra_members/g*mma/src/main.rs new file mode 100644 index 000000000000..f328e4d9d04c --- /dev/null +++ b/test cases/rust/43 extra_members/g*mma/src/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/test cases/rust/43 extra_members/meson.build b/test cases/rust/43 extra_members/meson.build new file mode 100644 index 000000000000..fd3d54677a0b --- /dev/null +++ b/test cases/rust/43 extra_members/meson.build @@ -0,0 +1,12 @@ +project('rust ws extra_members', 'rust', + meson_version: '>= 1.11' +) + +cargo = import('rust').workspace(extra_members: ['subdir/alpha']) + +if 'beta' in cargo.packages() + error('Unexpectedly resolved glob member') +endif + +cargo.package('alpha').executable() +cargo.package('gamma').executable() diff --git a/test cases/rust/43 extra_members/subdir/alpha/Cargo.toml b/test cases/rust/43 extra_members/subdir/alpha/Cargo.toml new file mode 100644 index 000000000000..1b69e2f3fa7a --- /dev/null +++ b/test cases/rust/43 extra_members/subdir/alpha/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "alpha" +version = "0.0.0" +edition = "2021" diff --git a/test cases/rust/43 extra_members/subdir/alpha/src/main.rs b/test cases/rust/43 extra_members/subdir/alpha/src/main.rs new file mode 100644 index 000000000000..f328e4d9d04c --- /dev/null +++ b/test cases/rust/43 extra_members/subdir/alpha/src/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/test cases/rust/43 extra_members/subdir/beta/Cargo.toml b/test cases/rust/43 extra_members/subdir/beta/Cargo.toml new file mode 100644 index 000000000000..346c1475c8c4 --- /dev/null +++ b/test cases/rust/43 extra_members/subdir/beta/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "beta" +version = "0.0.0" +edition = "2021" diff --git a/test cases/rust/43 extra_members/subdir/beta/src/main.rs b/test cases/rust/43 extra_members/subdir/beta/src/main.rs new file mode 100644 index 000000000000..5efd50c40dbc --- /dev/null +++ b/test cases/rust/43 extra_members/subdir/beta/src/main.rs @@ -0,0 +1,3 @@ +fn main() { +#error "Intentionally not building" +}