summaryrefslogtreecommitdiff
path: root/src/buildstream/_project.py
diff options
context:
space:
mode:
authorTristan van Berkom <tristan@codethink.co.uk>2020-11-20 17:24:09 +0900
committerTristan van Berkom <tristan@codethink.co.uk>2020-12-07 17:53:03 +0900
commit5ace573dc045ca5cab38d4baeff3abf874b152ee (patch)
treeb9c2e1d27fa5afd5e0466b218ecd64f6624816f7 /src/buildstream/_project.py
parent5352d7bacb4f3f59c315a35e990d57fd3d52466d (diff)
downloadbuildstream-5ace573dc045ca5cab38d4baeff3abf874b152ee.tar.gz
_project.py, _artifactproject.py: Adding ArtifactProject
The Project's class initializer is now refactored such that loading a project.conf is made optional. The initializer is now well sorted with public members showing up before private members, followed by the initialization body. pep484 type hints are now employed aggressively for all project instance members. The added ArtifactProject is added to serve as the data model counterpart of the ArtifactElement, ensuring that we never mistakenly use locally loaded project data in ArtifactElement instances. Consequently, the Project.sandbox and Project.splits variables are properly made public by this commit, as these are simply loaded from the project config and accessed elsewhere by Element; Element is updated to access these public members by their new public names.
Diffstat (limited to 'src/buildstream/_project.py')
-rw-r--r--src/buildstream/_project.py154
1 files changed, 90 insertions, 64 deletions
diff --git a/src/buildstream/_project.py b/src/buildstream/_project.py
index fd4a44732..2534e0209 100644
--- a/src/buildstream/_project.py
+++ b/src/buildstream/_project.py
@@ -18,6 +18,8 @@
# Tristan Van Berkom <tristan.vanberkom@codethink.co.uk>
# Tiago Gomes <tiago.gomes@codethink.co.uk>
+from typing import TYPE_CHECKING, Optional, Dict, Union, List
+
import os
import sys
from collections import OrderedDict
@@ -44,6 +46,10 @@ from ._message import Message, MessageType
from ._includes import Includes
from ._workspaces import WORKSPACE_PROJECT_FILE
+if TYPE_CHECKING:
+ from .node import ProvenanceInformation, MappingNode
+ from ._context import Context
+ from ._remote import RemoteSpec
# Project Configuration file
_PROJECT_CONF_FILE = "project.conf"
@@ -86,95 +92,115 @@ class ProjectConfig:
#
# The Project Configuration
#
+# Args:
+# directory: The project directory, or None for dummy ArtifactProjects
+# context: The invocation context
+# junction: The junction Element causing this project to be loaded
+# cli_options: The project options specified on the command line
+# default_mirror: The default mirror specified on the command line
+# parent_loader: The parent loader
+# provenance_node: The YAML provenance causing this project to be loaded
+# search_for_project: Whether to search for a project directory, e.g. from workspace metadata or parent directories
+# load_project: Whether to attempt to load a project.conf
+#
class Project:
def __init__(
self,
- directory,
- context,
+ directory: Optional[str],
+ context: "Context",
*,
- junction=None,
- cli_options=None,
- default_mirror=None,
- parent_loader=None,
- provenance_node=None,
- search_for_project=True,
+ junction: Optional[object] = None,
+ cli_options: Optional[Dict[str, str]] = None,
+ default_mirror: Optional[str] = None,
+ parent_loader: Optional[Loader] = None,
+ provenance_node: Optional["ProvenanceInformation"] = None,
+ search_for_project: bool = True,
+ load_project: bool = True,
):
+ #
+ # Public members
+ #
+ self.name: Optional[str] = None # The project name
+ self.directory: Optional[str] = directory # The project directory
+ self.element_path: Optional[str] = None # The project relative element path
- # The project name
- self.name = None
-
- self._context = context # The invocation Context, a private member
-
- # Create the LoadContext here if we are the toplevel project.
- if parent_loader:
- self.load_context = parent_loader.load_context
- else:
- self.load_context = LoadContext(self._context)
-
- if search_for_project:
- self.directory, self._invoked_from_workspace_element = self._find_project_dir(directory)
- else:
- self.directory = directory
- self._invoked_from_workspace_element = None
-
- self._absolute_directory_path = Path(self.directory).resolve()
-
- # Absolute path to where elements are loaded from within the project
- self.element_path = None
+ self.load_context: LoadContext # The LoadContext
+ self.loader: Optional[Loader] = None # The loader associated to this project
+ self.junction: Optional[object] = junction # The junction Element object, if this is a subproject
- # ProjectRefs for the main refs and also for junctions
- self.refs = ProjectRefs(self.directory, "project.refs")
- self.junction_refs = ProjectRefs(self.directory, "junction.refs")
+ self.ref_storage: Optional[ProjectRefStorage] = None # Where to store source refs
+ self.refs: Optional[ProjectRefs] = None
+ self.junction_refs: Optional[ProjectRefs] = None
- self.config = ProjectConfig()
- self.first_pass_config = ProjectConfig()
+ self.config: ProjectConfig = ProjectConfig()
+ self.first_pass_config: ProjectConfig = ProjectConfig()
- self.junction = junction # The junction Element object, if this is a subproject
+ self.base_environment: Union["MappingNode", Dict[str, str]] = {} # The base set of environment variables
+ self.base_env_nocache: List[str] = [] # The base nocache mask (list) for the environment
- self.ref_storage = None # ProjectRefStorage setting
- self.base_environment = {} # The base set of environment variables
- self.base_env_nocache = None # The base nocache mask (list) for the environment
+ # Remote specs for communicating with remote services
+ self.artifact_cache_specs: List["RemoteSpec"] = [] # Artifact caches
+ self.source_cache_specs: List["RemoteSpec"] = [] # Source caches
+ self.remote_execution_specs: List["RemoteSpec"] = [] # Remote execution services
- self.artifact_cache_specs = None
- self.source_cache_specs = None
- self.remote_execution_specs = None
+ self.element_factory: Optional[ElementFactory] = None # ElementFactory for loading elements
+ self.source_factory: Optional[SourceFactory] = None # SourceFactory for loading sources
- self.element_factory = None # ElementFactory for loading elements
- self.source_factory = None # SourceFactory for loading sources
+ self.sandbox: Optional["MappingNode"] = None
+ self.splits: Optional["MappingNode"] = None
#
- # Private Members
+ # Private members
#
- self._default_targets = None # Default target elements
- self._default_mirror = default_mirror # The name of the preferred mirror.
+ self._context: "Context" = context # The invocation Context
+ self._invoked_from_workspace_element: Optional[str] = None
+ self._absolute_directory_path: Optional[Path] = None
- self._cli_options = cli_options
+ self._default_targets: Optional[List[str]] = None # Default target elements
+ self._default_mirror: Optional[str] = default_mirror # The name of the preferred mirror.
+ self._cli_options: Optional[Dict[str, str]] = cli_options
- self._fatal_warnings = [] # A list of warnings which should trigger an error
-
- self._shell_command = [] # The default interactive shell command
- self._shell_environment = {} # Statically set environment vars
- self._shell_host_files = [] # A list of HostMount objects
- self._sandbox = None
- self._splits = None
+ self._fatal_warnings: List[str] = [] # A list of warnings which should trigger an error
+ self._shell_command: List[str] = [] # The default interactive shell command
+ self._shell_environment: Dict[str, str] = {} # Statically set environment vars
+ self._shell_host_files: List[str] = [] # A list of HostMount objects
# This is a lookup table of lists indexed by project,
# the child dictionaries are lists of ScalarNodes indicating
# junction names
- self._junction_duplicates = {}
+ self._junction_duplicates: Dict[str, List[str]] = {}
# A list of project relative junctions to consider as 'internal',
# stored as ScalarNodes.
- self._junction_internal = []
+ self._junction_internal: List[str] = []
- self._context.add_project(self)
+ self._partially_loaded: bool = False
+ self._fully_loaded: bool = False
+ self._project_includes: Optional[Includes] = None
- self._partially_loaded = False
- self._fully_loaded = False
- self._project_includes = None
+ #
+ # Initialization body
+ #
+ if parent_loader:
+ self.load_context = parent_loader.load_context
+ else:
+ self.load_context = LoadContext(self._context)
- with PROFILER.profile(Topics.LOAD_PROJECT, self.directory.replace(os.sep, "-")):
- self._load(parent_loader=parent_loader, provenance_node=provenance_node)
+ if search_for_project:
+ self.directory, self._invoked_from_workspace_element = self._find_project_dir(directory)
+
+ if self.directory:
+ self._absolute_directory_path = Path(self.directory).resolve()
+ self.refs = ProjectRefs(self.directory, "project.refs")
+ self.junction_refs = ProjectRefs(self.directory, "junction.refs")
+
+ self._context.add_project(self)
+
+ if self.directory and load_project:
+ with PROFILER.profile(Topics.LOAD_PROJECT, self.directory.replace(os.sep, "-")):
+ self._load(parent_loader=parent_loader, provenance_node=provenance_node)
+ else:
+ self._fully_loaded = True
self._partially_loaded = True
@@ -873,10 +899,10 @@ class Project:
self.base_env_nocache = config.get_str_list("environment-nocache")
# Load sandbox configuration
- self._sandbox = config.get_mapping("sandbox")
+ self.sandbox = config.get_mapping("sandbox")
# Load project split rules
- self._splits = config.get_mapping("split-rules")
+ self.splits = config.get_mapping("split-rules")
# Support backwards compatibility for fail-on-overlap
fail_on_overlap = config.get_scalar("fail-on-overlap", None)