diff options
author | Valentin David <valentin.david@codethink.co.uk> | 2018-06-11 15:57:28 +0200 |
---|---|---|
committer | Tiago Gomes <tiago.gomes@codethink.co.uk> | 2018-08-03 12:54:33 +0100 |
commit | 63a37dff1f2b21bece9d9d51aa956bd7b4448170 (patch) | |
tree | 673ad0b85abbed9deeed8afd69a7d02b092e8af1 | |
parent | 4bcd3cc5ffed08ca70cdd76e2256d2209c342500 (diff) | |
download | buildstream-63a37dff1f2b21bece9d9d51aa956bd7b4448170.tar.gz |
Make Project owner of Loader.
-rw-r--r-- | buildstream/_frontend/app.py | 30 | ||||
-rw-r--r-- | buildstream/_loader/loader.py | 54 | ||||
-rw-r--r-- | buildstream/_pipeline.py | 41 | ||||
-rw-r--r-- | buildstream/_platform/linux.py | 4 | ||||
-rw-r--r-- | buildstream/_platform/platform.py | 3 | ||||
-rw-r--r-- | buildstream/_platform/unix.py | 4 | ||||
-rw-r--r-- | buildstream/_project.py | 63 | ||||
-rw-r--r-- | buildstream/_stream.py | 8 | ||||
-rw-r--r-- | tests/loader/__init__.py | 2 |
9 files changed, 122 insertions, 87 deletions
diff --git a/buildstream/_frontend/app.py b/buildstream/_frontend/app.py index 47ab30771..dc38c5906 100644 --- a/buildstream/_frontend/app.py +++ b/buildstream/_frontend/app.py @@ -34,6 +34,7 @@ from .. import Scope # Import various buildstream internals from .._context import Context +from .._platform import Platform from .._project import Project from .._exceptions import BstError, StreamError, LoadError, LoadErrorReason, AppError from .._message import Message, MessageType, unconditional_messages @@ -67,6 +68,7 @@ class App(): self.context = None # The Context object self.stream = None # The Stream object self.project = None # The toplevel Project object + self.loader = None self.logger = None # The LogLine object self.interactive = None # Whether we are running in interactive mode self.colors = None # Whether to use colors in logging @@ -199,12 +201,27 @@ class App(): if option_value is not None: setattr(self.context, context_attr, option_value) + Platform.create_instance(self.context) + + # Create the logger right before setting the message handler + self.logger = LogLine(self.context, + self._content_profile, + self._format_profile, + self._success_profile, + self._error_profile, + self._detail_profile, + indent=INDENT) + + # Propagate pipeline feedback to the user + self.context.set_message_handler(self._message_handler) + # # Load the Project # try: self.project = Project(directory, self.context, cli_options=self._main_options['option'], default_mirror=self._main_options.get('default_mirror')) + self.loader = self.project.loader except LoadError as e: # Let's automatically start a `bst init` session in this case @@ -219,24 +236,13 @@ class App(): except BstError as e: self._error_exit(e, "Error loading project") - # Create the logger right before setting the message handler - self.logger = LogLine(self.context, - self._content_profile, - self._format_profile, - self._success_profile, - self._error_profile, - self._detail_profile, - indent=INDENT) - - # Propagate pipeline feedback to the user - self.context.set_message_handler(self._message_handler) - # Now that we have a logger and message handler, # we can override the global exception hook. sys.excepthook = self._global_exception_handler # Create the stream right away, we'll need to pass it around self.stream = Stream(self.context, self.project, self._session_start, + self.loader, session_start_callback=self.session_start_cb, interrupt_callback=self._interrupt_handler, ticker_callback=self._tick, diff --git a/buildstream/_loader/loader.py b/buildstream/_loader/loader.py index b3cad2248..280805981 100644 --- a/buildstream/_loader/loader.py +++ b/buildstream/_loader/loader.py @@ -25,7 +25,6 @@ import shutil from .._exceptions import LoadError, LoadErrorReason from .. import Consistency -from .._project import Project from .. import _yaml from ..element import Element from .._profile import Topics, profile_start, profile_end @@ -49,11 +48,10 @@ from . import MetaSource # parent (Loader): A parent Loader object, in the case this is a junctioned Loader # tempdir (str): A directory to cleanup with the Loader, given to the loader by a parent # loader in the case that this loader is a subproject loader. -# fetch_subprojects (bool): Whether to fetch subprojects while loading # class Loader(): - def __init__(self, context, project, *, parent=None, tempdir=None, fetch_subprojects=False): + def __init__(self, context, project, *, parent=None, tempdir=None): # Ensure we have an absolute path for the base directory basedir = project.element_path @@ -68,7 +66,6 @@ class Loader(): # # Private members # - self._fetch_subprojects = fetch_subprojects self._context = context self._options = project.options # Project options (OptionPool) self._basedir = basedir # Base project directory @@ -88,11 +85,12 @@ class Loader(): # this is a bit more expensive due to deep copies # ticker (callable): An optional function for tracking load progress # targets (list of str): Target, element-path relative bst filenames in the project + # fetch_subprojects (bool): Whether to fetch subprojects while loading # # Raises: LoadError # # Returns: The toplevel LoadElement - def load(self, targets, rewritable=False, ticker=None): + def load(self, targets, rewritable=False, ticker=None, fetch_subprojects=False): for filename in targets: if os.path.isabs(filename): @@ -109,8 +107,9 @@ class Loader(): for target in targets: profile_start(Topics.LOAD_PROJECT, target) - junction, name, loader = self._parse_name(target, rewritable, ticker) - loader._load_file(name, rewritable, ticker) + junction, name, loader = self._parse_name(target, rewritable, ticker, + fetch_subprojects=fetch_subprojects) + loader._load_file(name, rewritable, ticker, fetch_subprojects) deps.append(Dependency(name, junction=junction)) profile_end(Topics.LOAD_PROJECT, target) @@ -136,7 +135,8 @@ class Loader(): # for target in targets: profile_start(Topics.SORT_DEPENDENCIES, target) - junction, name, loader = self._parse_name(target, rewritable, ticker) + junction, name, loader = self._parse_name(target, rewritable, ticker, + fetch_subprojects=fetch_subprojects) loader._sort_dependencies(name) profile_end(Topics.SORT_DEPENDENCIES, target) # Finally, wrap what we have into LoadElements and return the target @@ -197,11 +197,12 @@ class Loader(): # filename (str): The element-path relative bst file # rewritable (bool): Whether we should load in round trippable mode # ticker (callable): A callback to report loaded filenames to the frontend + # fetch_subprojects (bool): Whether to fetch subprojects while loading # # Returns: # (LoadElement): A loaded LoadElement # - def _load_file(self, filename, rewritable, ticker): + def _load_file(self, filename, rewritable, ticker, fetch_subprojects): # Silently ignore already loaded files if filename in self._elements: @@ -249,12 +250,13 @@ class Loader(): # Load all dependency files for the new LoadElement for dep in element.deps: if dep.junction: - self._load_file(dep.junction, rewritable, ticker) - loader = self._get_loader(dep.junction, rewritable=rewritable, ticker=ticker) + self._load_file(dep.junction, rewritable, ticker, fetch_subprojects) + loader = self._get_loader(dep.junction, rewritable=rewritable, ticker=ticker, + fetch_subprojects=fetch_subprojects) else: loader = self - dep_element = loader._load_file(dep.name, rewritable, ticker) + dep_element = loader._load_file(dep.name, rewritable, ticker, fetch_subprojects) if _yaml.node_get(dep_element.node, str, Symbol.KIND) == 'junction': raise LoadError(LoadErrorReason.INVALID_DATA, @@ -453,11 +455,12 @@ class Loader(): # # Args: # filename (str): Junction name + # fetch_subprojects (bool): Whether to fetch subprojects while loading # # Raises: LoadError # # Returns: A Loader or None if specified junction does not exist - def _get_loader(self, filename, *, rewritable=False, ticker=None, level=0): + def _get_loader(self, filename, *, rewritable=False, ticker=None, level=0, fetch_subprojects=False): # return previously determined result if filename in self._loaders: loader = self._loaders[filename] @@ -474,13 +477,14 @@ class Loader(): if self._parent: # junctions in the parent take precedence over junctions defined # in subprojects - loader = self._parent._get_loader(filename, rewritable=rewritable, ticker=ticker, level=level + 1) + loader = self._parent._get_loader(filename, rewritable=rewritable, ticker=ticker, + level=level + 1, fetch_subprojects=fetch_subprojects) if loader: self._loaders[filename] = loader return loader try: - self._load_file(filename, rewritable, ticker) + self._load_file(filename, rewritable, ticker, fetch_subprojects) except LoadError as e: if e.reason != LoadErrorReason.MISSING_FILE: # other load error @@ -509,7 +513,7 @@ class Loader(): # Handle the case where a subproject needs to be fetched # if source.get_consistency() == Consistency.RESOLVED: - if self._fetch_subprojects: + if fetch_subprojects: if ticker: ticker(filename, 'Fetching subproject from {} source'.format(source.get_kind())) source._fetch() @@ -535,7 +539,9 @@ class Loader(): # Load the project project_dir = os.path.join(basedir, element.path) try: - project = Project(project_dir, self._context, junction=element) + from .._project import Project + project = Project(project_dir, self._context, junction=element, + parent_loader=self, tempdir=basedir) except LoadError as e: if e.reason == LoadErrorReason.MISSING_PROJECT_CONF: raise LoadError(reason=LoadErrorReason.INVALID_JUNCTION, @@ -545,11 +551,7 @@ class Loader(): else: raise - loader = Loader(self._context, project, - parent=self, - tempdir=basedir, - fetch_subprojects=self._fetch_subprojects) - + loader = project.loader self._loaders[filename] = loader return loader @@ -580,13 +582,14 @@ class Loader(): # rewritable (bool): Whether the loaded files should be rewritable # this is a bit more expensive due to deep copies # ticker (callable): An optional function for tracking load progress + # fetch_subprojects (bool): Whether to fetch subprojects while loading # # Returns: # (tuple): - (str): name of the junction element # - (str): name of the element # - (Loader): loader for sub-project # - def _parse_name(self, name, rewritable, ticker): + def _parse_name(self, name, rewritable, ticker, fetch_subprojects=False): # We allow to split only once since deep junctions names are forbidden. # Users who want to refer to elements in sub-sub-projects are required # to create junctions on the top level project. @@ -594,6 +597,7 @@ class Loader(): if len(junction_path) == 1: return None, junction_path[-1], self else: - self._load_file(junction_path[-2], rewritable, ticker) - loader = self._get_loader(junction_path[-2], rewritable=rewritable, ticker=ticker) + self._load_file(junction_path[-2], rewritable, ticker, fetch_subprojects) + loader = self._get_loader(junction_path[-2], rewritable=rewritable, ticker=ticker, + fetch_subprojects=fetch_subprojects) return junction_path[-2], junction_path[-1], loader diff --git a/buildstream/_pipeline.py b/buildstream/_pipeline.py index 386a1c53b..b74c7c302 100644 --- a/buildstream/_pipeline.py +++ b/buildstream/_pipeline.py @@ -25,9 +25,7 @@ from operator import itemgetter from ._exceptions import PipelineError from ._message import Message, MessageType -from ._loader import Loader from ._profile import Topics, profile_start, profile_end -from .element import Element from . import Scope, Consistency from ._project import ProjectRefStorage @@ -80,7 +78,6 @@ class Pipeline(): # Private members # self._artifacts = artifacts - self._loader = None # load() # @@ -109,30 +106,9 @@ class Pipeline(): profile_start(Topics.LOAD_PIPELINE, "_".join(t.replace(os.sep, '-') for t in targets)) - self._loader = Loader(self._context, self._project, - fetch_subprojects=fetch_subprojects) - - with self._context.timed_activity("Loading pipeline", silent_nested=True): - meta_elements = self._loader.load(targets, rewritable, None) - - # Resolve the real elements now that we've loaded the project - with self._context.timed_activity("Resolving pipeline"): - elements = [ - Element._new_from_meta(meta, self._artifacts) - for meta in meta_elements - ] - - # Now warn about any redundant source references which may have - # been discovered in the resolve() phase. - redundant_refs = Element._get_redundant_source_refs() - if redundant_refs: - detail = "The following inline specified source references will be ignored:\n\n" - lines = [ - "{}:{}".format(source._get_provenance(), ref) - for source, ref in redundant_refs - ] - detail += "\n".join(lines) - self._message(MessageType.WARN, "Ignoring redundant source references", detail=detail) + elements = self._project.load_elements(targets, self._artifacts, + rewritable=rewritable, + fetch_subprojects=fetch_subprojects) # Now create element groups to match the input target groups elt_iter = iter(elements) @@ -388,17 +364,6 @@ class Pipeline(): detail += " " + element._get_full_name() + "\n" raise PipelineError("Inconsistent pipeline", detail=detail, reason="inconsistent-pipeline") - # cleanup() - # - # Cleans up resources used by the Pipeline. - # - def cleanup(self): - if self._loader: - self._loader.cleanup() - - # Reset the element loader state - Element._reset_load_state() - ############################################################# # Private Methods # ############################################################# diff --git a/buildstream/_platform/linux.py b/buildstream/_platform/linux.py index 56ebb410e..a5fd0d687 100644 --- a/buildstream/_platform/linux.py +++ b/buildstream/_platform/linux.py @@ -30,9 +30,9 @@ from . import Platform class Linux(Platform): - def __init__(self, context, project): + def __init__(self, context): - super().__init__(context, project) + super().__init__(context) self._die_with_parent_available = _site.check_bwrap_version(0, 1, 8) self._user_ns_available = self._check_user_ns_available(context) diff --git a/buildstream/_platform/platform.py b/buildstream/_platform/platform.py index 29da33563..8a074eb62 100644 --- a/buildstream/_platform/platform.py +++ b/buildstream/_platform/platform.py @@ -35,9 +35,8 @@ class Platform(): # Args: # context (context): The project context # - def __init__(self, context, project): + def __init__(self, context): self.context = context - self.project = project @classmethod def create_instance(cls, *args, **kwargs): diff --git a/buildstream/_platform/unix.py b/buildstream/_platform/unix.py index be4c129d3..0306a4ac5 100644 --- a/buildstream/_platform/unix.py +++ b/buildstream/_platform/unix.py @@ -28,9 +28,9 @@ from . import Platform class Unix(Platform): - def __init__(self, context, project): + def __init__(self, context): - super().__init__(context, project) + super().__init__(context) self._artifact_cache = CASCache(context) # Not necessarily 100% reliable, but we want to fail early. diff --git a/buildstream/_project.py b/buildstream/_project.py index 3ac562836..12f23e2e4 100644 --- a/buildstream/_project.py +++ b/buildstream/_project.py @@ -34,6 +34,9 @@ from ._elementfactory import ElementFactory from ._sourcefactory import SourceFactory from ._projectrefs import ProjectRefs, ProjectRefStorage from ._versions import BST_FORMAT_VERSION +from ._loader import Loader +from .element import Element +from ._message import Message, MessageType # Project Configuration file @@ -68,7 +71,8 @@ class HostMount(): # class Project(): - def __init__(self, directory, context, *, junction=None, cli_options=None, default_mirror=None): + def __init__(self, directory, context, *, junction=None, cli_options=None, + default_mirror=None, parent_loader=None, tempdir=None): # The project name self.name = None @@ -118,6 +122,10 @@ class Project(): self._context.add_project(self) + self.loader = Loader(self._context, self, + parent=parent_loader, + tempdir=tempdir) + # translate_url(): # # Translates the given url which may be specified with an alias @@ -232,6 +240,59 @@ class Project(): mirror_list.append(self._aliases[alias]) return mirror_list + # load_elements() + # + # Loads elements from target names. + # + # Args: + # targets (list): Target names + # artifacts (ArtifactCache): Artifact cache + # rewritable (bool): Whether the loaded files should be rewritable + # this is a bit more expensive due to deep copies + # fetch_subprojects (bool): Whether we should fetch subprojects as a part of the + # loading process, if they are not yet locally cached + # + # Returns: + # (list): A list of loaded Element + # + def load_elements(self, targets, artifacts, *, + rewritable=False, fetch_subprojects=False): + with self._context.timed_activity("Loading elements", silent_nested=True): + meta_elements = self.loader.load(targets, rewritable=rewritable, + ticker=None, + fetch_subprojects=fetch_subprojects) + + with self._context.timed_activity("Resolving elements"): + elements = [ + Element._new_from_meta(meta, artifacts) + for meta in meta_elements + ] + + # Now warn about any redundant source references which may have + # been discovered in the resolve() phase. + redundant_refs = Element._get_redundant_source_refs() + if redundant_refs: + detail = "The following inline specified source references will be ignored:\n\n" + lines = [ + "{}:{}".format(source._get_provenance(), ref) + for source, ref in redundant_refs + ] + detail += "\n".join(lines) + self._context.message( + Message(None, MessageType.WARN, "Ignoring redundant source references", detail=detail)) + + return elements + + # cleanup() + # + # Cleans up resources used loading elements + # + def cleanup(self): + self.loader.cleanup() + + # Reset the element loader state + Element._reset_load_state() + # _load(): # # Loads the project configuration file in the project directory. diff --git a/buildstream/_stream.py b/buildstream/_stream.py index 843c8e8e2..63a31dbd7 100644 --- a/buildstream/_stream.py +++ b/buildstream/_stream.py @@ -45,6 +45,7 @@ from . import Scope, Consistency # context (Context): The Context object # project (Project): The Project object # session_start (datetime): The time when the session started +# loader (Loader): The Loader object # session_start_callback (callable): A callback to invoke when the session starts # interrupt_callback (callable): A callback to invoke when we get interrupted # ticker_callback (callable): Invoked every second while running the scheduler @@ -53,7 +54,7 @@ from . import Scope, Consistency # class Stream(): - def __init__(self, context, project, session_start, *, + def __init__(self, context, project, session_start, loader, *, session_start_callback=None, interrupt_callback=None, ticker_callback=None, @@ -71,7 +72,6 @@ class Stream(): # # Private members # - Platform.create_instance(context, project) self._platform = Platform.get_platform() self._artifacts = self._platform.artifactcache self._context = context @@ -90,8 +90,8 @@ class Stream(): # Cleans up application state # def cleanup(self): - if self._pipeline: - self._pipeline.cleanup() + if self._project: + self._project.cleanup() # load_selection() # diff --git a/tests/loader/__init__.py b/tests/loader/__init__.py index 49db9cfd0..fcefdacf5 100644 --- a/tests/loader/__init__.py +++ b/tests/loader/__init__.py @@ -11,4 +11,4 @@ from buildstream._loader import Loader def make_loader(basedir): context = Context() project = Project(basedir, context) - return Loader(context, project) + return project.loader |