diff options
Diffstat (limited to 'src/buildstream/scriptelement.py')
-rw-r--r-- | src/buildstream/scriptelement.py | 146 |
1 files changed, 52 insertions, 94 deletions
diff --git a/src/buildstream/scriptelement.py b/src/buildstream/scriptelement.py index 5ecae998c..3e84c598b 100644 --- a/src/buildstream/scriptelement.py +++ b/src/buildstream/scriptelement.py @@ -36,7 +36,7 @@ import os from collections import OrderedDict from typing import List, Optional, TYPE_CHECKING -from .element import Element, ElementError +from .element import Element from .sandbox import SandboxFlags if TYPE_CHECKING: @@ -48,7 +48,7 @@ class ScriptElement(Element): __cwd = "/" __root_read_only = False __commands = None # type: OrderedDict[str, List[str]] - __layout = [] # type: List[Dict[str, Optional[str]]] + __layout = {} # type: Dict[str, List[Element]] # The compose element's output is its dependencies, so # we must rebuild if the dependencies change even when @@ -112,43 +112,44 @@ class ScriptElement(Element): """ self.__root_read_only = root_read_only - def layout_add(self, element: Optional[str], destination: str) -> None: - """Adds an element-destination pair to the layout. + def layout_add(self, element: Element, location: str) -> None: + """Adds an element to the layout. Layout is a way of defining how dependencies should be added to the staging area for running commands. Args: - element: The name of the element to stage, or None. This may be any - element found in the dependencies, whether it is a direct - or indirect dependency. - destination: The path inside the staging area for where to - stage this element. If it is not "/", then integration - commands will not be run. + element (Element): The element to stage. + location (str): The path inside the staging area for where to + stage this element. If it is not "/", then integration + commands will not be run. If this function is never called, then the default behavior is to just stage the build dependencies of the element in question at the - sandbox root. Otherwise, the runtime dependencies of each specified - element will be staged in their specified destination directories. + sandbox root. Otherwise, the specified elements including their + runtime dependencies will be staged in their respective locations. .. note:: - The order of directories in the layout is significant as they - will be mounted into the sandbox. It is an error to specify a parent - directory which will shadow a directory already present in the layout. - - .. note:: - - In the case that no element is specified, a read-write directory will - be made available at the specified location. + The order of directories in the layout is not significant. The + elements for each respective directory in the layout will be staged + in the predetermined deterministic staging order. """ # - # Even if this is an empty list by default, make sure that its + # Even if this is an empty dict by default, make sure that it is # instance data instead of appending stuff directly onto class data. # if not self.__layout: - self.__layout = [] - self.__layout.append({"element": element, "destination": destination}) + self.__layout = {} + + # Ensure the element list + try: + element_list = self.__layout[location] + except KeyError: + element_list = [] + self.__layout[location] = element_list + + element_list.append(element) def add_commands(self, group_name: str, command_list: List[str]) -> None: """Adds a list of commands under the group-name. @@ -174,17 +175,19 @@ class ScriptElement(Element): ############################################################# # Abstract Method Implementations # ############################################################# - def preflight(self): - # The layout, if set, must make sense. - self.__validate_layout() + pass def get_unique_key(self): + layout_key = { + location: [(element.project_name, element.name) for element in element_list] + for location, element_list in self.__layout.items() + } return { "commands": self.__commands, "cwd": self.__cwd, "install-root": self.__install_root, - "layout": self.__layout, + "layout": layout_key, "root-read-only": self.__root_read_only, } @@ -196,67 +199,42 @@ class ScriptElement(Element): # Setup environment sandbox.set_environment(self.get_environment()) - # Tell the sandbox to mount the install root - directories = {self.__install_root: False} - - # set the output directory - sandbox.set_output_directory(self.__install_root) + # Mark the install root + sandbox.mark_directory(self.__install_root, artifact=False) # Mark the artifact directories in the layout - for item in self.__layout: - destination = item["destination"] - was_artifact = directories.get(destination, False) - directories[destination] = item["element"] or was_artifact - - for directory, artifact in directories.items(): - # Root does not need to be marked as it is always mounted - # with artifact (unless explicitly marked non-artifact) - if directory != "/": - sandbox.mark_directory(directory, artifact=artifact) + for location in self.__layout: + sandbox.mark_directory(location, artifact=True) def stage(self, sandbox): - # Stage the elements, and run integration commands where appropriate. + # If self.layout_add() was never called, do the default staging of + # everything in "/" and run the integration commands if not self.__layout: - # if no layout set, stage all dependencies into the sandbox root with self.timed_activity("Staging dependencies", silent_nested=True): self.stage_dependency_artifacts(sandbox) - # Run any integration commands provided by the dependencies - # once they are all staged and ready with sandbox.batch(SandboxFlags.NONE, label="Integrating sandbox"): for dep in self.dependencies(): dep.integrate(sandbox) - else: - # If layout, follow its rules. - for item in self.__layout: - - # Skip layout members which dont stage an element - if not item["element"]: - continue - - element = self.search(item["element"]) - with self.timed_activity( - "Staging {} at {}".format(element.name, item["destination"]), silent_nested=True - ): - element.stage_dependency_artifacts(sandbox, [element], path=item["destination"]) - - with sandbox.batch(SandboxFlags.NONE): - for item in self.__layout: - - # Skip layout members which dont stage an element - if not item["element"]: - continue - - element = self.search(item["element"]) - - # Integration commands can only be run for elements staged to / - if item["destination"] == "/": - with self.timed_activity("Integrating {}".format(element.name), silent_nested=True): - for dep in element.dependencies(): - dep.integrate(sandbox) + else: + # First stage it all + # + for location, element_list in self.__layout.items(): + self.stage_dependency_artifacts(sandbox, element_list, path=location) + + # Now integrate any elements staged in the root + # + element_list = self.__layout.get("/", None) + if element_list: + with sandbox.batch(SandboxFlags.NONE), self.timed_activity("Integrating sandbox", silent_nested=True): + for dep in self.dependencies(element_list): + dep.integrate(sandbox) + + # Ensure the install root exists + # install_root_path_components = self.__install_root.lstrip(os.sep).split(os.sep) sandbox.get_virtual_directory().descend(*install_root_path_components, create=True) @@ -277,26 +255,6 @@ class ScriptElement(Element): # Return where the result can be collected from return self.__install_root - ############################################################# - # Private Local Methods # - ############################################################# - - def __validate_layout(self): - if self.__layout: - # Cannot proceeed if layout is used, but none are for "/" - root_defined = any([(entry["destination"] == "/") for entry in self.__layout]) - if not root_defined: - raise ElementError("{}: Using layout, but none are staged as '/'".format(self)) - - # Cannot proceed if layout specifies an element that isn't part - # of the dependencies. - for item in self.__layout: - if item["element"]: - if not self.search(item["element"]): - raise ElementError( - "{}: '{}' in layout not found in dependencies".format(self, item["element"]) - ) - def setup(): return ScriptElement |