cargo: apply profile to targets, via override_options

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Paolo Bonzini authored and Paolo Bonzini committed 2026-09-07 18:48:51 +02:00
1 parent b4b39dee4a
commit c6cbfbbf68
6 files changed
+113 -5

No files matched your search

@@ -19,3 +19,6 @@ settings found in a Cargo `[profile]` section can be expressed natively:
The remaining Cargo profile keys map onto existing built-in options:
`opt-level` to `optimization`, `debug` to `debug`, `debug-assertions`
to `b_ndebug` and `lto` to `b_lto`.
The Cargo workspace object will also add these automatically based on
the value of another new option, `rust_cargo_profile`.
+44 -3
View File
@@ -40,9 +40,10 @@ if T.TYPE_CHECKING:
from .. import mparser
from typing_extensions import Literal
from .manifest import Dependency
from .manifest import Dependency, Profile
from ..environment import Environment
from ..compilers.rust import RustCompiler
from ..options import ElementaryOptionValues
RUST_ABI = Literal['rust', 'c', 'proc-macro']
@@ -282,6 +283,8 @@ class Interpreter:
self.packages: T.Dict[PackageKey, PackageState] = {}
# Map subdir to workspace
self.workspaces: T.Dict[str, WorkspaceState] = {}
# [profile] tables from the top-level Cargo.toml
self.profiles: T.Dict[str, Profile] = {}
# Files that should trigger a reconfigure if modified
self.build_def_files: T.List[str] = []
# Cargo packages
@@ -321,9 +324,8 @@ class Interpreter:
# [patch] only takes effect in the top-level Cargo.toml
for warning in validate_patch(ws.workspace.patch, ws.packages_to_member):
mlog.warning(warning)
if ws.workspace.profile:
mlog.warning('[profile] entries are not implemented yet')
self.profiles = ws.workspace.profile
self._prepare_entry_point(ws)
return ws
@@ -350,6 +352,45 @@ class Interpreter:
raise MesonException('argument to package() cannot be a subproject')
return ws.packages[path]
def _selected_profile(self, override: T.Optional[ElementaryOptionValues] = None) -> T.Optional[str]:
"""Resolve the rust_cargo_profile selection to a Cargo profile name.
override takes precedence over the rust_cargo_profile option, and
comes from a target's own override_options."""
optstore = self.environment.coredata.optstore
if override is not None:
selection = override
else:
try:
selection = optstore.get_value_for('rust_cargo_profile')
except KeyError:
return None
assert isinstance(selection, str)
if selection == 'none':
return None
if selection != 'from_buildtype':
return selection
buildtype = optstore.get_value_for('buildtype')
if buildtype in {'plain', 'custom'}:
return None
return 'dev' if buildtype == 'debug' else 'release'
def get_override_options(self, pkg: PackageState, for_machine: MachineChoice,
profile: str | None = None) -> T.Dict[str, ElementaryOptionValues]:
"""Return override_options implied by the Cargo manifest: the package
edition (rust_std) and the options implied by a [profile], using
``profile`` if not None or otherwise rust_cargo_profile selects."""
opts: T.Dict[str, ElementaryOptionValues] = {
'rust_std': pkg.manifest.package.edition,
}
profile_name = self._selected_profile(profile)
if profile_name is not None and self.profiles is not None:
if (profile_obj := self.profiles.get(profile_name)) is not None:
opts.update(profile_obj.to_meson_options(for_machine))
return opts
def interpret(self, subdir: str, project_root: T.Optional[str] = None) -> mparser.CodeBlockNode:
filename = os.path.join(self.environment.source_dir, subdir, 'Cargo.toml')
build = builder.Builder(filename)
+31
View File
@@ -23,6 +23,7 @@ if T.TYPE_CHECKING:
from . import raw
from .raw import EDITION, CRATE_TYPE, LINT_LEVEL
from ..options import ElementaryOptionValues
from ..wrap.wrap import PackageDefinition
# Copied from typeshed. Blarg that they don't expose this
@@ -550,6 +551,36 @@ class Profile:
profile.lto_mode = 'thin' if raw_profile.get('lto') == 'thin' else 'default'
return profile
def to_meson_options(self, for_machine: MachineChoice) -> T.Dict[str, ElementaryOptionValues]:
"""Map the profile onto Meson option values, only for keys that are set.
For the build machine, the [build-override] settings are layered on top
(Cargo applies those to build scripts, proc macros and their deps)."""
opts: T.Dict[str, ElementaryOptionValues] = {}
if self.opt_level is not None:
# Meson's 'optimization' has no 'z'; fall back to 's'.
opts['optimization'] = 's' if self.opt_level == 'z' else self.opt_level
if self.debug is not None:
opts['debug'] = self.debug
# Meson only handles strip at install time
if self.debug_assertions is not None:
# note inverted polarity
opts['b_ndebug'] = 'false' if self.debug_assertions else 'true'
if self.overflow_checks is not None:
opts['rust_overflow_checks'] = self.overflow_checks
if self.lto is not None:
opts['b_lto'] = self.lto
if self.lto_mode is not None:
opts['b_lto_mode'] = self.lto_mode
if self.panic is not None:
opts['rust_panic'] = self.panic
if self.incremental is not None:
opts['rust_incremental'] = self.incremental
if self.codegen_units is not None:
opts['rust_codegen_units'] = self.codegen_units
if for_machine is MachineChoice.BUILD and self.build_override is not None:
opts.update(self.build_override.to_meson_options(for_machine))
return opts
@dataclasses.dataclass
class Manifest:
+7
View File
@@ -404,6 +404,13 @@ class RustCompiler(Compiler):
'Directory used to store incremental compilation data (empty=pick a default under the build directory)',
'')
key = self.form_compileropt_key('cargo_profile')
opts[key] = options.UserComboOption(
self.make_option_name(key),
'Cargo [profile] to apply when building Cargo subprojects',
'from_buildtype',
choices=['none', 'dev', 'release', 'from_buildtype'])
return opts
def get_dependency_compile_args(self, dep: 'Dependency') -> T.List[str]:
+5 -1
View File
@@ -391,7 +391,11 @@ class RustPackage(RustCrate):
self.package.get_rustc_args(state.environment, state.subdir, kwargs['native'])
kwargs['rust_args'].extend(rust_args)
kwargs['override_options'].setdefault('rust_std', self.package.manifest.package.edition)
profile = kwargs['override_options'].get('rust_cargo_profile')
assert profile is None or isinstance(profile, str), 'for mypy'
overrides = self.rust_ws.interpreter.cargo.get_override_options(self.package, kwargs['native'], profile)
for key, value in overrides.items():
kwargs['override_options'].setdefault(key, value)
def _library_method(self, state: ModuleState, args: T.Tuple[
T.Optional[T.Union[str, StructuredSources]],
+23 -1
View File
@@ -14,7 +14,7 @@ from mesonbuild.cargo.interpreter import load_cargo_lock
from mesonbuild.cargo.manifest import Dependency, Lint, Manifest, Package, Workspace, validate_patch
from mesonbuild.cargo.toml import load_toml
from mesonbuild.cargo.version import api, cargo_parse, SemVer
from mesonbuild.mesonlib import MesonException
from mesonbuild.mesonlib import MachineChoice, MesonException
class CargoVersionTest(unittest.TestCase):
@@ -746,6 +746,28 @@ class CargoTomlTest(unittest.TestCase):
self.assertEqual(manifest.profile['dev'].opt_level, 's')
def test_cargo_toml_profile_to_meson_options(self) -> None:
with tempfile.TemporaryDirectory() as tmpdir:
fname = os.path.join(tmpdir, 'Cargo.toml')
with open(fname, 'w', encoding='utf-8') as f:
f.write(self.CARGO_TOML_PROFILE)
manifest = Manifest.from_raw(load_toml(fname), 'Cargo.toml')
release = manifest.profile['release']
host = release.to_meson_options(MachineChoice.HOST)
self.assertEqual(host['optimization'], '3')
self.assertEqual(host['rust_codegen_units'], 16)
self.assertEqual(host['b_lto_mode'], 'thin')
self.assertEqual(host['rust_panic'], 'abort')
build = release.to_meson_options(MachineChoice.BUILD)
# On the build machine the [build-override] keys are layered on top.
self.assertEqual(build['optimization'], '0')
self.assertEqual(build['rust_codegen_units'], 256)
# keys not set by build-override stay from the base profile
self.assertEqual(build['b_lto_mode'], 'thin')
self.assertEqual(build['rust_panic'], 'abort')
def test_validate_patch(self) -> None:
# packages_to_member values are normalized with os.path.normpath (see
# _load_workspace_member), so do the same here