From e2e74ee371df0cdc940901e7ea68a35970f54033 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 7 Mar 2022 10:16:27 -0800 Subject: [PATCH 01/15] mesonlib: Add a from_node initializer to MesonException This is useful when creating an exception with a node option, with less typing. --- mesonbuild/mesonlib/universal.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/mesonbuild/mesonlib/universal.py b/mesonbuild/mesonlib/universal.py index 41bd1f296..99bcd320a 100644 --- a/mesonbuild/mesonlib/universal.py +++ b/mesonbuild/mesonlib/universal.py @@ -41,6 +41,7 @@ if T.TYPE_CHECKING: from ..build import ConfigurationData from ..coredata import KeyedOptionDictType, UserOption from ..compilers.compilers import Compiler + from ..mparser import BaseNode FileOrString = T.Union['File', str] @@ -171,6 +172,15 @@ class MesonException(Exception): self.lineno = lineno self.colno = colno + @classmethod + def from_node(cls, *args: object, node: BaseNode) -> MesonException: + """Create a MesonException with location data from a BaseNode + + :param node: A BaseNode to set location data from + :return: A Meson Exception instance + """ + return cls(*args, file=node.filename, lineno=node.lineno, colno=node.colno) + class MesonBugException(MesonException): '''Exceptions thrown when there is a clear Meson bug that should be reported''' From df2a437b5be468e4ca6920cbee2e1172e95f17df Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 2 Mar 2022 10:04:50 -0800 Subject: [PATCH 02/15] build: fix type annotation issue I noticed by inspection --- mesonbuild/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 6916bcbf1..54ad74018 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -426,7 +426,7 @@ class ExtractedObjects(HoldableObject): # Filter out headers and all non-source files return [s for s in sources if environment.is_source(s) and not environment.is_header(s)] - def classify_all_sources(self, sources: T.List[str], generated_sources: T.Sequence['GeneratedTypes']) -> T.Dict['Compiler', T.List['FileOrString']]: + def classify_all_sources(self, sources: T.List[FileOrString], generated_sources: T.Sequence['GeneratedTypes']) -> T.Dict['Compiler', T.List['FileOrString']]: sources_ = self.get_sources(sources, generated_sources) return classify_unity_sources(self.target.compilers.values(), sources_) From aa03a0c346720cd78135c05fafc5671b5e108d5b Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 2 Mar 2022 10:05:18 -0800 Subject: [PATCH 03/15] build: fix typo in type alias The declaration is `EnvInitValueType`, but when it's used it's `EnvValueType`. --- mesonbuild/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 54ad74018..ecbc1d981 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -455,7 +455,7 @@ class ExtractedObjects(HoldableObject): EnvInitValueType = T.Dict[str, T.Union[str, T.List[str]]] class EnvironmentVariables(HoldableObject): - def __init__(self, values: T.Optional[EnvValueType] = None, + def __init__(self, values: T.Optional[EnvInitValueType] = None, init_method: Literal['set', 'prepend', 'append'] = 'set', separator: str = os.pathsep) -> None: self.envvars: T.List[T.Tuple[T.Callable[[T.Dict[str, str], str, T.List[str], str], str], str, T.List[str], str]] = [] # The set of all env vars we have operations for. Only used for self.has_name() From 9519a5b7fd0f12ee60bd62a5c6a639e3230ace73 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 4 May 2021 13:04:20 -0700 Subject: [PATCH 04/15] scripts: Add a script to copy files. --- mesonbuild/scripts/copy.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 mesonbuild/scripts/copy.py diff --git a/mesonbuild/scripts/copy.py b/mesonbuild/scripts/copy.py new file mode 100644 index 000000000..acef2a89b --- /dev/null +++ b/mesonbuild/scripts/copy.py @@ -0,0 +1,18 @@ +# SPDX-License-Identifer: Apache-2.0 +# Copyright © 2021 Intel Corporation + +"""Helper script to copy files at build time. + +This is easier than trying to detect whether to use copy, cp, or something else. +""" + +import shutil +import typing as T + + +def run(args: T.List[str]) -> int: + try: + shutil.copy2(args[0], args[1]) + except Exception: + return 1 + return 0 From da2cdacb28f1326799b6c3e039b4c2ea6cf3c35b Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 1 Oct 2021 09:47:48 -0700 Subject: [PATCH 05/15] backend/ninja: add rules to copy files to the build dir --- mesonbuild/backend/ninjabackend.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index a66111b01..a8616b822 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1176,6 +1176,8 @@ class NinjaBackend(backends.Backend): self.add_rule(NinjaRule('CUSTOM_COMMAND_DEP', ['$COMMAND'], [], '$DESC', deps='gcc', depfile='$DEPFILE', extra='restat = 1')) + self.add_rule(NinjaRule('COPY_FILE', self.environment.get_build_command() + ['--internal', 'copy'], + ['$in', '$out'], 'Copying $in to $out')) c = self.environment.get_build_command() + \ ['--internal', @@ -1650,6 +1652,16 @@ class NinjaBackend(backends.Backend): return static_sources, generated_sources, cython_sources + def _generate_copy_target(self, src: 'mesonlib.FileOrString', output: Path) -> None: + """Create a target to copy a source file from one location to another.""" + if isinstance(src, File): + instr = src.absolute_path(self.environment.source_dir, self.environment.build_dir) + else: + instr = src + elem = NinjaBuildElement(self.all_outputs, [str(output)], 'COPY_FILE', [instr]) + elem.add_orderdep(instr) + self.add_build(elem) + def generate_rust_target(self, target: build.BuildTarget) -> None: rustc = target.compilers['rust'] # Rust compiler takes only the main file as input and From 39433762a9af1624be1c581b6f5cdb105b86bcd1 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 1 Oct 2021 09:10:22 -0700 Subject: [PATCH 06/15] build: Add structured sources --- mesonbuild/build.py | 55 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index ecbc1d981..033941c66 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -452,8 +452,63 @@ class ExtractedObjects(HoldableObject): for source in self.get_sources(self.srclist, self.genlist) ] + +@dataclass(eq=False, order=False) +class StructuredSources(HoldableObject): + + """A container for sources in languages that use filesystem hierarchy. + + Languages like Rust and Cython rely on the layout of files in the filesystem + as part of the compiler implementation. This structure allows us to + represent the required filesystem layout. + """ + + sources: T.DefaultDict[str, T.List[T.Union[str, File, CustomTarget, CustomTargetIndex, GeneratedList]]] = field( + default_factory=lambda: defaultdict(list)) + + def __add__(self, other: StructuredSources) -> StructuredSources: + sources = self.sources.copy() + for k, v in other.sources.items(): + sources[k].extend(v) + return StructuredSources(sources) + + def __bool__(self) -> bool: + return bool(self.sources) + + def first_file(self) -> T.Union[str, File, CustomTarget, CustomTargetIndex, GeneratedList]: + """Get the first source in the root + + :return: The first source in the root + """ + return self.sources[''][0] + + def as_list(self) -> T.List[T.Union[str, File, CustomTarget, CustomTargetIndex, GeneratedList]]: + return list(itertools.chain.from_iterable(self.sources.values())) + + def needs_copy(self, target: BuildTarget) -> bool: + """Do we need to create a structure in the build directory. + + This allows us to avoid making copies if the structures exists in the + source dir. Which could happen in situations where a generated source + only exists in some configurations + """ + p = pathlib.Path(target.subdir) + for files in self.sources.values(): + for f in files: + if isinstance(f, str): + if not (target.environment.source_dir / p / f).exists(): + return True + elif isinstance(f, File): + if f.is_built: + return True + else: + return True + return False + + EnvInitValueType = T.Dict[str, T.Union[str, T.List[str]]] + class EnvironmentVariables(HoldableObject): def __init__(self, values: T.Optional[EnvInitValueType] = None, init_method: Literal['set', 'prepend', 'append'] = 'set', separator: str = os.pathsep) -> None: From ba3d9b5d1616af4380bddbcbdacab4a3fcff6150 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 1 Oct 2021 09:14:16 -0700 Subject: [PATCH 07/15] dependencies: Dependency Sources can be StructuredSources --- mesonbuild/dependencies/base.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index c5c62d5ad..7b8515948 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -14,6 +14,8 @@ # This file contains the detection logic for external dependencies. # Custom logic for several other packages are in separate files. + +from __future__ import annotations import copy import os import collections @@ -29,6 +31,7 @@ from ..mesonlib import version_compare_many if T.TYPE_CHECKING: from .._typing import ImmutableListProtocol + from ..build import StructuredSources from ..compilers.compilers import Compiler from ..environment import Environment from ..interpreterbase import FeatureCheckBase @@ -91,7 +94,7 @@ class Dependency(HoldableObject): # Raw -L and -l arguments without manual library searching # If None, self.link_args will be used self.raw_link_args: T.Optional[T.List[str]] = None - self.sources: T.List[T.Union['FileOrString', 'CustomTarget']] = [] + self.sources: T.List[T.Union['FileOrString', 'CustomTarget', 'StructuredSources']] = [] self.include_type = self._process_include_type_kw(kwargs) self.ext_deps: T.List[Dependency] = [] self.d_features: T.DefaultDict[str, T.List[T.Any]] = collections.defaultdict(list) @@ -148,7 +151,7 @@ class Dependency(HoldableObject): def found(self) -> bool: return self.is_found - def get_sources(self) -> T.List[T.Union['FileOrString', 'CustomTarget']]: + def get_sources(self) -> T.List[T.Union['FileOrString', 'CustomTarget', 'StructuredSources']]: """Source files that need to be added to the target. As an example, gtest-all.cc when using GTest.""" return self.sources @@ -228,7 +231,7 @@ class InternalDependency(Dependency): link_args: T.List[str], libraries: T.List[T.Union['BuildTarget', 'CustomTarget']], whole_libraries: T.List[T.Union['BuildTarget', 'CustomTarget']], - sources: T.Sequence[T.Union['FileOrString', 'CustomTarget']], + sources: T.Sequence[T.Union['FileOrString', 'CustomTarget', StructuredSources]], ext_deps: T.List[Dependency], variables: T.Dict[str, T.Any], d_module_versions: T.List[str], d_import_dirs: T.List['IncludeDirs']): super().__init__(DependencyTypeName('internal'), {}) From bcf1583bb065d16031d21df4e9121af0a4d77e7f Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 1 Oct 2021 10:19:58 -0700 Subject: [PATCH 08/15] interpreter: teach source_strings_to_files about StructuredSources --- mesonbuild/interpreter/interpreter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 9f1271900..7014627ee 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -2753,7 +2753,7 @@ Try setting b_lundef to false instead.'''.format(self.coredata.options[OptionKey results.append(s) elif isinstance(s, (build.GeneratedList, build.BuildTarget, build.CustomTargetIndex, build.CustomTarget, - build.ExtractedObjects)): + build.ExtractedObjects, build.StructuredSources)): results.append(s) else: raise InterpreterException(f'Source item is {s!r} instead of ' From fd55ff6a725b920e821dd3c2d5179dd0011c216a Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 1 Oct 2021 09:22:54 -0700 Subject: [PATCH 09/15] interpreter: Add a holder for StructuredSources And teach the interpreter how to use it --- mesonbuild/interpreter/interpreter.py | 1 + mesonbuild/interpreter/interpreterobjects.py | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 7014627ee..650af0314 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -438,6 +438,7 @@ class Interpreter(InterpreterBase, HoldableObject): build.InstallDir: OBJ.InstallDirHolder, build.IncludeDirs: OBJ.IncludeDirsHolder, build.EnvironmentVariables: OBJ.EnvironmentVariablesHolder, + build.StructuredSources: OBJ.StructuredSourcesHolder, compilers.RunResult: compilerOBJ.TryRunResultHolder, dependencies.ExternalLibrary: OBJ.ExternalLibraryHolder, coredata.UserFeatureOption: OBJ.FeatureOptionHolder, diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index d3a4b1256..d3bab08e2 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -978,3 +978,9 @@ class GeneratorHolder(ObjectHolder[build.Generator]): preserve_path_from, extra_args=kwargs['extra_args']) return gl + + +class StructuredSourcesHolder(ObjectHolder[build.StructuredSources]): + + def __init__(self, sources: build.StructuredSources, interp: 'Interpreter'): + super().__init__(sources, interp) From cbc62e892aaea60a3692797c521672b95be9389d Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 1 Oct 2021 09:23:32 -0700 Subject: [PATCH 10/15] interpreter: add an implementation for structured_sources --- data/syntax-highlighting/vim/syntax/meson.vim | 1 + mesonbuild/ast/interpreter.py | 1 + mesonbuild/interpreter/interpreter.py | 26 +++++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/data/syntax-highlighting/vim/syntax/meson.vim b/data/syntax-highlighting/vim/syntax/meson.vim index 15b47cd47..0519bb279 100644 --- a/data/syntax-highlighting/vim/syntax/meson.vim +++ b/data/syntax-highlighting/vim/syntax/meson.vim @@ -116,6 +116,7 @@ syn keyword mesonBuiltin \ shared_library \ shared_module \ static_library + \ structured_sources \ subdir \ subdir_done \ subproject diff --git a/mesonbuild/ast/interpreter.py b/mesonbuild/ast/interpreter.py index f5a1e5e41..e5ff26688 100644 --- a/mesonbuild/ast/interpreter.py +++ b/mesonbuild/ast/interpreter.py @@ -154,6 +154,7 @@ class AstInterpreter(InterpreterBase): 'alias_target': self.func_do_nothing, 'summary': self.func_do_nothing, 'range': self.func_do_nothing, + 'structured_sources': self.func_do_nothing, }) def _unholder_args(self, args: _T, kwargs: _V) -> T.Tuple[_T, _V]: diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 650af0314..6627b5e48 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -385,6 +385,7 @@ class Interpreter(InterpreterBase, HoldableObject): 'run_command': self.func_run_command, 'run_target': self.func_run_target, 'set_variable': self.func_set_variable, + 'structured_sources': self.func_structured_sources, 'subdir': self.func_subdir, 'shared_library': self.func_shared_lib, 'shared_module': self.func_shared_module, @@ -2107,6 +2108,31 @@ external dependencies (including libraries) must go to "dependencies".''') self.build.symlinks.append(l) return l + @FeatureNew('structured_sources', '0.62.0') + @typed_pos_args('structured_sources', object, optargs=[dict]) + @noKwargs + @noArgsFlattening + def func_structured_sources( + self, node: mparser.BaseNode, + args: T.Tuple[object, T.Optional[T.Dict[str, object]]], + kwargs: 'TYPE_kwargs') -> build.StructuredSources: + valid_types = (str, mesonlib.File, build.GeneratedList, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList) + sources: T.Dict[str, T.List[T.Union['mesonlib.FileOrString', 'build.GeneratedTypes']]] = collections.defaultdict(list) + + for arg in mesonlib.listify(args[0]): + if not isinstance(arg, valid_types): + raise InvalidArguments(f'structured_sources: type "{type(arg)}" is not valid') + sources[''].append(arg) + if args[1]: + if '' in args[1]: + raise InvalidArguments('structured_sources: keys to dictionary argument may not be an empty string.') + for k, v in args[1].items(): + for arg in mesonlib.listify(v): + if not isinstance(arg, valid_types): + raise InvalidArguments(f'structured_sources: type "{type(arg)}" is not valid') + sources[k].append(arg) + return build.StructuredSources(sources) + @typed_pos_args('subdir', str) @typed_kwargs( 'subdir', From 7d1431a06039cb29bc2585929a9fbac58bdedb0e Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 1 Oct 2021 09:38:52 -0700 Subject: [PATCH 11/15] build: plumb structured sources into BuildTargets --- mesonbuild/ast/introspection.py | 2 +- mesonbuild/build.py | 73 ++++++++++++++++++++------- mesonbuild/interpreter/interpreter.py | 37 ++++++++++++-- mesonbuild/modules/gnome.py | 16 +++--- mesonbuild/modules/unstable_rust.py | 11 ++-- unittests/allplatformstests.py | 1 + 6 files changed, 105 insertions(+), 35 deletions(-) diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py index 9bd73d729..af295423f 100644 --- a/mesonbuild/ast/introspection.py +++ b/mesonbuild/ast/introspection.py @@ -249,7 +249,7 @@ class IntrospectionInterpreter(AstInterpreter): objects = [] # type: T.List[T.Any] empty_sources = [] # type: T.List[T.Any] # Passing the unresolved sources list causes errors - target = targetclass(name, self.subdir, self.subproject, for_machine, empty_sources, objects, self.environment, kwargs_reduced) + target = targetclass(name, self.subdir, self.subproject, for_machine, empty_sources, [], objects, self.environment, kwargs_reduced) new_target = { 'name': target.get_basename(), diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 033941c66..f2de50c2f 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -739,14 +739,16 @@ class BuildTarget(Target): install_dir: T.List[T.Union[str, bool]] - def __init__(self, name: str, subdir: str, subproject: 'SubProject', for_machine: MachineChoice, - sources: T.List['SourceOutputs'], objects, environment: environment.Environment, kwargs): + def __init__(self, name: str, subdir: str, subproject: SubProject, for_machine: MachineChoice, + sources: T.List['SourceOutputs'], structured_sources: T.Optional[StructuredSources], + objects, environment: environment.Environment, kwargs): super().__init__(name, subdir, subproject, True, for_machine) unity_opt = environment.coredata.get_option(OptionKey('unity')) self.is_unity = unity_opt == 'on' or (unity_opt == 'subprojects' and subproject != '') self.environment = environment self.compilers = OrderedDict() # type: OrderedDict[str, Compiler] self.objects: T.List[T.Union[str, 'File', 'ExtractedObjects']] = [] + self.structured_sources = structured_sources self.external_deps: T.List[dependencies.Dependency] = [] self.include_dirs: T.List['IncludeDirs'] = [] self.link_language = kwargs.get('link_language') @@ -778,13 +780,18 @@ class BuildTarget(Target): self.process_kwargs(kwargs, environment) self.check_unknown_kwargs(kwargs) self.process_compilers() - if not any([self.sources, self.generated, self.objects, self.link_whole]): + if not any([self.sources, self.generated, self.objects, self.link_whole, self.structured_sources]): raise InvalidArguments(f'Build target {name} has no sources.') self.process_compilers_late() self.validate_sources() self.validate_install(environment) self.check_module_linking() + if self.structured_sources and any([self.sources, self.generated]): + raise MesonException('cannot mix structured sources and unstructured sources') + if self.structured_sources and 'rust' not in self.compilers: + raise MesonException('structured sources are only supported in Rust targets') + def __repr__(self): repr_str = "<{0} {1}: {2}>" return repr_str.format(self.__class__.__name__, self.get_id(), self.filename) @@ -888,21 +895,31 @@ class BuildTarget(Target): self.compilers[lang] = compilers[lang] break - def process_compilers(self): + def process_compilers(self) -> None: ''' Populate self.compilers, which is the list of compilers that this target will use for compiling all its sources. We also add compilers that were used by extracted objects to simplify dynamic linker determination. ''' - if not self.sources and not self.generated and not self.objects: + if not any([self.sources, self.generated, self.objects, self.structured_sources]): return # Populate list of compilers compilers = self.environment.coredata.compilers[self.for_machine] # Pre-existing sources - sources = list(self.sources) + sources: T.List['FileOrString'] = list(self.sources) + generated = self.generated.copy() + + if self.structured_sources: + for v in self.structured_sources.sources.values(): + for src in v: + if isinstance(src, (str, File)): + sources.append(src) + else: + generated.append(src) + # All generated sources - for gensrc in self.generated: + for gensrc in generated: for s in gensrc.get_outputs(): # Generated objects can't be compiled, so don't use them for # compiler detection. If our target only has generated objects, @@ -1626,6 +1643,16 @@ You probably should put it in link_with instead.''') elif self.generated: if self.generated[0].get_outputs()[0].endswith('.rs'): return True + elif self.structured_sources: + for v in self.structured_sources.sources.values(): + for s in v: + if isinstance(s, (str, File)): + if s.endswith('.rs'): + return True + else: + for ss in s.get_outputs(): + if ss.endswith('.rs'): + return True return False def get_using_msvc(self) -> bool: @@ -1827,12 +1854,13 @@ class Executable(BuildTarget): known_kwargs = known_exe_kwargs def __init__(self, name: str, subdir: str, subproject: str, for_machine: MachineChoice, - sources: T.List[File], objects, environment: environment.Environment, kwargs): + sources: T.List[File], structured_sources: T.Optional['StructuredSources'], + objects, environment: environment.Environment, kwargs): self.typename = 'executable' key = OptionKey('b_pie') if 'pie' not in kwargs and key in environment.coredata.options: kwargs['pie'] = environment.coredata.options[key].value - super().__init__(name, subdir, subproject, for_machine, sources, objects, environment, kwargs) + super().__init__(name, subdir, subproject, for_machine, sources, structured_sources, objects, environment, kwargs) # Unless overridden, executables have no suffix or prefix. Except on # Windows and with C#/Mono executables where the suffix is 'exe' if not hasattr(self, 'prefix'): @@ -1952,9 +1980,11 @@ class Executable(BuildTarget): class StaticLibrary(BuildTarget): known_kwargs = known_stlib_kwargs - def __init__(self, name, subdir, subproject, for_machine: MachineChoice, sources, objects, environment, kwargs): + def __init__(self, name: str, subdir: str, subproject: str, for_machine: MachineChoice, + sources: T.List[File], structured_sources: T.Optional['StructuredSources'], + objects, environment: environment.Environment, kwargs): self.typename = 'static library' - super().__init__(name, subdir, subproject, for_machine, sources, objects, environment, kwargs) + super().__init__(name, subdir, subproject, for_machine, sources, structured_sources, objects, environment, kwargs) if 'cs' in self.compilers: raise InvalidArguments('Static libraries not supported for C#.') if 'rust' in self.compilers: @@ -2013,7 +2043,9 @@ class StaticLibrary(BuildTarget): class SharedLibrary(BuildTarget): known_kwargs = known_shlib_kwargs - def __init__(self, name, subdir, subproject, for_machine: MachineChoice, sources, objects, environment, kwargs): + def __init__(self, name: str, subdir: str, subproject: str, for_machine: MachineChoice, + sources: T.List[File], structured_sources: T.Optional['StructuredSources'], + objects, environment: environment.Environment, kwargs): self.typename = 'shared library' self.soversion = None self.ltversion = None @@ -2030,7 +2062,7 @@ class SharedLibrary(BuildTarget): self.debug_filename = None # Use by the pkgconfig module self.shared_library_only = False - super().__init__(name, subdir, subproject, for_machine, sources, objects, environment, kwargs) + super().__init__(name, subdir, subproject, for_machine, sources, structured_sources, objects, environment, kwargs) 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': @@ -2338,12 +2370,15 @@ class SharedLibrary(BuildTarget): class SharedModule(SharedLibrary): known_kwargs = known_shmod_kwargs - def __init__(self, name, subdir, subproject, for_machine: MachineChoice, sources, objects, environment, kwargs): + def __init__(self, name: str, subdir: str, subproject: str, for_machine: MachineChoice, + sources: T.List[File], structured_sources: T.Optional['StructuredSources'], + objects, environment: environment.Environment, kwargs): if 'version' in kwargs: raise MesonException('Shared modules must not specify the version kwarg.') if 'soversion' in kwargs: raise MesonException('Shared modules must not specify the soversion kwarg.') - super().__init__(name, subdir, subproject, for_machine, sources, objects, environment, kwargs) + super().__init__(name, subdir, subproject, for_machine, sources, + structured_sources, objects, environment, kwargs) self.typename = 'shared module' # We need to set the soname in cases where build files link the module # to build targets, see: https://github.com/mesonbuild/meson/issues/9492 @@ -2663,15 +2698,19 @@ class AliasTarget(RunTarget): class Jar(BuildTarget): known_kwargs = known_jar_kwargs - def __init__(self, name, subdir, subproject, for_machine: MachineChoice, sources, objects, environment, kwargs): + def __init__(self, name: str, subdir: str, subproject: str, for_machine: MachineChoice, + sources: T.List[File], structured_sources: T.Optional['StructuredSources'], + objects, environment: environment.Environment, kwargs): self.typename = 'jar' - super().__init__(name, subdir, subproject, for_machine, sources, objects, environment, kwargs) + super().__init__(name, subdir, subproject, for_machine, sources, structured_sources, objects, environment, kwargs) for s in self.sources: if not s.endswith('.java'): raise InvalidArguments(f'Jar source {s} is not a java file.') for t in self.link_targets: if not isinstance(t, Jar): raise InvalidArguments(f'Link target {t} is not a jar target.') + if self.structured_sources: + raise InvalidArguments(f'structured sources are not supported in Java targets.') self.filename = self.name + '.jar' self.outputs = [self.filename] self.java_args = kwargs.get('java_args', []) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 6627b5e48..bc011b7c1 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -105,7 +105,7 @@ if T.TYPE_CHECKING: # Input source types passed to the build.Target classes SourceOutputs = T.Union[mesonlib.File, build.GeneratedList, build.BuildTarget, build.CustomTargetIndex, build.CustomTarget, - build.ExtractedObjects, build.GeneratedList] + build.ExtractedObjects, build.GeneratedList, build.StructuredSources] def _project_version_validator(value: T.Union[T.List, str, mesonlib.File, None]) -> T.Optional[str]: @@ -2863,7 +2863,7 @@ Try setting b_lundef to false instead.'''.format(self.coredata.options[OptionKey else: raise InterpreterException(f'Unknown default_library value: {default_library}.') - def build_target(self, node, args, kwargs, targetclass): + def build_target(self, node: mparser.BaseNode, args, kwargs, targetclass): @FeatureNewKwargs('build target', '0.42.0', ['rust_crate_type', 'build_rpath', 'implicit_include_directories']) @FeatureNewKwargs('build target', '0.41.0', ['rust_args']) @FeatureNewKwargs('build target', '0.40.0', ['build_by_default']) @@ -2896,8 +2896,39 @@ Try setting b_lundef to false instead.'''.format(self.coredata.options[OptionKey # passed to library() when default_library == 'static'. kwargs = {k: v for k, v in kwargs.items() if k in targetclass.known_kwargs} + srcs: T.List['SourceInputs'] = [] + struct: T.Optional[build.StructuredSources] = build.StructuredSources() + for s in sources: + if isinstance(s, build.StructuredSources): + struct = struct + s + else: + srcs.append(s) + + if not struct: + struct = None + else: + # Validate that we won't end up with two outputs with the same name. + # i.e, don't allow: + # [structured_sources('foo/bar.rs'), structured_sources('bar/bar.rs')] + for v in struct.sources.values(): + outputs: T.Set[str] = set() + for f in v: + o: T.List[str] + if isinstance(f, str): + o = [os.path.basename(f)] + elif isinstance(f, mesonlib.File): + o = [f.fname] + else: + o = f.get_outputs() + conflicts = outputs.intersection(o) + if conflicts: + raise InvalidArguments.from_node( + f"Conflicting sources in structured sources: {', '.join(sorted(conflicts))}", + node=node) + outputs.update(o) + kwargs['include_directories'] = self.extract_incdirs(kwargs) - target = targetclass(name, self.subdir, self.subproject, for_machine, sources, objs, self.environment, kwargs) + target = targetclass(name, self.subdir, self.subproject, for_machine, srcs, struct, objs, self.environment, kwargs) target.project_version = self.project_version self.add_stdlib_info(target) diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 486b395a4..d1729ef7f 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -607,10 +607,10 @@ class GnomeModule(ExtensionModule): def _get_link_args(self, state: 'ModuleState', lib: T.Union[build.SharedLibrary, build.StaticLibrary], - depends: T.Sequence[T.Union[build.BuildTarget, 'build.GeneratedTypes', 'FileOrString']], + depends: T.Sequence[T.Union[build.BuildTarget, 'build.GeneratedTypes', 'FileOrString', build.StructuredSources]], include_rpath: bool = False, use_gir_args: bool = False - ) -> T.Tuple[T.List[str], T.List[T.Union[build.BuildTarget, 'build.GeneratedTypes', 'FileOrString']]]: + ) -> T.Tuple[T.List[str], T.List[T.Union[build.BuildTarget, 'build.GeneratedTypes', 'FileOrString', build.StructuredSources]]]: link_command: T.List[str] = [] new_depends = list(depends) # Construct link args @@ -638,12 +638,12 @@ class GnomeModule(ExtensionModule): def _get_dependencies_flags( self, deps: T.Sequence[T.Union['Dependency', build.BuildTarget, build.CustomTarget, build.CustomTargetIndex]], state: 'ModuleState', - depends: T.Sequence[T.Union[build.BuildTarget, 'build.GeneratedTypes', 'FileOrString']], + depends: T.Sequence[T.Union[build.BuildTarget, 'build.GeneratedTypes', 'FileOrString', build.StructuredSources]], include_rpath: bool = False, use_gir_args: bool = False, separate_nodedup: bool = False ) -> T.Tuple[OrderedSet[str], OrderedSet[str], OrderedSet[str], T.Optional[T.List[str]], OrderedSet[str], - T.List[T.Union[build.BuildTarget, 'build.GeneratedTypes', 'FileOrString']]]: + T.List[T.Union[build.BuildTarget, 'build.GeneratedTypes', 'FileOrString', build.StructuredSources]]]: cflags: OrderedSet[str] = OrderedSet() internal_ldflags: OrderedSet[str] = OrderedSet() external_ldflags: OrderedSet[str] = OrderedSet() @@ -931,7 +931,7 @@ class GnomeModule(ExtensionModule): girfile: str, scan_command: T.Sequence[T.Union['FileOrString', Executable, ExternalProgram, OverrideProgram]], generated_files: T.Sequence[T.Union[str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList]], - depends: T.Sequence[T.Union['FileOrString', build.BuildTarget, 'build.GeneratedTypes']], + depends: T.Sequence[T.Union['FileOrString', build.BuildTarget, 'build.GeneratedTypes', build.StructuredSources]], kwargs: T.Dict[str, T.Any]) -> GirTarget: install = kwargs['install_gir'] if install is None: @@ -989,8 +989,8 @@ class GnomeModule(ExtensionModule): def _gather_typelib_includes_and_update_depends( state: 'ModuleState', deps: T.Sequence[T.Union[Dependency, build.BuildTarget, build.CustomTarget, build.CustomTargetIndex]], - depends: T.Sequence[T.Union[build.BuildTarget, 'build.GeneratedTypes', 'FileOrString']] - ) -> T.Tuple[T.List[str], T.List[T.Union[build.BuildTarget, 'build.GeneratedTypes', 'FileOrString']]]: + depends: T.Sequence[T.Union[build.BuildTarget, 'build.GeneratedTypes', 'FileOrString', build.StructuredSources]] + ) -> T.Tuple[T.List[str], T.List[T.Union[build.BuildTarget, 'build.GeneratedTypes', 'FileOrString', build.StructuredSources]]]: # Need to recursively add deps on GirTarget sources from our # dependencies and also find the include directories needed for the # typelib generation custom target below. @@ -1092,7 +1092,7 @@ class GnomeModule(ExtensionModule): srcdir = os.path.join(state.environment.get_source_dir(), state.subdir) builddir = os.path.join(state.environment.get_build_dir(), state.subdir) - depends: T.List[T.Union['FileOrString', 'build.GeneratedTypes', build.BuildTarget]] = [] + depends: T.List[T.Union['FileOrString', 'build.GeneratedTypes', build.BuildTarget, build.StructuredSources]] = [] depends.extend(gir_dep.sources) depends.extend(girtargets) diff --git a/mesonbuild/modules/unstable_rust.py b/mesonbuild/modules/unstable_rust.py index 501273f9a..f656cd85d 100644 --- a/mesonbuild/modules/unstable_rust.py +++ b/mesonbuild/modules/unstable_rust.py @@ -17,7 +17,7 @@ import typing as T from . import ExtensionModule, ModuleReturnValue from .. import mlog -from ..build import BothLibraries, BuildTarget, CustomTargetIndex, Executable, ExtractedObjects, GeneratedList, IncludeDirs, CustomTarget +from ..build import BothLibraries, BuildTarget, CustomTargetIndex, Executable, ExtractedObjects, GeneratedList, IncludeDirs, CustomTarget, StructuredSources from ..dependencies import Dependency, ExternalLibrary from ..interpreter.interpreter import TEST_KWARGS from ..interpreterbase import ContainerTypeInfo, InterpreterException, KwargInfo, FeatureNew, typed_kwargs, typed_pos_args, noPosargs @@ -149,10 +149,9 @@ class RustModule(ExtensionModule): new_target_kwargs['dependencies'] = new_target_kwargs.get('dependencies', []) + dependencies new_target = Executable( - name, base_target.subdir, state.subproject, - base_target.for_machine, base_target.sources, - base_target.objects, base_target.environment, - new_target_kwargs + name, base_target.subdir, state.subproject, base_target.for_machine, + base_target.sources, base_target.structured_sources, + base_target.objects, base_target.environment, new_target_kwargs ) test = self.interpreter.make_test( @@ -203,7 +202,7 @@ class RustModule(ExtensionModule): name: str if isinstance(header, File): name = header.fname - elif isinstance(header, (BuildTarget, BothLibraries, ExtractedObjects)): + elif isinstance(header, (BuildTarget, BothLibraries, ExtractedObjects, StructuredSources)): raise InterpreterException('bindgen source file must be a C header, not an object or build target') else: name = header.get_outputs()[0] diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 665e0c62d..b424b15c4 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -3988,6 +3988,7 @@ class AllPlatformTests(BasePlatformTests): def output_name(name, type_): return type_(name=name, subdir=None, subproject=None, for_machine=MachineChoice.HOST, sources=[], + structured_sources=None, objects=[], environment=env, kwargs={}).filename shared_lib_name = lambda name: output_name(name, SharedLibrary) From 0d0a4fa0fe02ca8297743a5a8e64ee72060cc990 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 1 Oct 2021 09:48:56 -0700 Subject: [PATCH 12/15] backends/ninja: Add support for structured sources with rust --- mesonbuild/backend/ninjabackend.py | 43 ++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index a8616b822..fdd5cd340 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -54,6 +54,7 @@ if T.TYPE_CHECKING: from .._typing import ImmutableListProtocol from ..linkers import DynamicLinker, StaticLinker from ..compilers.cs import CsCompiler + from ..interpreter.interpreter import SourceOutputs FORTRAN_INCLUDE_PAT = r"^\s*#?include\s*['\"](\w+\.\w+)['\"]" @@ -1662,6 +1663,29 @@ class NinjaBackend(backends.Backend): elem.add_orderdep(instr) self.add_build(elem) + def __generate_compile_structure(self, target: build.BuildTarget) -> T.Tuple[T.List[str], T.Optional[str]]: + first_file: T.Optional[str] = None + orderdeps: T.List[str] = [] + root = Path(self.get_target_private_dir(target)) / 'structured' + for path, files in target.structured_sources.sources.items(): + for file in files: + if isinstance(file, (str, File)): + if isinstance(file, str): + file = File.from_absolute_file(file) + out = root / path / Path(file.fname).name + orderdeps.append(str(out)) + self._generate_copy_target(file, out) + if first_file is None: + first_file = str(out) + else: + for f in file.get_outputs(): + out = root / path / f + orderdeps.append(str(out)) + self._generate_copy_target(str(Path(file.subdir) / f), out) + if first_file is None: + first_file = str(out) + return orderdeps, first_file + def generate_rust_target(self, target: build.BuildTarget) -> None: rustc = target.compilers['rust'] # Rust compiler takes only the main file as input and @@ -1682,6 +1706,25 @@ class NinjaBackend(backends.Backend): orderdeps: T.List[str] = [] main_rust_file = None + if target.structured_sources: + if target.structured_sources.needs_copy(target): + _ods, main_rust_file = self.__generate_compile_structure(target) + orderdeps.extend(_ods) + else: + g = target.structured_sources.first_file() + if isinstance(g, str): + g = File.from_source_file(self.environment.source_dir, target.subdir, g) + + if isinstance(g, File): + main_rust_file = g.rel_to_builddir(self.build_to_src) + elif isinstance(g, GeneratedList): + main_rust_file = os.path.join(self.get_target_private_dir(target), i) + else: + main_rust_file = os.path.join(g.get_subdir(), i) + orderdeps.extend([os.path.join(self.build_to_src, target.subdir, s) + for s in target.structured_sources.as_list()]) + + for i in target.get_sources(): if not rustc.can_compile(i): raise InvalidArguments(f'Rust target {target.get_basename()} contains a non-rust source file.') From 038d0723647f58d62754afb8f4dca7dc716a5ba8 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 3 May 2021 12:57:04 -0700 Subject: [PATCH 13/15] tests/rust: Add a test for structured inputs --- .../main.rs | 0 .../meson.build | 13 +++++++ .../test.json | 7 ++++ .../122 structured_sources conflicts/main.rs | 0 .../meson.build | 17 ++++++++ .../test.json | 7 ++++ test cases/rust/18 structured sources/gen.py | 20 ++++++++++ .../rust/18 structured sources/meson.build | 39 +++++++++++++++++++ .../18 structured sources/no_copy_test.py | 18 +++++++++ .../rust/18 structured sources/src/foo.rs.in | 4 ++ .../rust/18 structured sources/src/main.rs | 5 +++ .../18 structured sources/src2/foo/mod.rs | 4 ++ .../18 structured sources/src2/main-unique.rs | 5 +++ 13 files changed, 139 insertions(+) create mode 100644 test cases/failing/121 structured source empty string/main.rs create mode 100644 test cases/failing/121 structured source empty string/meson.build create mode 100644 test cases/failing/121 structured source empty string/test.json create mode 100644 test cases/failing/122 structured_sources conflicts/main.rs create mode 100644 test cases/failing/122 structured_sources conflicts/meson.build create mode 100644 test cases/failing/122 structured_sources conflicts/test.json create mode 100755 test cases/rust/18 structured sources/gen.py create mode 100644 test cases/rust/18 structured sources/meson.build create mode 100755 test cases/rust/18 structured sources/no_copy_test.py create mode 100644 test cases/rust/18 structured sources/src/foo.rs.in create mode 100644 test cases/rust/18 structured sources/src/main.rs create mode 100644 test cases/rust/18 structured sources/src2/foo/mod.rs create mode 100644 test cases/rust/18 structured sources/src2/main-unique.rs diff --git a/test cases/failing/121 structured source empty string/main.rs b/test cases/failing/121 structured source empty string/main.rs new file mode 100644 index 000000000..e69de29bb diff --git a/test cases/failing/121 structured source empty string/meson.build b/test cases/failing/121 structured source empty string/meson.build new file mode 100644 index 000000000..071599147 --- /dev/null +++ b/test cases/failing/121 structured source empty string/meson.build @@ -0,0 +1,13 @@ +project('structured_source with empty string key') + +if not add_languages(['rust'], required : false, native : false) + error('MESON_SKIP_TEST: Rust is required but not found.') +endif + +executable( + 'main', + structured_sources( + 'main.rs', + {'' : 'main.rs'}, + ) +) diff --git a/test cases/failing/121 structured source empty string/test.json b/test cases/failing/121 structured source empty string/test.json new file mode 100644 index 000000000..feb2af9e7 --- /dev/null +++ b/test cases/failing/121 structured source empty string/test.json @@ -0,0 +1,7 @@ +{ + "stdout": [ + { + "line": "test cases/failing/121 structured source empty string/meson.build:7:0: ERROR: structured_sources: keys to dictionary argument may not be an empty string." + } + ] +} diff --git a/test cases/failing/122 structured_sources conflicts/main.rs b/test cases/failing/122 structured_sources conflicts/main.rs new file mode 100644 index 000000000..e69de29bb diff --git a/test cases/failing/122 structured_sources conflicts/meson.build b/test cases/failing/122 structured_sources conflicts/meson.build new file mode 100644 index 000000000..561ad86bc --- /dev/null +++ b/test cases/failing/122 structured_sources conflicts/meson.build @@ -0,0 +1,17 @@ +project('structured_source with empty string key') + +if not add_languages(['rust'], required : false, native : false) + error('MESON_SKIP_TEST: Rust is required but not found.') +endif + +executable( + 'main', + [ + structured_sources( + 'main.rs', + ), + structured_sources( + 'main.rs', + ), + ], +) diff --git a/test cases/failing/122 structured_sources conflicts/test.json b/test cases/failing/122 structured_sources conflicts/test.json new file mode 100644 index 000000000..4833f86b1 --- /dev/null +++ b/test cases/failing/122 structured_sources conflicts/test.json @@ -0,0 +1,7 @@ +{ + "stdout": [ + { + "line": "test cases/failing/122 structured_sources conflicts/meson.build:7:0: ERROR: Conflicting sources in structured sources: main.rs" + } + ] +} diff --git a/test cases/rust/18 structured sources/gen.py b/test cases/rust/18 structured sources/gen.py new file mode 100755 index 000000000..16e5c04e1 --- /dev/null +++ b/test cases/rust/18 structured sources/gen.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 + +import argparse +import textwrap + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument('output') + args = parser.parse_args() + + with open(args.output, 'w') as f: + f.write(textwrap.dedent('''\ + pub fn bar() -> () { + println!("Hello, World!"); + }''')) + + +if __name__ == "__main__": + main() diff --git a/test cases/rust/18 structured sources/meson.build b/test cases/rust/18 structured sources/meson.build new file mode 100644 index 000000000..8fa044364 --- /dev/null +++ b/test cases/rust/18 structured sources/meson.build @@ -0,0 +1,39 @@ +project('structured input', 'rust') + +foo_mod_rs = configure_file( + input : 'src/foo.rs.in', + output : 'mod.rs', + configuration : {'message' : 'Hello, World!'}, +) + +conf_file = executable( + 'main_conf_file', + structured_sources( + 'src/main.rs', + {'foo' : [foo_mod_rs]}, + ), +) + +ct = custom_target( + 'foo.rs', + output : 'foo.rs', + command : ['gen.py', '@OUTPUT@'], +) + +target = executable( + 'main_custom_target', + structured_sources( + ['src/main.rs', ct], + ), +) + +# Should not be coppied +executable( + 'no_copy_target', + structured_sources( + ['src2/main-unique.rs'], + {'foo': 'src2/foo/mod.rs'}, + ), +) + +test('no-copy', find_program('no_copy_test.py'), args : meson.current_build_dir()) diff --git a/test cases/rust/18 structured sources/no_copy_test.py b/test cases/rust/18 structured sources/no_copy_test.py new file mode 100755 index 000000000..91506b20f --- /dev/null +++ b/test cases/rust/18 structured sources/no_copy_test.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +import argparse +import os + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument('builddir') + args = parser.parse_args() + + for _, _, files in os.walk(args.builddir): + if 'main-unique.rs' in files: + exit(1) + + +if __name__ == "__main__": + main() diff --git a/test cases/rust/18 structured sources/src/foo.rs.in b/test cases/rust/18 structured sources/src/foo.rs.in new file mode 100644 index 000000000..4f3fc4263 --- /dev/null +++ b/test cases/rust/18 structured sources/src/foo.rs.in @@ -0,0 +1,4 @@ + +pub fn bar() -> () { + println!("@message@"); +} diff --git a/test cases/rust/18 structured sources/src/main.rs b/test cases/rust/18 structured sources/src/main.rs new file mode 100644 index 000000000..3ffeee2be --- /dev/null +++ b/test cases/rust/18 structured sources/src/main.rs @@ -0,0 +1,5 @@ +mod foo; + +fn main() { + foo::bar(); +} diff --git a/test cases/rust/18 structured sources/src2/foo/mod.rs b/test cases/rust/18 structured sources/src2/foo/mod.rs new file mode 100644 index 000000000..9463d95b8 --- /dev/null +++ b/test cases/rust/18 structured sources/src2/foo/mod.rs @@ -0,0 +1,4 @@ + +pub fn bar() -> () { + println!("Hello, World!"); +} diff --git a/test cases/rust/18 structured sources/src2/main-unique.rs b/test cases/rust/18 structured sources/src2/main-unique.rs new file mode 100644 index 000000000..3ffeee2be --- /dev/null +++ b/test cases/rust/18 structured sources/src2/main-unique.rs @@ -0,0 +1,5 @@ +mod foo; + +fn main() { + foo::bar(); +} From 9cf7a125613f8cc929828780b8c7aefd0d3ee4ac Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 4 Mar 2022 15:26:50 -0800 Subject: [PATCH 14/15] docs: Add docs for structured_sources --- docs/markdown/snippets/structured_sources.md | 26 ++++++++++++++++++++ docs/yaml/functions/_build_target_base.yaml | 2 +- docs/yaml/functions/structured_sources.yaml | 21 ++++++++++++++++ docs/yaml/objects/structured_src.yaml | 3 +++ 4 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 docs/markdown/snippets/structured_sources.md create mode 100644 docs/yaml/functions/structured_sources.yaml create mode 100644 docs/yaml/objects/structured_src.yaml diff --git a/docs/markdown/snippets/structured_sources.md b/docs/markdown/snippets/structured_sources.md new file mode 100644 index 000000000..19fdc86f2 --- /dev/null +++ b/docs/markdown/snippets/structured_sources.md @@ -0,0 +1,26 @@ +## structured_sources() + +A new function, `structured_sources()` has been added. This function allows +languages like Rust which depend on the filesystem layout at compile time to mix +generated and static sources. + +```meson +executable( + 'main', + structured_sources( + 'main.rs, + {'mod' : generated_mod_rs}, + ) +) +``` + +Meson will then at build time copy the files into the build directory (if +necessary), so that the desired file structure is laid out, and compile that. In +this case: + +``` +root/ + main.rs + mod/ + mod.rs +``` diff --git a/docs/yaml/functions/_build_target_base.yaml b/docs/yaml/functions/_build_target_base.yaml index 68df5d85f..4db37f4d8 100644 --- a/docs/yaml/functions/_build_target_base.yaml +++ b/docs/yaml/functions/_build_target_base.yaml @@ -49,7 +49,7 @@ kwargs: eg: `cpp_args` for C++ sources: - type: str | file | custom_tgt | custom_idx | generated_list + type: str | file | custom_tgt | custom_idx | generated_list | structured_src description: Additional source files. Same as the source varargs. build_by_default: diff --git a/docs/yaml/functions/structured_sources.yaml b/docs/yaml/functions/structured_sources.yaml new file mode 100644 index 000000000..a5f0a83f3 --- /dev/null +++ b/docs/yaml/functions/structured_sources.yaml @@ -0,0 +1,21 @@ +name: structured_sources +returns: structured_src +since: 0.62.0 +description: | + Create a StructuredSource object, which is opaque and may be passed as a source + to any build_target (including static_library, shared_library, executable, + etc.). This is useful for languages like Rust, which use the filesystem layout + to determine import names. This is only allowed in Rust targets, and cannot be + mixed with non structured inputs. + +posargs: + root: + type: list[str | file | custom_tgt | custom_idx | generated_list] + description: Sources to put at the root of the generated structure + +optargs: + additional: + type: dict[str | file | custom_tgt | custom_idx | generated_list] + description: | + Additional sources, where the key is the directory under the root to place + the values diff --git a/docs/yaml/objects/structured_src.yaml b/docs/yaml/objects/structured_src.yaml new file mode 100644 index 000000000..839575d7c --- /dev/null +++ b/docs/yaml/objects/structured_src.yaml @@ -0,0 +1,3 @@ +name: structured_src +long_name: Structured Source +description: Opaque object returned by [[structured_sources]]. From ff4c283b3ac29f9f0cf067ceac2b1348964baeee Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 17 May 2021 16:51:20 -0700 Subject: [PATCH 15/15] docs: Add a document on using Rust in Meson This is hardly complete, but it's a start. --- docs/markdown/Rust.md | 56 +++++++++++++++++++++++++++++++++++++++++++ docs/sitemap.txt | 1 + 2 files changed, 57 insertions(+) create mode 100644 docs/markdown/Rust.md diff --git a/docs/markdown/Rust.md b/docs/markdown/Rust.md new file mode 100644 index 000000000..b2376c3fa --- /dev/null +++ b/docs/markdown/Rust.md @@ -0,0 +1,56 @@ +--- +title: Rust +short-description: Working with Rust in Meson +--- + +# Using Rust with Meson + +## Mixing Rust and non-Rust sources + +Meson currently does not support creating a single target with Rust and non Rust +sources mixed together, therefore one must compile multiple libraries and link +them. + +```meson +rust_lib = static_library( + 'rust_lib', + sources : 'lib.rs', + ... +) + +c_lib = static_library( + 'c_lib', + sources : 'lib.c', + link_with : rust_lib, +) +``` +This is an implementation detail of Meson, and is subject to change in the future. + +## Mixing Generated and Static sources + +*Note* This feature was added in 0.62 + +You can use a [[structured_source]] for this. Structured sources are a dictionary +mapping a string of the directory, to a source or list of sources. +When using a structured source all inputs *must* be listed, as Meson may copy +the sources from the source tree to the build tree. + +Structured inputs are generally not needed when not using generated sources. + +As an implementation detail, Meson will attempt to determine if it needs to copy +files at configure time and will skip copying if it can. Copying is done at +build time (when necessary), to avoid reconfiguring when sources change. + +```meson +executable( + 'rust_exe', + structured_sources( + 'main.rs', + { + 'foo' : ['bar.rs', 'foo/lib.rs', generated_rs], + 'foo/bar' : [...], + 'other' : [...], + } + ) +) +``` diff --git a/docs/sitemap.txt b/docs/sitemap.txt index 11b64e0c7..e562ac79d 100644 --- a/docs/sitemap.txt +++ b/docs/sitemap.txt @@ -63,6 +63,7 @@ index.md Vala.md D.md Cython.md + Rust.md IDE-integration.md Custom-build-targets.md Build-system-converters.md