build: Add structured sources
@@ -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:
