Skip to content

Commit 6104cd5

Browse files
committed
cargo: implement machine-specific dependency tracking
Change the dependency resolution code to take a MachineChoice as an argument. Create build- and/or host-side configurations through the machines_from() method in Manifest.
1 parent 00a45b9 commit 6104cd5

File tree

3 files changed

+82
-42
lines changed

3 files changed

+82
-42
lines changed

docs/markdown/Wrap-dependency-system-manual.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,10 @@ Some naming conventions need to be respected:
364364
This is typically used as `extra_deps += dependency('foo')`.
365365
- Since *1.10.0* the `features` variable is an array of enabled features for the subproject.
366366

367+
Note that currently `meson/meson.build` is only evaluated once even if a given
368+
dependency is needed for both the build and host machine. This should rarely be
369+
a problem, but it should be kept in mind to avoid problems when cross compiling.
370+
367371
Since *1.5.0* Cargo wraps can also be provided with `Cargo.lock` file at the root
368372
of (sub)project source tree. Meson will automatically load that file and convert
369373
it into a series of wraps definitions.

mesonbuild/cargo/interpreter.py

Lines changed: 57 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,11 @@ def interpret_package(self, manifest: Manifest, build: builder.Builder, subdir:
144144
else:
145145
pkg, cached = self._fetch_package(manifest.package.name, manifest.package.api)
146146
if not cached:
147-
self._prepare_package(pkg)
148-
# This is an entry point, always enable the 'default' feature.
149-
# FIXME: We should have a Meson option similar to `cargo build --no-default-features`
150-
self._enable_feature(pkg, 'default')
147+
for machine in pkg.manifest.machines_from(MachineChoice.HOST):
148+
self._prepare_package(pkg, machine)
149+
# This is an entry point, always enable the 'default' feature.
150+
# FIXME: We should have a Meson option similar to `cargo build --no-default-features`
151+
self._enable_feature(pkg, 'default', machine)
151152
ast += self._create_project(pkg.manifest.package.name, pkg, build)
152153
ast.append(build.assign(build.function('import', [build.string('rust')]), 'rust'))
153154
ast += self._create_package(pkg, build, subdir)
@@ -192,6 +193,8 @@ def _create_package(self, pkg: PackageState, build: builder.Builder, subdir: str
192193

193194
if pkg.manifest.lib:
194195
for crate_type in pkg.manifest.lib.crate_type:
196+
if crate_type == 'proc-macro' and machine == MachineChoice.HOST:
197+
continue
195198
ast.extend(self._create_lib(pkg, build, crate_type, machine))
196199

197200
return ast
@@ -205,7 +208,8 @@ def interpret_workspace(self, workspace: Workspace, build: builder.Builder, subd
205208
if not ws.required_members:
206209
for member in ws.workspace.default_members:
207210
pkg = self._require_workspace_member(ws, member)
208-
self._prepare_package(pkg)
211+
for machine in pkg.manifest.machines_from(MachineChoice.HOST):
212+
self._prepare_package(pkg, machine)
209213

210214
# Call subdir() for each required member of the workspace. The order is
211215
# important, if a member depends on another member, that member must be
@@ -216,12 +220,16 @@ def _process_member(member: str) -> None:
216220
if member in processed_members:
217221
return
218222
pkg = ws.packages[member]
219-
cfg = pkg.cfg[MachineChoice.HOST]
220-
for depname in cfg.required_deps:
221-
dep = pkg.manifest.dependencies[depname]
222-
if dep.path:
223-
dep_member = os.path.normpath(os.path.join(pkg.ws_member, dep.path))
224-
_process_member(dep_member)
223+
# Process dependencies for all configured machines
224+
for machine in MachineChoice:
225+
cfg = pkg.cfg[machine]
226+
if cfg is None:
227+
continue
228+
for depname in cfg.required_deps:
229+
dep = pkg.manifest.dependencies[depname]
230+
if dep.path:
231+
dep_member = os.path.normpath(os.path.join(pkg.ws_member, dep.path))
232+
_process_member(dep_member)
225233
if member == '.':
226234
ast.extend(self._create_package(pkg, build, subdir))
227235
else:
@@ -301,20 +309,21 @@ def _fetch_package_from_subproject(self, package_name: str, meson_depname: str)
301309
self.packages[key] = pkg
302310
return pkg, False
303311

304-
def _prepare_package(self, pkg: PackageState) -> None:
305-
if pkg.cfg[MachineChoice.HOST]:
306-
return
312+
def _prepare_package(self, pkg: PackageState, machine: MachineChoice) -> None:
313+
"""Prepare package configuration for specific machine."""
314+
if pkg.cfg[machine] is not None:
315+
return # Already prepared for this machine
307316

308-
_ = pkg.get_or_create_cfg(MachineChoice.HOST)
309-
# Merge target specific dependencies that are enabled
310-
cfgs = self._get_cfgs(MachineChoice.HOST)
317+
_ = pkg.get_or_create_cfg(machine)
318+
# Merge target-specific dependencies that are enabled for this machine
319+
target_cfgs = self._get_cfgs(machine)
311320
for condition, dependencies in pkg.manifest.target.items():
312-
if eval_cfg(condition, cfgs):
321+
if eval_cfg(condition, target_cfgs):
313322
pkg.manifest.dependencies.update(dependencies)
314-
# Fetch required dependencies recursively.
323+
# Fetch required dependencies recursively for this machine
315324
for depname, dep in pkg.manifest.dependencies.items():
316325
if not dep.optional:
317-
self._add_dependency(pkg, depname)
326+
self._add_dependency(pkg, depname, machine)
318327

319328
def _dep_package(self, pkg: PackageState, dep: Dependency) -> PackageState:
320329
if dep.path:
@@ -369,8 +378,8 @@ def _load_manifest(self, subdir: str, workspace: T.Optional[Workspace] = None, m
369378
self.manifests[subdir] = manifest_
370379
return manifest_
371380

372-
def _add_dependency(self, pkg: PackageState, depname: str) -> None:
373-
cfg = pkg.cfg[MachineChoice.HOST]
381+
def _add_dependency(self, pkg: PackageState, depname: str, machine: MachineChoice) -> None:
382+
cfg = pkg.cfg[machine]
374383
if depname in cfg.required_deps:
375384
return
376385
dep = pkg.manifest.dependencies.get(depname)
@@ -379,22 +388,24 @@ def _add_dependency(self, pkg: PackageState, depname: str) -> None:
379388
return
380389
cfg.required_deps.add(depname)
381390
dep_pkg = self._dep_package(pkg, dep)
382-
self._prepare_package(dep_pkg)
383-
if dep.default_features:
384-
self._enable_feature(dep_pkg, 'default')
385-
for f in dep.features:
386-
self._enable_feature(dep_pkg, f)
387-
for f in cfg.optional_deps_features[depname]:
388-
self._enable_feature(dep_pkg, f)
389-
390-
def _enable_feature(self, pkg: PackageState, feature: str) -> None:
391-
cfg = pkg.cfg[MachineChoice.HOST]
391+
# Use machines_from() to determine which machines the dependency needs
392+
for dep_machine in dep_pkg.manifest.machines_from(machine):
393+
self._prepare_package(dep_pkg, dep_machine)
394+
if dep.default_features:
395+
self._enable_feature(dep_pkg, 'default', dep_machine)
396+
for f in dep.features:
397+
self._enable_feature(dep_pkg, f, dep_machine)
398+
for f in cfg.optional_deps_features[depname]:
399+
self._enable_feature(dep_pkg, f, dep_machine)
400+
401+
def _enable_feature(self, pkg: PackageState, feature: str, machine: MachineChoice) -> None:
402+
cfg = pkg.cfg[machine]
392403
if feature in cfg.features:
393404
return
394405
cfg.features.add(feature)
395406
# A feature can also be a dependency.
396407
if feature in pkg.manifest.dependencies:
397-
self._add_dependency(pkg, feature)
408+
self._add_dependency(pkg, feature, machine)
398409
# Recurse on extra features and dependencies this feature pulls.
399410
# https://doc.rust-lang.org/cargo/reference/features.html#the-features-section
400411
for f in pkg.manifest.features.get(feature, []):
@@ -403,19 +414,21 @@ def _enable_feature(self, pkg: PackageState, feature: str) -> None:
403414
if depname[-1] == '?':
404415
depname = depname[:-1]
405416
else:
406-
self._add_dependency(pkg, depname)
417+
self._add_dependency(pkg, depname, machine)
407418
if depname in cfg.required_deps:
408419
dep = pkg.manifest.dependencies[depname]
409420
dep_pkg = self._dep_package(pkg, dep)
410-
self._enable_feature(dep_pkg, dep_f)
421+
# Use machines_from() to determine which machines the dependency needs
422+
for dep_machine in dep_pkg.manifest.machines_from(machine):
423+
self._enable_feature(dep_pkg, dep_f, dep_machine)
411424
else:
412425
# This feature will be enabled only if that dependency
413426
# is later added.
414427
cfg.optional_deps_features[depname].add(dep_f)
415428
elif f.startswith('dep:'):
416-
self._add_dependency(pkg, f[4:])
429+
self._add_dependency(pkg, f[4:], machine)
417430
else:
418-
self._enable_feature(pkg, f)
431+
self._enable_feature(pkg, f, machine)
419432

420433
def has_check_cfg(self, machine: MachineChoice) -> bool:
421434
if not self.environment.is_cross_build():
@@ -507,8 +520,10 @@ def _create_dependencies(self, pkg: PackageState, build: builder.Builder, machin
507520
for depname in cfg.required_deps:
508521
dep = pkg.manifest.dependencies[depname]
509522
dep_pkg = self._dep_package(pkg, dep)
510-
if dep_pkg.manifest.lib:
511-
ast += self._create_dependency(dep_pkg, dep, build, machine)
523+
if not dep_pkg.manifest.lib:
524+
continue
525+
for dep_machine in dep_pkg.manifest.machines_from(machine):
526+
ast += self._create_dependency(dep_pkg, dep, build, dep_machine)
512527
ast.append(build.assign(build.array([]), 'system_deps_args'))
513528
for name, sys_dep in pkg.manifest.system_dependencies.items():
514529
if sys_dep.enabled(cfg.features):
@@ -632,10 +647,11 @@ def _create_lib(self, pkg: PackageState, build: builder.Builder, crate_type: raw
632647
for name in cfg.required_deps:
633648
dep = pkg.manifest.dependencies[name]
634649
dep_pkg = self._dep_package(pkg, dep)
635-
dependencies.append(build.identifier(_dependency_varname(dep.package, machine)))
650+
for dep_machine in dep_pkg.manifest.machines_from(machine):
651+
dependencies.append(build.identifier(_dependency_varname(dep.package, dep_machine)))
636652
dep_lib_name = dep_pkg.manifest.lib.name
637653
dep_crate_name = name if name != dep.package else dep_lib_name
638-
dependency_map[build.string(_fixup_meson_target_name(dep_lib_name, machine))] = build.string(dep_crate_name)
654+
dependency_map[build.string(_fixup_meson_target_name(dep_lib_name, dep_machine))] = build.string(dep_crate_name)
639655
for name, sys_dep in pkg.manifest.system_dependencies.items():
640656
if sys_dep.enabled(cfg.features):
641657
dependencies.append(build.identifier(f'{fixup_meson_varname(name)}_system_dep'))

mesonbuild/cargo/manifest.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import typing as T
1212

1313
from . import version
14-
from ..mesonlib import MesonException, lazy_property, Version
14+
from ..mesonlib import MesonException, lazy_property, Version, MachineChoice
1515
from .. import mlog
1616

1717
if T.TYPE_CHECKING:
@@ -513,6 +513,26 @@ class Manifest:
513513
def __post_init__(self) -> None:
514514
self.features.setdefault('default', [])
515515

516+
def machines_from(self, parent_machine: MachineChoice) -> T.Iterable[MachineChoice]:
517+
"""Return the machines this manifest should be built for based on the machine
518+
for the package that depended on this one."""
519+
if self.lib is None:
520+
# No support for bin, test, etc.
521+
return
522+
523+
need_build = False
524+
need_host = False
525+
for crate_type in self.lib.crate_type:
526+
if crate_type == 'proc-macro' or parent_machine == MachineChoice.BUILD:
527+
need_build = True
528+
else:
529+
need_host = True
530+
531+
if need_build:
532+
yield MachineChoice.BUILD
533+
if need_host:
534+
yield MachineChoice.HOST
535+
516536
@lazy_property
517537
def system_dependencies(self) -> T.Dict[str, SystemDependency]:
518538
return {k: SystemDependency.from_raw(k, v) for k, v in self.package.metadata.get('system-deps', {}).items()}

0 commit comments

Comments
 (0)