Skip to content
Open
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
22 changes: 18 additions & 4 deletions mesonbuild/cargo/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from __future__ import annotations
import dataclasses
import functools
import itertools
import os
import pathlib
import collections
Expand Down Expand Up @@ -144,6 +145,8 @@ def get_lint_args(self, rustc: RustCompiler) -> T.List[str]:
args.extend(lint.to_arguments(has_check_cfg))

if has_check_cfg:
args.append('--check-cfg')
args.append('cfg(test)')
for feature in self.manifest.features:
if feature != 'default':
args.append('--check-cfg')
Expand Down Expand Up @@ -426,6 +429,20 @@ def _prepare_package(self, pkg: PackageState) -> None:
for condition, dependencies in pkg.manifest.target.items():
if eval_cfg(condition, cfgs):
pkg.manifest.dependencies.update(dependencies)

# If you specify the optional dependency with the dep: prefix anywhere in the [features]
# table, that disables the implicit feature.
deps = set(feature[4:]
for feature in itertools.chain.from_iterable(pkg.manifest.features.values())
if feature.startswith('dep:'))
for name, dep in itertools.chain(pkg.manifest.dependencies.items(),
pkg.manifest.dev_dependencies.items(),
pkg.manifest.build_dependencies.items()):
if dep.optional and name not in deps:
pkg.manifest.features.setdefault(name, [])
pkg.manifest.features[name].append(f'dep:{name}')
deps.add(name)

# Fetch required dependencies recursively.
for depname, dep in pkg.manifest.dependencies.items():
if not dep.optional:
Expand Down Expand Up @@ -497,9 +514,6 @@ def _enable_feature(self, pkg: PackageState, feature: str) -> None:
if feature in cfg.features:
return
cfg.features.add(feature)
# A feature can also be a dependency.
if feature in pkg.manifest.dependencies:
self._add_dependency(pkg, feature)
# Recurse on extra features and dependencies this feature pulls.
# https://doc.rust-lang.org/cargo/reference/features.html#the-features-section
for f in pkg.manifest.features.get(feature, []):
Expand Down Expand Up @@ -825,7 +839,7 @@ def load_cargo_lock(filename: str, subproject_dir: str) -> T.Optional[CargoLock]
meson_depname = _dependency_name(package.name, version.api(package.version))
if package.source is None:
# This is project's package, or one of its workspace members.
pass
continue
elif package.source == 'registry+https://github.com/rust-lang/crates.io-index':
checksum = package.checksum
if checksum is None:
Expand Down
3 changes: 1 addition & 2 deletions mesonbuild/cargo/manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,8 +442,7 @@ def from_raw(cls, r: T.Union[raw.FromWorkspace, T.Dict[str, T.Dict[str, raw.Lint
settings = T.cast('raw.Lint', {'level': settings})
check_cfg = None
if name == 'unexpected_cfgs':
# 'cfg(test)' is added automatically by cargo
check_cfg = ['cfg(test)'] + settings.get('check-cfg', [])
check_cfg = settings.get('check-cfg', [])
lints[name] = Lint(name=name,
level=settings['level'],
priority=settings.get('priority', 0),
Expand Down
17 changes: 14 additions & 3 deletions mesonbuild/cargo/toml.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,28 @@ class TomlImplementationMissing(MesonException):
pass


class CargoTomlError(MesonException):
"""Exception for TOML parsing errors, keeping proper location info."""


def load_toml(filename: str) -> T.Dict[str, object]:
if tomllib:
with open(filename, 'rb') as f:
raw = tomllib.load(f)
try:
with open(filename, 'rb') as f:
raw = tomllib.load(f)
except tomllib.TOMLDecodeError as e:
if hasattr(e, 'msg'):
raise CargoTomlError(e.msg, file=filename, lineno=e.lineno, colno=e.colno) from e
else:
raise CargoTomlError(str(e), file=filename) from e
else:
if toml2json is None:
raise TomlImplementationMissing('Could not find an implementation of tomllib, nor toml2json')

p, out, err = Popen_safe([toml2json, filename])
if p.returncode != 0:
raise MesonException('toml2json failed to decode output\n', err)
error_msg = err.strip() or 'toml2json failed to decode TOML'
raise CargoTomlError(error_msg, file=filename)

raw = json.loads(out)

Expand Down
19 changes: 19 additions & 0 deletions test cases/failing/136 cargo toml error/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
project('cargo-toml-error', 'c')

if not add_languages('rust', required: false)
error('MESON_SKIP_TEST Rust not present, required for Cargo subprojets')
endif

# Check if we have tomllib/tomli (not toml2json)
python = find_program('python3', 'python')
result = run_command(python, '-c', 'import tomllib', check: false)
if result.returncode() != 0
result = run_command(python, '-c', 'import tomli', check: false)
if result.returncode() != 0
# Skip test if using toml2json - error format will be different
error('MESON_SKIP_TEST toml2json in use, skipping test')
endif
endif

# This should trigger a CargoTomlError with proper location info
foo_dep = dependency('foo-0')
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[wrap-file]
method = cargo

[provide]
dependency_names = foo-0
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "foo"
version = "0.0.1"
edition = "2021"
# This creates a TOML decode error: duplicate key
name = "bar"
8 changes: 8 additions & 0 deletions test cases/failing/136 cargo toml error/test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"stdout": [
{
"match": "re",
"line": "test cases/failing/136 cargo toml error/(subprojects/foo-0-rs/Cargo\\.toml:6:13|meson\\.build:19:10): ERROR: Cannot overwrite a value( \\(at.*)?"
}
]
}
5 changes: 2 additions & 3 deletions unittests/cargotests.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ def test_cargo_toml_lints(self) -> None:
self.assertEqual(manifest.lints[2].name, 'unexpected_cfgs')
self.assertEqual(manifest.lints[2].level, 'deny')
self.assertEqual(manifest.lints[2].priority, 0)
self.assertEqual(manifest.lints[2].check_cfg, ['cfg(test)', 'cfg(MESON)'])
self.assertEqual(manifest.lints[2].check_cfg, ['cfg(MESON)'])

def test_cargo_toml_lints_to_args(self) -> None:
with tempfile.TemporaryDirectory() as tmpdir:
Expand All @@ -444,8 +444,7 @@ def test_cargo_toml_lints_to_args(self) -> None:
self.assertEqual(manifest.lints[1].to_arguments(True), ['-A', 'unknown_lints'])
self.assertEqual(manifest.lints[2].to_arguments(False), ['-D', 'unexpected_cfgs'])
self.assertEqual(manifest.lints[2].to_arguments(True),
['-D', 'unexpected_cfgs', '--check-cfg', 'cfg(test)',
'--check-cfg', 'cfg(MESON)'])
['-D', 'unexpected_cfgs', '--check-cfg', 'cfg(MESON)'])

def test_cargo_toml_dependencies(self) -> None:
with tempfile.TemporaryDirectory() as tmpdir:
Expand Down
Loading