Skip to content

Rust: Replace rust_crate_type with rust_abi #11714

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Sep 19, 2023
Merged
29 changes: 29 additions & 0 deletions docs/markdown/Rust-module.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,32 @@ were never turned on by Meson.
[properties]
bindgen_clang_arguments = ['--target', 'x86_64-linux-gnu']
```

### proc_macro()

```meson
rustmod.proc_macro(name, sources, ...)
```

*Since 1.3.0*

This function creates a Rust `proc-macro` crate, similar to:
```meson
[[shared_library]](name, sources,
rust_crate_type: 'proc-macro',
native: true)
```

`proc-macro` targets can be passed to `link_with` keyword argument of other Rust
targets.

Only a subset of [[shared_library]] keyword arguments are allowed:
- rust_args
- rust_dependency_map
- sources
- dependencies
- extra_files
- link_args
- link_depends
- link_with
- override_options
11 changes: 11 additions & 0 deletions docs/markdown/snippets/rust_crate_type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
## Deprecated `rust_crate_type` and replaced by `rust_abi`

The new `rust_abi` keyword argument is accepted by [[shared_library]],
[[static_library]], [[library]] and [[shared_module]] functions. It can be either
`'rust'` (the default) or `'c'` strings.

`rust_crate_type` is now deprecated because Meson already knows if it's a shared
or static library, user only need to specify the ABI (Rust or C).

`proc_macro` crates are now handled by the [`rust.proc_macro()`](Rust-module.md#proc_macro)
method.
5 changes: 5 additions & 0 deletions docs/yaml/functions/_build_target_base.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ kwargs:
rust_crate_type:
type: str
since: 0.42.0
deprecated: 1.3.0
description: |
Set the specific type of rust crate to compile (when compiling rust).

Expand All @@ -306,6 +307,10 @@ kwargs:

"proc-macro" is new in 0.62.0.

*Since 1.3.0* this is deprecated and replaced by "rust_abi" keyword argument.
`proc_macro` crates are now handled by the [`rust.proc_macro()`](Rust-module.md#proc_macro)
method.

rust_dependency_map:
type: dict[str]
since: 1.2.0
Expand Down
11 changes: 11 additions & 0 deletions docs/yaml/functions/library.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,14 @@ varargs_inherit: _build_target_base
kwargs_inherit:
- shared_library
- static_library

kwargs:
rust_abi:
type: str
since: 1.3.0
description: |
Set the specific ABI to compile (when compiling rust).
- 'rust' (default): Create a "rlib" or "dylib" crate depending on the library
type being build.
- 'c': Create a "cdylib" or "staticlib" crate depending on the library
type being build.
8 changes: 8 additions & 0 deletions docs/yaml/functions/shared_library.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,11 @@ kwargs:
description: |
Specify a Microsoft module definition file for controlling symbol exports,
etc., on platforms where that is possible (e.g. Windows).

rust_abi:
type: str
since: 1.3.0
description: |
Set the specific ABI to compile (when compiling rust).
- 'rust' (default): Create a "dylib" crate.
- 'c': Create a "cdylib" crate.
8 changes: 8 additions & 0 deletions docs/yaml/functions/shared_module.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,11 @@ kwargs:
description: |
Specify a Microsoft module definition file for controlling symbol exports,
etc., on platforms where that is possible (e.g. Windows).

rust_abi:
type: str
since: 1.3.0
description: |
Set the specific ABI to compile (when compiling rust).
- 'rust' (default): Create a "dylib" crate.
- 'c': Create a "cdylib" crate.
8 changes: 8 additions & 0 deletions docs/yaml/functions/static_library.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,11 @@ kwargs:
If `true` the object files in the target will be prelinked,
meaning that it will contain only one prelinked
object file rather than the individual object files.

rust_abi:
type: str
since: 1.3.0
description: |
Set the specific ABI to compile (when compiling rust).
- 'rust' (default): Create a "rlib" crate.
- 'c': Create a "staticlib" crate.
16 changes: 3 additions & 13 deletions mesonbuild/backend/ninjabackend.py
Original file line number Diff line number Diff line change
Expand Up @@ -1937,16 +1937,7 @@ def generate_rust_target(self, target: build.BuildTarget) -> None:
if main_rust_file is None:
raise RuntimeError('A Rust target has no Rust sources. This is weird. Also a bug. Please report')
target_name = os.path.join(target.subdir, target.get_filename())
if isinstance(target, build.Executable):
cratetype = 'bin'
elif hasattr(target, 'rust_crate_type'):
cratetype = target.rust_crate_type
elif isinstance(target, build.SharedLibrary):
cratetype = 'dylib'
elif isinstance(target, build.StaticLibrary):
cratetype = 'rlib'
else:
raise InvalidArguments('Unknown target type for rustc.')
cratetype = target.rust_crate_type
args.extend(['--crate-type', cratetype])

# If we're dynamically linking, add those arguments
Expand All @@ -1960,10 +1951,9 @@ def generate_rust_target(self, target: build.BuildTarget) -> None:
# Rustc replaces - with _. spaces or dots are not allowed, so we replace them with underscores
args += ['--crate-name', target.name.replace('-', '_').replace(' ', '_').replace('.', '_')]
depfile = os.path.join(target.subdir, target.name + '.d')
args += ['--emit', f'dep-info={depfile}', '--emit', 'link']
args += ['--emit', f'dep-info={depfile}', '--emit', f'link={target_name}']
args += ['--out-dir', self.get_target_private_dir(target)]
args += target.get_extra_args('rust')
output = rustc.get_output_args(os.path.join(target.subdir, target.get_filename()))
args += output
linkdirs = mesonlib.OrderedSet()
external_deps = target.external_deps.copy()

Expand Down
106 changes: 59 additions & 47 deletions mesonbuild/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,9 @@
cs_kwargs)

known_exe_kwargs = known_build_target_kwargs | {'implib', 'export_dynamic', 'pie'}
known_shlib_kwargs = known_build_target_kwargs | {'version', 'soversion', 'vs_module_defs', 'darwin_versions'}
known_shmod_kwargs = known_build_target_kwargs | {'vs_module_defs'}
known_stlib_kwargs = known_build_target_kwargs | {'pic', 'prelink'}
known_shlib_kwargs = known_build_target_kwargs | {'version', 'soversion', 'vs_module_defs', 'darwin_versions', 'rust_abi'}
known_shmod_kwargs = known_build_target_kwargs | {'vs_module_defs', 'rust_abi'}
known_stlib_kwargs = known_build_target_kwargs | {'pic', 'prelink', 'rust_abi'}
known_jar_kwargs = known_exe_kwargs | {'main_class', 'java_resources'}

def _process_install_tag(install_tag: T.Optional[T.List[T.Optional[str]]],
Expand Down Expand Up @@ -1408,12 +1408,7 @@ def link(self, targets):
msg = f"Can't link non-PIC static library {t.name!r} into shared library {self.name!r}. "
msg += "Use the 'pic' option to static_library to build with PIC."
raise InvalidArguments(msg)
if self.for_machine is not t.for_machine:
msg = f'Tried to mix libraries for machines {self.for_machine} and {t.for_machine} in target {self.name!r}'
if self.environment.is_cross_build():
raise InvalidArguments(msg + ' This is not possible in a cross build.')
else:
mlog.warning(msg + ' This will fail in cross build.')
self.check_can_link_together(t)
self.link_targets.append(t)

def link_whole(self, targets, promoted: bool = False):
Expand All @@ -1429,12 +1424,7 @@ def link_whole(self, targets, promoted: bool = False):
msg = f"Can't link non-PIC static library {t.name!r} into shared library {self.name!r}. "
msg += "Use the 'pic' option to static_library to build with PIC."
raise InvalidArguments(msg)
if self.for_machine is not t.for_machine:
msg = f'Tried to mix libraries for machines {self.for_machine} and {t.for_machine} in target {self.name!r}'
if self.environment.is_cross_build():
raise InvalidArguments(msg + ' This is not possible in a cross build.')
else:
mlog.warning(msg + ' This will fail in cross build.')
self.check_can_link_together(t)
if isinstance(self, StaticLibrary) and not self.uses_rust():
# When we're a static library and we link_whole: to another static
# library, we need to add that target's objects to ourselves.
Expand Down Expand Up @@ -1480,6 +1470,17 @@ def check_can_extract_objects(self, t: T.Union[Target, CustomTargetIndex], origi
f' and thus has to include objects from {t.name!r} to be usable.')
raise InvalidArguments(m)

def check_can_link_together(self, t: BuildTargetTypes) -> None:
links_with_rust_abi = isinstance(t, BuildTarget) and t.uses_rust_abi()
if not self.uses_rust() and links_with_rust_abi:
raise InvalidArguments(f'Try to link Rust ABI library {t.name!r} with a non-Rust target {self.name!r}')
if self.for_machine is not t.for_machine and (not links_with_rust_abi or t.rust_crate_type != 'proc-macro'):
msg = f'Tried to mix libraries for machines {self.for_machine} and {t.for_machine} in target {self.name!r}'
if self.environment.is_cross_build():
raise InvalidArguments(msg + ' This is not possible in a cross build.')
else:
mlog.warning(msg + ' This will fail in cross build.')

def add_pch(self, language: str, pchlist: T.List[str]) -> None:
if not pchlist:
return
Expand Down Expand Up @@ -1645,6 +1646,9 @@ def get_used_stdlib_args(self, link_language: str) -> T.List[str]:
def uses_rust(self) -> bool:
return 'rust' in self.compilers

def uses_rust_abi(self) -> bool:
return self.uses_rust() and self.rust_crate_type in {'dylib', 'rlib', 'proc-macro'}

def uses_fortran(self) -> bool:
return 'fortran' in self.compilers

Expand Down Expand Up @@ -1980,6 +1984,13 @@ def post_init(self) -> None:
if create_debug_file:
self.debug_filename = self.name + '.pdb'

def process_kwargs(self, kwargs):
super().process_kwargs(kwargs)

self.rust_crate_type = kwargs.get('rust_crate_type') or 'bin'
if self.rust_crate_type != 'bin':
raise InvalidArguments('Invalid rust_crate_type: must be "bin" for executables.')

def get_default_install_dir(self) -> T.Tuple[str, str]:
return self.environment.get_bindir(), '{bindir}'

Expand Down Expand Up @@ -2052,18 +2063,12 @@ def post_init(self) -> None:
super().post_init()
if 'cs' in self.compilers:
raise InvalidArguments('Static libraries not supported for C#.')
if 'rust' in self.compilers:
# If no crate type is specified, or it's the generic lib type, use rlib
if not hasattr(self, 'rust_crate_type') or self.rust_crate_type == 'lib':
mlog.debug('Defaulting Rust static library target crate type to rlib')
self.rust_crate_type = 'rlib'
# Don't let configuration proceed with a non-static crate type
elif self.rust_crate_type not in ['rlib', 'staticlib']:
raise InvalidArguments(f'Crate type "{self.rust_crate_type}" invalid for static libraries; must be "rlib" or "staticlib"')
if self.uses_rust():
# See https://github.com/rust-lang/rust/issues/110460
if self.rust_crate_type == 'rlib' and any(c in self.name for c in ['-', ' ', '.']):
raise InvalidArguments('Rust crate type "rlib" does not allow spaces, periods or dashes in the library name '
'due to a limitation of rustc. Replace them with underscores, for example')
raise InvalidArguments(f'Rust crate {self.name} type {self.rust_crate_type} does not allow spaces, '
'periods or dashes in the library name due to a limitation of rustc. '
'Replace them with underscores, for example')
if self.rust_crate_type == 'staticlib':
# FIXME: In the case of no-std we should not add those libraries,
# but we have no way to know currently.
Expand All @@ -2085,8 +2090,8 @@ def post_init(self) -> None:
if not hasattr(self, 'prefix'):
self.prefix = 'lib'
if not hasattr(self, 'suffix'):
if 'rust' in self.compilers:
if not hasattr(self, 'rust_crate_type') or self.rust_crate_type == 'rlib':
if self.uses_rust():
if self.rust_crate_type == 'rlib':
# default Rust static library suffix
self.suffix = 'rlib'
elif self.rust_crate_type == 'staticlib':
Expand All @@ -2107,12 +2112,20 @@ def type_suffix(self):

def process_kwargs(self, kwargs):
super().process_kwargs(kwargs)
if 'rust_crate_type' in kwargs:
rust_crate_type = kwargs['rust_crate_type']
if isinstance(rust_crate_type, str):

rust_abi = kwargs.get('rust_abi')
rust_crate_type = kwargs.get('rust_crate_type')
if rust_crate_type:
if rust_abi:
raise InvalidArguments('rust_abi and rust_crate_type are mutually exclusive.')
if rust_crate_type == 'lib':
self.rust_crate_type = 'rlib'
elif rust_crate_type in {'rlib', 'staticlib'}:
self.rust_crate_type = rust_crate_type
else:
raise InvalidArguments(f'Invalid rust_crate_type "{rust_crate_type}": must be a string.')
raise InvalidArguments(f'Crate type {rust_crate_type!r} invalid for static libraries; must be "rlib" or "staticlib"')
else:
self.rust_crate_type = 'staticlib' if rust_abi == 'c' else 'rlib'

def is_linkable_target(self):
return True
Expand Down Expand Up @@ -2153,18 +2166,12 @@ def __init__(

def post_init(self) -> None:
super().post_init()
if 'rust' in self.compilers:
# If no crate type is specified, or it's the generic lib type, use dylib
if not hasattr(self, 'rust_crate_type') or self.rust_crate_type == 'lib':
mlog.debug('Defaulting Rust dynamic library target crate type to "dylib"')
self.rust_crate_type = 'dylib'
# Don't let configuration proceed with a non-dynamic crate type
elif self.rust_crate_type not in ['dylib', 'cdylib', 'proc-macro']:
raise InvalidArguments(f'Crate type "{self.rust_crate_type}" invalid for dynamic libraries; must be "dylib", "cdylib", or "proc-macro"')
if self.uses_rust():
# See https://github.com/rust-lang/rust/issues/110460
if self.rust_crate_type != 'cdylib' and any(c in self.name for c in ['-', ' ', '.']):
raise InvalidArguments('Rust crate types "dylib" and "proc-macro" do not allow spaces, periods or dashes in the library name '
'due to a limitation of rustc. Replace them with underscores, for example')
raise InvalidArguments(f'Rust crate {self.name} type {self.rust_crate_type} does not allow spaces, '
'periods or dashes in the library name due to a limitation of rustc. '
'Replace them with underscores, for example')

if not hasattr(self, 'prefix'):
self.prefix = None
Expand Down Expand Up @@ -2335,14 +2342,19 @@ def process_kwargs(self, kwargs):
'a file object or a Custom Target')
self.process_link_depends(path)

if 'rust_crate_type' in kwargs:
rust_crate_type = kwargs['rust_crate_type']
if isinstance(rust_crate_type, str):
rust_abi = kwargs.get('rust_abi')
rust_crate_type = kwargs.get('rust_crate_type')
if rust_crate_type:
if rust_abi:
raise InvalidArguments('rust_abi and rust_crate_type are mutually exclusive.')
if rust_crate_type == 'lib':
self.rust_crate_type = 'dylib'
elif rust_crate_type in {'dylib', 'cdylib', 'proc-macro'}:
self.rust_crate_type = rust_crate_type
else:
raise InvalidArguments(f'Invalid rust_crate_type "{rust_crate_type}": must be a string.')
if rust_crate_type == 'proc-macro':
FeatureNew.single_use('Rust crate type "proc-macro"', '0.62.0', self.subproject)
raise InvalidArguments(f'Crate type {rust_crate_type!r} invalid for shared libraries; must be "dylib", "cdylib" or "proc-macro"')
else:
self.rust_crate_type = 'cdylib' if rust_abi == 'c' else 'dylib'

def get_import_filename(self) -> T.Optional[str]:
"""
Expand Down
16 changes: 5 additions & 11 deletions mesonbuild/compilers/rust.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
import re
import typing as T

from .. import coredata, mlog
from ..mesonlib import EnvironmentException, MesonException, Popen_safe, OptionKey, join_args
from .. import coredata
from ..mesonlib import EnvironmentException, MesonException, Popen_safe_logged, OptionKey
from .compilers import Compiler, rust_buildtype_args, clike_debug_args

if T.TYPE_CHECKING:
Expand Down Expand Up @@ -82,13 +82,7 @@ def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
'''))

cmdlist = self.exelist + ['-o', output_name, source_name]
pc, stdo, stde = Popen_safe(cmdlist, cwd=work_dir)
mlog.debug('Sanity check compiler command line:', join_args(cmdlist))
mlog.debug('Sanity check compile stdout:')
mlog.debug(stdo)
mlog.debug('-----\nSanity check compile stderr:')
mlog.debug(stde)
mlog.debug('-----')
pc, stdo, stde = Popen_safe_logged(cmdlist, cwd=work_dir)
if pc.returncode != 0:
raise EnvironmentException(f'Rust compiler {self.name_string()} cannot compile programs.')
if self.is_cross:
Expand All @@ -104,7 +98,7 @@ def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
raise EnvironmentException(f'Executables created by Rust compiler {self.name_string()} are not runnable.')
# Get libraries needed to link with a Rust staticlib
cmdlist = self.exelist + ['--crate-type', 'staticlib', '--print', 'native-static-libs', source_name]
p, stdo, stde = Popen_safe(cmdlist, cwd=work_dir)
p, stdo, stde = Popen_safe_logged(cmdlist, cwd=work_dir)
if p.returncode == 0:
match = re.search('native-static-libs: (.*)$', stde, re.MULTILINE)
if match:
Expand All @@ -123,7 +117,7 @@ def get_buildtype_args(self, buildtype: str) -> T.List[str]:

def get_sysroot(self) -> str:
cmd = self.get_exelist(ccache=False) + ['--print', 'sysroot']
p, stdo, stde = Popen_safe(cmd)
p, stdo, stde = Popen_safe_logged(cmd)
return stdo.split('\n', maxsplit=1)[0]

def get_debug_args(self, is_debug: bool) -> T.List[str]:
Expand Down
Loading