diff --git a/docs/markdown/Rust-module.md b/docs/markdown/Rust-module.md index b68fc6574884..57e65b0f4d6b 100644 --- a/docs/markdown/Rust-module.md +++ b/docs/markdown/Rust-module.md @@ -143,6 +143,23 @@ were never turned on by Meson. bindgen_clang_arguments = ['--target', 'x86_64-linux-gnu'] ``` +### compiler_target() + +*Since 1.11.0* + +```meson +rustmod.compiler_target() +rustmod.compiler_target(native: true) +``` + +Returns the Rust target triple (e.g. `x86_64-unknown-linux-gnu`) for the +Rust compiler. By default, or with `native: false`, this returns the +target for the host machine. If `native: true` is passed, it returns +the target for the build machine instead. + +This is useful when converting build scripts to Meson, because it +matches the value of Cargo's `TARGET` and `HOST` environment variables. + ### proc_macro() ```meson diff --git a/docs/markdown/snippets/rust_compiler_target.md b/docs/markdown/snippets/rust_compiler_target.md new file mode 100644 index 000000000000..b253932eb494 --- /dev/null +++ b/docs/markdown/snippets/rust_compiler_target.md @@ -0,0 +1,5 @@ +## `compiler_target()` method in the Rust module + +The Rust module has a `compiler_target()` method that can be useful +when converting build scripts to Meson, because its return value +matches the value of Cargo's `TARGET` and `HOST` environment variables. diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py index a0db544e4e7e..d111eccf6a86 100644 --- a/mesonbuild/compilers/rust.py +++ b/mesonbuild/compilers/rust.py @@ -12,7 +12,7 @@ import typing as T from .. import options -from ..mesonlib import EnvironmentException, MesonException, Popen_safe_logged, version_compare +from ..mesonlib import EnvironmentException, MesonException, Popen_safe, Popen_safe_logged, version_compare from ..linkers.linkers import VisualStudioLikeLinkerMixin from ..options import OptionKey from .compilers import Compiler, CompileCheckMode, clike_debug_args, is_library @@ -239,6 +239,20 @@ def get_cfgs(self) -> T.List[str]: p, stdo, stde = Popen_safe_logged(cmd) return stdo.splitlines() + @functools.lru_cache(maxsize=None) + def get_target_triple(self) -> str: + # First check if --target is explicitly set in the compiler command + target = parse_target(self.get_exe_args()) + if target: + return target + # Fall back to parsing the host triple from `rustc -vV` + cmd = self.get_exelist(ccache=False) + ['-vV'] + p, stdo, stde = Popen_safe(cmd) + for line in stdo.splitlines(): + if line.startswith('host:'): + return line.split(':', 1)[1].strip() + raise EnvironmentException('Could not determine Rust target triple') + @functools.lru_cache(maxsize=None) def get_crt_static(self) -> bool: return 'target_feature="crt-static"' in self.get_cfgs() diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index f1efed1968b4..1ec46d10506a 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -23,6 +23,11 @@ TestArgs = T.Union[str, File, build.Target, ExternalProgram] RustAbi = Literal['rust', 'c'] +class NativeKW(TypedDict): + + native: MachineChoice + + class FuncAddProjectArgs(TypedDict): """Keyword Arguments for the add_*_arguments family of arguments. diff --git a/mesonbuild/interpreter/mesonmain.py b/mesonbuild/interpreter/mesonmain.py index edc21434557e..dafabcbcbca5 100644 --- a/mesonbuild/interpreter/mesonmain.py +++ b/mesonbuild/interpreter/mesonmain.py @@ -30,6 +30,7 @@ from ..interpreterbase import TYPE_kwargs, TYPE_var from ..mesonlib import ExecutableSerialisation from .interpreter import Interpreter + from .kwargs import NativeKW class FuncOverrideDependency(TypedDict): @@ -42,10 +43,6 @@ class AddInstallScriptKW(TypedDict): install_tag: str dry_run: bool - class NativeKW(TypedDict): - - native: mesonlib.MachineChoice - class AddDevenvKW(TypedDict): method: Literal['set', 'prepend', 'append'] separator: str diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index 183b6fe5c11b..edbae3dedd77 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -19,7 +19,7 @@ from ..dependencies import Dependency from ..interpreter.type_checking import ( DEPENDENCIES_KW, LINK_WITH_KW, LINK_WHOLE_KW, SHARED_LIB_KWS, TEST_KWS, TEST_KWS_NO_ARGS, - OUTPUT_KW, INCLUDE_DIRECTORIES, SOURCES_VARARGS, NoneType, in_set_validator + OUTPUT_KW, INCLUDE_DIRECTORIES, SOURCES_VARARGS, NATIVE_KW, NoneType, in_set_validator, ) from ..interpreterbase import ContainerTypeInfo, InterpreterException, KwargInfo, typed_kwargs, typed_pos_args, noKwargs, noPosargs, permittedKwargs from ..interpreter.interpreterobjects import Doctest @@ -217,6 +217,7 @@ def __init__(self, interpreter: Interpreter) -> None: 'test': self.test, 'doctest': self.doctest, 'bindgen': self.bindgen, + 'compiler_target': self.compiler_target, 'proc_macro': self.proc_macro, 'workspace': self.workspace, }) @@ -612,6 +613,19 @@ def bindgen(self, state: ModuleState, args: T.List, kwargs: FuncBindgen) -> Modu return ModuleReturnValue(target, [target]) + @FeatureNew('rust.compiler_target', '1.11.0') + @noPosargs + @typed_kwargs('rust.compiler_target', NATIVE_KW) + def compiler_target(self, state: ModuleState, args: T.List, kwargs: '_kwargs.NativeKW') -> str: + """Returns the Rust target triple for the specified machine's Rust compiler.""" + for_machine = kwargs['native'] + compilers = state._interpreter.coredata.compilers[for_machine] + if 'rust' in compilers: + rustc = T.cast('RustCompiler', compilers['rust']) + return rustc.get_target_triple() + else: + raise MesonException(f'No Rust compiler was requested for the {for_machine} machine') + # Allow a limited set of kwargs, but still use the full set of typed_kwargs() # because it could be setting required default values. @FeatureNew('rust.proc_macro', '1.3.0') diff --git a/test cases/rust/36 compiles/meson.build b/test cases/rust/36 compiles/meson.build deleted file mode 100644 index efb916b335b6..000000000000 --- a/test cases/rust/36 compiles/meson.build +++ /dev/null @@ -1,13 +0,0 @@ -project('rust compiles', 'rust') - -rustc = meson.get_compiler('rust') - -# Test that compiles() works without and with a main function -assert(rustc.compiles('pub fn foo() {}'), 'compiles() without main should work') -assert(rustc.compiles('pub fn main() {}'), 'compiles() with main should work') - -# Test with a simple expression -assert(rustc.compiles('pub fn bar() { let _x: i32 = 1; }'), 'compiles() with simple code should work') - -# Test that invalid code fails -assert(not rustc.compiles('pub fn baz() { invalid_syntax }'), 'compiles() should fail on invalid code') diff --git a/test cases/rust/36 probes/meson.build b/test cases/rust/36 probes/meson.build new file mode 100644 index 000000000000..436f324e6800 --- /dev/null +++ b/test cases/rust/36 probes/meson.build @@ -0,0 +1,27 @@ +project('rust compiles', 'rust') + +rustc = meson.get_compiler('rust') + +# Test that compiles() works without and with a main function +assert(rustc.compiles('pub fn foo() {}'), 'compiles() without main should work') +assert(rustc.compiles('pub fn main() {}'), 'compiles() with main should work') + +# Test with a simple expression +assert(rustc.compiles('pub fn bar() { let _x: i32 = 1; }'), 'compiles() with simple code should work') + +# Test that invalid code fails +assert(not rustc.compiles('pub fn baz() { invalid_syntax }'), 'compiles() should fail on invalid code') + + +rustmod = import('rust') + +cargo_target = rustmod.compiler_target() +message('Host Rust target triple: ' + cargo_target) +assert(cargo_target != '', 'compiler_target() should return a non-empty string') +assert(cargo_target == rustmod.compiler_target(native: false), 'compiler_target(native: false) should match the default') +assert(cargo_target.contains('-'), 'compiler_target() should return a triple containing dashes') + +cargo_host = rustmod.compiler_target(native: true) +message('Build Rust target triple: ' + cargo_host) +assert(cargo_host != '', 'compiler_target(native: true) should return a non-empty string') +assert(cargo_host.contains('-'), 'compiler_target(native: true) should return a triple containing dashes')