diff options
Diffstat (limited to 'buildstream/_frontend/main.py')
-rw-r--r-- | buildstream/_frontend/main.py | 90 |
1 files changed, 81 insertions, 9 deletions
diff --git a/buildstream/_frontend/main.py b/buildstream/_frontend/main.py index fd2384ec3..5ec6884a0 100644 --- a/buildstream/_frontend/main.py +++ b/buildstream/_frontend/main.py @@ -21,6 +21,7 @@ import os import sys import resource +import datetime from contextlib import contextmanager from blessings import Terminal @@ -33,8 +34,8 @@ from .. import Scope # Import various buildstream internals from .._context import Context from .._project import Project -from .._exceptions import BstError -from .._message import MessageType, unconditional_messages +from .._exceptions import BstError, PipelineError +from .._message import Message, MessageType, unconditional_messages from .._pipeline import Pipeline from .._scheduler import Scheduler from .._profile import Topics, profile_start, profile_end @@ -50,10 +51,14 @@ INDENT = 4 ################################################################## # Main Application State # ################################################################## - class App(): def __init__(self, main_options): + + # Snapshot the start time of the session at the earliest opportunity, + # this is used for inclusive session time logging + self.session_start = datetime.datetime.now() + self.main_options = main_options self.logger = None self.status = None @@ -104,13 +109,39 @@ class App(): # Set soft limit to hard limit resource.setrlimit(resource.RLIMIT_NOFILE, (limits[1], limits[1])) + # initialized() # - # Initialize the main pipeline + # Context manager to initialize the application and optionally run a session + # within the context manager. # - def initialize(self, elements, except_=tuple(), rewritable=False, - use_configured_remote_caches=False, add_remote_cache=None, - track_elements=None, fetch_subprojects=False): - + # This context manager will take care of catching errors from within the + # context and report them consistently, so the CLI need not take care of + # reporting the errors and exiting with a consistent error status. + # + # Args: + # elements (list of elements): The elements to load recursively + # session_name (str): The name of the session, or None for no session + # except_ (list of elements): The elements to except + # rewritable (bool): Whether we should load the YAML files for roundtripping + # use_configured_remote_caches (bool): Whether we should contact remotes + # add_remote_cache (str): The URL for an explicitly mentioned remote cache + # track_elements (list of elements): Elements which are to be tracked + # fetch_subprojects (bool): Whether we should fetch subprojects as a part of the + # loading process, if they are not yet locally cached + # + # Note that the except_ argument may have a subtly different meaning depending + # on the activity performed on the Pipeline. In normal circumstances the except_ + # argument excludes elements from the `elements` list. In a build session, the + # except_ elements are excluded from the tracking plan. + # + # If a session_name is provided, we treat the block as a session, and print + # the session header and summary, and time the main session from startup time. + # + @contextmanager + def initialized(self, elements, *, session_name=None, + except_=tuple(), rewritable=False, + use_configured_remote_caches=False, add_remote_cache=None, + track_elements=None, fetch_subprojects=False): profile_start(Topics.LOAD_PIPELINE, "_".join(t.replace(os.sep, '-') for t in elements)) directory = self.main_options['directory'] @@ -150,7 +181,7 @@ class App(): self.interactive_failures = False # Create the application's scheduler - self.scheduler = Scheduler(self.context, + self.scheduler = Scheduler(self.context, self.session_start, interrupt_callback=self.interrupt_handler, ticker_callback=self.tick, job_start_callback=self.job_started, @@ -176,6 +207,9 @@ class App(): # Propagate pipeline feedback to the user self.context._set_message_handler(self.message_handler) + if session_name: + self.message(MessageType.START, session_name) + try: self.project = Project(directory, self.context, cli_options=self.main_options['option']) except BstError as e: @@ -209,6 +243,44 @@ class App(): profile_end(Topics.LOAD_PIPELINE, "_".join(t.replace(os.sep, '-') for t in elements)) + # Print the heading + if session_name: + self.print_heading() + + # Run the body of the session here, once everything is loaded + try: + yield + + # Catch the error outside of the session timer and summarize what happened + except BstError as e: + elapsed = self.scheduler.elapsed_time() + + if session_name: + if isinstance(e, PipelineError) and e.terminated: # pylint: disable=no-member + self.message(MessageType.WARN, session_name + ' Terminated', elapsed=elapsed) + else: + self.message(MessageType.FAIL, session_name, elapsed=elapsed) + + self.print_error(e) + + if session_name: + self.print_summary() + + sys.exit(-1) + + # No exceptions occurred, print the summary + else: + if session_name: + self.message(MessageType.SUCCESS, session_name, elapsed=self.scheduler.elapsed_time()) + self.print_summary() + + # Local message propagator + # + def message(self, message_type, message, **kwargs): + args = dict(kwargs) + self.context._message( + Message(None, message_type, message, **args)) + # # Render the status area, conditional on some internal state # |