Skip to content

Commit 95a37fa

Browse files
committed
cargo: include the implicit feature for dependencies in the manifest
Make the implicit `xyz = ["dep:xyz"]` declaration explicit in the Manifest. This also makes it possible to implement correctly the part of the spec where "If you specify the optional dependency with the dep: prefix anywhere in the [features] table, that disables the implicit feature." Signed-off-by: Paolo Bonzini <[email protected]>
1 parent a90592a commit 95a37fa

File tree

3 files changed

+52
-4
lines changed

3 files changed

+52
-4
lines changed

mesonbuild/cargo/interpreter.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -499,9 +499,6 @@ def _enable_feature(self, pkg: PackageState, feature: str) -> None:
499499
if feature in cfg.features:
500500
return
501501
cfg.features.add(feature)
502-
# A feature can also be a dependency.
503-
if feature in pkg.manifest.dependencies:
504-
self._add_dependency(pkg, feature)
505502
# Recurse on extra features and dependencies this feature pulls.
506503
# https://doc.rust-lang.org/cargo/reference/features.html#the-features-section
507504
for f in pkg.manifest.features.get(feature, []):

mesonbuild/cargo/manifest.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import collections
99
import dataclasses
10+
import itertools
1011
import os
1112
import typing as T
1213

@@ -518,7 +519,7 @@ def from_raw(cls, raw: raw.Manifest, path: str, workspace: T.Optional[Workspace]
518519
def dependencies_from_raw(x: T.Dict[str, T.Any]) -> T.Dict[str, Dependency]:
519520
return {k: Dependency.from_raw(k, v, member_path, workspace) for k, v in x.items()}
520521

521-
return _raw_to_dataclass(raw, cls, f'Cargo.toml package {pkg.name}',
522+
data = _raw_to_dataclass(raw, cls, f'Cargo.toml package {pkg.name}',
522523
raw_from_workspace=workspace.inheritable if workspace else None,
523524
ignored_fields=['badges', 'workspace'],
524525
package=ConvertValue(lambda _: pkg),
@@ -533,6 +534,21 @@ def dependencies_from_raw(x: T.Dict[str, T.Any]) -> T.Dict[str, Dependency]:
533534
example=ConvertValue(lambda x: [Example.from_raw(b, pkg) for b in x]),
534535
target=ConvertValue(lambda x: {k: dependencies_from_raw(v.get('dependencies', {})) for k, v in x.items()}))
535536

537+
# If you specify the optional dependency with the dep: prefix anywhere in the [features]
538+
# table, that disables the implicit feature.
539+
deps = set(feature[4:]
540+
for feature in itertools.chain.from_iterable(data.features.values())
541+
if feature.startswith('dep:'))
542+
543+
for name, dep in itertools.chain(data.dependencies.items(),
544+
data.dev_dependencies.items(),
545+
data.build_dependencies.items()):
546+
if dep.optional and name not in deps:
547+
data.features.setdefault(name, [])
548+
data.features[name].append(f'dep:{name}')
549+
deps.add(name)
550+
551+
return data
536552

537553
@dataclasses.dataclass
538554
class Workspace:

unittests/cargotests.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,26 @@ class CargoTomlTest(unittest.TestCase):
299299
crate-type = ["lib"] # ignored
300300
''')
301301

302+
CARGO_TOML_RUSTIX = textwrap.dedent('''\
303+
[package]
304+
name = "rustix"
305+
edition = "2021"
306+
rust-version = "1.63"
307+
version = "0.38.34"
308+
309+
[dependencies]
310+
alloc = { version = "1.0.0", optional = true, package = "rustc-std-workspace-alloc" }
311+
libc = { version = "0.2.0", optional = true, package = "libc" }
312+
libc_errno = { version = "0.3.8", optional = true, package = "errno" }
313+
314+
[features]
315+
alloc = []
316+
default = ["std"]
317+
rustc-dep-of-std = ["dep:alloc"]
318+
std = ["alloc"]
319+
use-libc = ["libc_errno", "libc"]
320+
''')
321+
302322
CARGO_TOML_WS = textwrap.dedent('''\
303323
[workspace]
304324
resolver = "2"
@@ -558,6 +578,21 @@ def test_cargo_toml_system_deps(self) -> None:
558578
self.assertEqual(len(manifest.system_dependencies['pango'].feature_overrides), 1)
559579
self.assertEqual(manifest.system_dependencies['pango'].feature_overrides['v1_42'], {'version': '1.42'})
560580

581+
def test_cargo_toml_dep_features(self) -> None:
582+
with tempfile.TemporaryDirectory() as tmpdir:
583+
fname = os.path.join(tmpdir, 'Cargo.toml')
584+
with open(fname, 'w', encoding='utf-8') as f:
585+
f.write(self.CARGO_TOML_RUSTIX)
586+
manifest_toml = load_toml(fname)
587+
manifest = Manifest.from_raw(manifest_toml, 'Cargo.toml')
588+
589+
# not included because 'alloc = []' exists
590+
self.assertNotIn('dep:alloc', manifest.features['alloc'])
591+
# included because no libc feature exists
592+
self.assertIn('dep:libc', manifest.features['libc'])
593+
# included with the dependency name, not the package name
594+
self.assertIn('dep:libc_errno', manifest.features['libc_errno'])
595+
561596
def test_cargo_toml_features(self) -> None:
562597
with tempfile.TemporaryDirectory() as tmpdir:
563598
fname = os.path.join(tmpdir, 'Cargo.toml')

0 commit comments

Comments
 (0)