aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild/build.py
diff options
context:
space:
mode:
Diffstat (limited to 'mesonbuild/build.py')
-rw-r--r--mesonbuild/build.py132
1 files changed, 113 insertions, 19 deletions
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 5c1bd32..fbc1618 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_)
@@ -452,10 +452,65 @@ 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[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()
@@ -684,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')
@@ -723,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)
@@ -833,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,
@@ -1571,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:
@@ -1772,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'):
@@ -1897,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:
@@ -1958,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
@@ -1975,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':
@@ -2285,12 +2372,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
@@ -2610,15 +2700,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', [])