Skip to content

Commit 6624a77

Browse files
committed
modules: rust: add package.library/package.proc_macro methods
1 parent da56d72 commit 6624a77

File tree

3 files changed

+191
-53
lines changed

3 files changed

+191
-53
lines changed

docs/markdown/Rust-module.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,36 @@ Keyword arguments:
379379
- `dev_dependencies`: (`bool`, default: false) Whether to include development dependencies (not yet implemented)
380380
- `system_dependencies`: (`bool`, default: true) Whether to include system dependencies
381381

382+
#### package.library()
383+
384+
```meson
385+
lib = pkg.library(...)
386+
```
387+
388+
Builds library targets for a workspace package. The method requires that
389+
the package's `Cargo.toml` file contains the `[lib]` section or that it
390+
is discovered from the contents of the file system. Static vs. shared library is
391+
decided based on the crate types in `Cargo.toml`
392+
393+
Positional arguments:
394+
- `target_name`: (`str`, optional) Name of the binary target to build.
395+
- `sources`: (`StructuredSources`, optional) Source files for the executable. If omitted,
396+
uses the path specified in the `[lib]` section of `Cargo.toml`.
397+
398+
Accepts all keyword arguments from [[shared_library]] and [[static_library]].
399+
`rust_abi` must match the crate types and is mandatory if more than one
400+
ABI is exposed by the crate.
401+
402+
#### package.proc_macro()
403+
404+
```meson
405+
lib = pkg.proc_macro(...)
406+
```
407+
408+
Builds a proc-macro crate for a workspace package.
409+
410+
Accepts all keyword arguments from [[shared_library]].
411+
382412
### Subprojects only
383413

384414
#### subproject.dependency()

mesonbuild/cargo/interpreter.py

Lines changed: 37 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,6 @@ def _dependency_name(package_name: str, api: str, suffix: str = '-rs') -> str:
4545
return f'{basename}-{api}{suffix}'
4646

4747

48-
def _library_name(name: str, api: str, lib_type: Literal['rust', 'c', 'proc-macro'] = 'rust') -> str:
49-
# Add the API version to the library name to avoid conflicts when multiple
50-
# versions of the same crate are used. The Ninja backend removed everything
51-
# after the + to form the crate name.
52-
if lib_type == 'c':
53-
return name
54-
return f'{name}+{api.replace(".", "_")}'
55-
56-
5748
def _extra_args_varname() -> str:
5849
return 'extra_args'
5950

@@ -85,7 +76,7 @@ def get_dependency_map(self, manifest: Manifest) -> T.Dict[str, str]:
8576
dep = manifest.dependencies[name]
8677
dep_key = PackageKey(dep.package, dep.api)
8778
dep_pkg = self.dep_packages[dep_key]
88-
dep_lib_name = _library_name(dep_pkg.manifest.lib.name, dep_pkg.manifest.package.api)
79+
dep_lib_name = dep_pkg.library_name()
8980
dep_crate_name = name if name != dep.package else dep_pkg.manifest.lib.name
9081
dependency_map[dep_lib_name] = dep_crate_name
9182
return dependency_map
@@ -107,6 +98,15 @@ def path(self) -> T.Optional[str]:
10798
return None
10899
return os.path.normpath(os.path.join(self.ws_subdir, self.ws_member))
109100

101+
def library_name(self, lib_type: RUST_ABI = 'rust') -> str:
102+
# Add the API version to the library name to avoid conflicts when multiple
103+
# versions of the same crate are used. The Ninja backend removed everything
104+
# after the + to form the crate name.
105+
name = fixup_meson_varname(self.manifest.package.name)
106+
if lib_type == 'c':
107+
return name
108+
return f'{name}+{self.manifest.package.api.replace(".", "_")}'
109+
110110
def get_env_dict(self, environment: Environment, subdir: str) -> T.Dict[str, str]:
111111
"""Get environment variables for this package."""
112112
# Common variables for build.rs and crates
@@ -201,17 +201,33 @@ def supported_abis(self) -> T.Set[RUST_ABI]:
201201
def get_subproject_name(self) -> str:
202202
return _dependency_name(self.manifest.package.name, self.manifest.package.api)
203203

204-
def get_dependency_name(self, rust_abi: T.Optional[RUST_ABI]) -> str:
205-
"""Get the dependency name for a package with the given ABI."""
204+
def abi_resolve_default(self, rust_abi: T.Optional[RUST_ABI]) -> RUST_ABI:
206205
supported_abis = self.supported_abis()
207206
if rust_abi is None:
208207
if len(supported_abis) > 1:
209208
raise MesonException(f'Package {self.manifest.package.name} support more than one ABI')
210-
rust_abi = next(iter(supported_abis))
209+
return next(iter(supported_abis))
211210
else:
212211
if rust_abi not in supported_abis:
213212
raise MesonException(f'Package {self.manifest.package.name} does not support ABI {rust_abi}')
213+
return rust_abi
214+
215+
def abi_has_shared(self, rust_abi: RUST_ABI) -> bool:
216+
if rust_abi == 'proc-macro':
217+
return True
218+
return ('cdylib' if rust_abi == 'c' else 'dylib') in self.manifest.lib.crate_type
219+
220+
def abi_has_static(self, rust_abi: RUST_ABI) -> bool:
221+
if rust_abi == 'proc-macro':
222+
return False
223+
crate_type = self.manifest.lib.crate_type
224+
if rust_abi == 'c':
225+
return 'staticlib' in crate_type
226+
return 'lib' in crate_type or 'rlib' in crate_type
214227

228+
def get_dependency_name(self, rust_abi: T.Optional[RUST_ABI]) -> str:
229+
"""Get the dependency name for a package with the given ABI."""
230+
rust_abi = self.abi_resolve_default(rust_abi)
215231
package_name = self.manifest.package.name
216232
api = self.manifest.package.api
217233

@@ -350,17 +366,8 @@ def _create_package(self, pkg: PackageState, build: builder.Builder, subdir: str
350366
crate_type = pkg.manifest.lib.crate_type
351367
if 'dylib' in crate_type and 'cdylib' in crate_type:
352368
raise MesonException('Cannot build both dylib and cdylib due to file name conflict')
353-
abis = pkg.supported_abis()
354-
if 'proc-macro' in abis:
355-
ast.extend(self._create_lib(pkg, build, subdir, 'proc-macro', shared=True))
356-
if 'rust' in abis:
357-
ast.extend(self._create_lib(pkg, build, subdir, 'rust',
358-
static=('lib' in crate_type or 'rlib' in crate_type),
359-
shared='dylib' in crate_type))
360-
if 'c' in abis:
361-
ast.extend(self._create_lib(pkg, build, subdir, 'c',
362-
static='staticlib' in crate_type,
363-
shared='cdylib' in crate_type))
369+
for abi in pkg.supported_abis():
370+
ast.extend(self._create_lib(pkg, build, subdir, abi))
364371

365372
return ast
366373

@@ -781,46 +788,24 @@ def _create_meson_subdir(self, build: builder.Builder) -> T.List[mparser.BaseNod
781788
]
782789

783790
def _create_lib(self, pkg: PackageState, build: builder.Builder, subdir: str,
784-
lib_type: RUST_ABI,
785-
static: bool = False, shared: bool = False) -> T.List[mparser.BaseNode]:
786-
pkg_dependencies = build.method('dependencies', build.identifier('pkg_obj'))
787-
extra_deps_ref = build.identifier(_extra_deps_varname())
788-
dependencies = build.plus(pkg_dependencies, extra_deps_ref)
789-
790-
package_rust_args = build.method('rust_args', build.identifier('pkg_obj'))
791-
extra_args_ref = build.identifier(_extra_args_varname())
792-
rust_args = build.plus(package_rust_args, extra_args_ref)
793-
794-
override_options: T.Dict[mparser.BaseNode, mparser.BaseNode] = {
795-
build.string('rust_std'): build.string(pkg.manifest.package.edition),
796-
}
797-
791+
lib_type: RUST_ABI) -> T.List[mparser.BaseNode]:
798792
posargs: T.List[mparser.BaseNode] = [
799-
build.string(_library_name(pkg.manifest.lib.name, pkg.manifest.package.api, lib_type)),
800-
build.string(pkg.manifest.lib.path),
793+
build.string(pkg.library_name(lib_type)),
801794
]
802795

803796
kwargs: T.Dict[str, mparser.BaseNode] = {
804-
'dependencies': dependencies,
805-
'rust_dependency_map': build.method('rust_dependency_map', build.identifier('pkg_obj')),
806-
'rust_args': rust_args,
807-
'override_options': build.dict(override_options),
797+
'dependencies': build.identifier(_extra_deps_varname()),
798+
'rust_args': build.identifier(_extra_args_varname()),
808799
}
809800

810801
depname_suffix = '' if lib_type == 'c' else '-rs'
811802
depname = _dependency_name(pkg.manifest.package.name, pkg.manifest.package.api, depname_suffix)
812803

813-
lib: mparser.BaseNode
814804
if lib_type == 'proc-macro':
815-
lib = build.method('proc_macro', build.identifier('rust'), posargs, kwargs)
805+
lib = build.method('proc_macro', build.identifier('pkg_obj'), posargs, kwargs)
816806
else:
817-
if static and shared:
818-
target_type = 'both_libraries'
819-
else:
820-
target_type = 'shared_library' if shared else 'static_library'
821-
822807
kwargs['rust_abi'] = build.string(lib_type)
823-
lib = build.function(target_type, posargs, kwargs)
808+
lib = build.method('library', build.identifier('pkg_obj'), posargs, kwargs)
824809

825810
# lib = xxx_library()
826811
# dep = declare_dependency()

mesonbuild/modules/rust.py

Lines changed: 124 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
from ..dependencies import Dependency
2020
from ..interpreter.type_checking import (
2121
DEPENDENCIES_KW, LINK_WITH_KW, LINK_WHOLE_KW, SHARED_LIB_KWS, TEST_KWS, TEST_KWS_NO_ARGS,
22-
OUTPUT_KW, INCLUDE_DIRECTORIES, SOURCES_VARARGS, NoneType, in_set_validator
22+
OUTPUT_KW, INCLUDE_DIRECTORIES, SOURCES_VARARGS, NoneType, in_set_validator,
23+
LIBRARY_KWS, _BASE_LANG_KW
2324
)
2425
from ..interpreterbase import ContainerTypeInfo, InterpreterException, KwargInfo, typed_kwargs, typed_pos_args, noKwargs, noPosargs, permittedKwargs
2526
from ..interpreter.interpreterobjects import Doctest
@@ -75,6 +76,11 @@ class FuncWorkspace(TypedDict):
7576
class FuncDependency(TypedDict):
7677
rust_abi: T.Optional[RUST_ABI]
7778

79+
class RustPackageLibrary(_kwargs.Library):
80+
dependencies: T.List[T.Union[Dependency, ExternalLibrary]]
81+
link_with: T.List[LibTypes]
82+
link_whole: T.List[LibTypes]
83+
7884
RUST_TEST_KWS: T.List[KwargInfo] = [
7985
KwargInfo(
8086
'rust_args',
@@ -234,6 +240,8 @@ def __init__(self, rust_ws: RustWorkspace, package: cargo.PackageState) -> None:
234240
super().__init__(rust_ws, package)
235241
self.methods.update({
236242
'dependencies': self.dependencies_method,
243+
'library': self.library_method,
244+
'proc_macro': self.proc_macro_method,
237245
})
238246

239247
@noPosargs
@@ -271,6 +279,121 @@ def dependencies_method(self, state: ModuleState, args: T.List, kwargs: T.Dict[s
271279

272280
return dependencies
273281

282+
@staticmethod
283+
def validate_pos_args(name: str, args: T.Tuple[
284+
T.Optional[T.Union[str, StructuredSources]],
285+
T.Optional[StructuredSources]]) -> T.Tuple[T.Optional[str], T.Optional[StructuredSources]]:
286+
if isinstance(args[0], str):
287+
return args[0], args[1]
288+
if args[1] is not None:
289+
raise MesonException(f"{name} only accepts one StructuredSources parameter")
290+
return None, args[0]
291+
292+
def merge_kw_args(self, state: ModuleState, kwargs: RustPackageLibrary) -> None:
293+
deps = kwargs['dependencies']
294+
kwargs['dependencies'] = self.dependencies_method(state, [], {})
295+
kwargs['dependencies'].extend(deps)
296+
297+
depmap = kwargs['rust_dependency_map']
298+
kwargs['rust_dependency_map'] = self.rust_dependency_map_method(state, [], {})
299+
kwargs['rust_dependency_map'].update(depmap)
300+
301+
rust_args = kwargs['rust_args']
302+
kwargs['rust_args'] = self.rust_args_method(state, [], {})
303+
kwargs['rust_args'].extend(rust_args)
304+
305+
kwargs['override_options'].setdefault('rust_std', self.package.manifest.package.edition)
306+
307+
def _library_method(self, state: ModuleState, args: T.Tuple[
308+
T.Optional[T.Union[str, StructuredSources]],
309+
T.Optional[StructuredSources]], kwargs: RustPackageLibrary,
310+
static: bool, shared: bool) -> T.Union[BothLibraries, SharedLibrary, StaticLibrary]:
311+
tgt_args = self.validate_pos_args('package.library', args)
312+
if not self.package.manifest.lib:
313+
raise MesonException("no [lib] section in Cargo package")
314+
315+
sources: T.Union[StructuredSources, str]
316+
tgt_name, sources = tgt_args
317+
if not tgt_name:
318+
rust_abi: RUST_ABI
319+
if kwargs['rust_crate_type'] is not None:
320+
rust_abi = 'rust' if kwargs['rust_crate_type'] in {'lib', 'rlib', 'dylib', 'proc-macro'} else 'c'
321+
else:
322+
rust_abi = kwargs['rust_abi']
323+
tgt_name = self.package.library_name(rust_abi)
324+
if not sources:
325+
sources = self.package.manifest.lib.path
326+
327+
lib_args: T.Tuple[str, SourcesVarargsType] = (tgt_name, [sources])
328+
self.merge_kw_args(state, kwargs)
329+
330+
if static and shared:
331+
return state._interpreter.build_both_libraries(state.current_node, lib_args, kwargs)
332+
elif shared:
333+
return state._interpreter.build_target(state.current_node, lib_args,
334+
T.cast('_kwargs.SharedLibrary', kwargs),
335+
SharedLibrary)
336+
else:
337+
return state._interpreter.build_target(state.current_node, lib_args,
338+
T.cast('_kwargs.StaticLibrary', kwargs),
339+
StaticLibrary)
340+
341+
def _proc_macro_method(self, state: 'ModuleState', args: T.Tuple[
342+
T.Optional[T.Union[str, StructuredSources]],
343+
T.Optional[StructuredSources]], kwargs: RustPackageLibrary) -> SharedLibrary:
344+
kwargs['native'] = MachineChoice.BUILD
345+
kwargs['rust_abi'] = None
346+
kwargs['rust_crate_type'] = 'proc-macro'
347+
kwargs['rust_args'] = kwargs['rust_args'] + ['--extern', 'proc_macro']
348+
result = self._library_method(state, args, kwargs, shared=True, static=False)
349+
return T.cast('SharedLibrary', result)
350+
351+
@typed_pos_args('package.library', optargs=[(str, StructuredSources), StructuredSources])
352+
@typed_kwargs(
353+
'package.library',
354+
*LIBRARY_KWS,
355+
DEPENDENCIES_KW,
356+
LINK_WITH_KW,
357+
LINK_WHOLE_KW,
358+
_BASE_LANG_KW.evolve(name='rust_args'),
359+
)
360+
def library_method(self, state: ModuleState, args: T.Tuple[
361+
T.Optional[T.Union[str, StructuredSources]],
362+
T.Optional[StructuredSources]], kwargs: RustPackageLibrary) -> T.Union[BothLibraries, SharedLibrary, StaticLibrary]:
363+
if not self.package.manifest.lib:
364+
raise MesonException("no [lib] section in Cargo package")
365+
if kwargs['rust_crate_type'] is not None:
366+
static = kwargs['rust_crate_type'] in {'lib', 'rlib', 'staticlib'}
367+
shared = kwargs['rust_crate_type'] in {'dylib', 'cdylib', 'proc-macro'}
368+
else:
369+
rust_abi = self.package.abi_resolve_default(kwargs['rust_abi'])
370+
static = self.package.abi_has_static(rust_abi)
371+
shared = self.package.abi_has_shared(rust_abi)
372+
if rust_abi == 'proc-macro':
373+
kwargs['rust_crate_type'] = 'proc-macro'
374+
kwargs['rust_abi'] = None
375+
else:
376+
kwargs['rust_abi'] = rust_abi
377+
return self._library_method(state, args, kwargs, static=static, shared=shared)
378+
379+
@typed_pos_args('package.proc_macro', optargs=[(str, StructuredSources), StructuredSources])
380+
@typed_kwargs(
381+
'package.proc_macro',
382+
*SHARED_LIB_KWS,
383+
DEPENDENCIES_KW,
384+
LINK_WITH_KW,
385+
LINK_WHOLE_KW,
386+
_BASE_LANG_KW.evolve(name='rust_args'),
387+
)
388+
def proc_macro_method(self, state: 'ModuleState', args: T.Tuple[
389+
T.Optional[T.Union[str, StructuredSources]],
390+
T.Optional[StructuredSources]], kwargs: RustPackageLibrary) -> SharedLibrary:
391+
if not self.package.manifest.lib:
392+
raise MesonException("no [lib] section in Cargo package")
393+
if 'proc-macro' not in self.package.manifest.lib.crate_type:
394+
raise MesonException("not a procedural macro crate")
395+
return self._proc_macro_method(state, args, kwargs)
396+
274397

275398
class RustSubproject(RustCrate):
276399
"""Represents a Cargo subproject."""

0 commit comments

Comments
 (0)