summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTristan Maat <tristan.maat@codethink.co.uk>2018-03-14 13:11:01 +0000
committerTristan Maat <tristan.maat@codethink.co.uk>2018-03-27 14:32:52 +0100
commitf761140f18a7d54caf6e6dba8a722b9ff1f4430e (patch)
tree4175da85352754b9f4f62d281454ab0c331cc6dc
parentfb332267bfccc54ea7bc57af7a676a4eee635bcd (diff)
downloadbuildstream-f761140f18a7d54caf6e6dba8a722b9ff1f4430e.tar.gz
Make workspaces use objects instead of pipeline helper methods
-rw-r--r--buildstream/_frontend/cli.py11
-rw-r--r--buildstream/_pipeline.py38
-rw-r--r--buildstream/_project.py157
-rw-r--r--buildstream/_workspaces.py322
-rw-r--r--buildstream/element.py11
-rw-r--r--buildstream/source.py90
6 files changed, 376 insertions, 253 deletions
diff --git a/buildstream/_frontend/cli.py b/buildstream/_frontend/cli.py
index c0bf74117..c6c9d8156 100644
--- a/buildstream/_frontend/cli.py
+++ b/buildstream/_frontend/cli.py
@@ -641,7 +641,7 @@ def workspace_close(app, remove_dir, element):
app.initialize((element,))
- if app.pipeline.project._get_workspace(app.pipeline.targets[0].name) is None:
+ if app.pipeline.project._workspaces.get_workspace(app.pipeline.targets[0]) is None:
click.echo("ERROR: Workspace '{}' does not exist".format(element), err=True)
sys.exit(-1)
@@ -716,13 +716,12 @@ def workspace_list(app):
sys.exit(-1)
workspaces = []
- for element_name, directory in project._list_workspaces():
- workspace_ = {
+ for element_name, workspace_ in project._workspaces.list():
+ workspace_detail = {
'element': element_name,
- 'directory': directory,
+ 'directory': workspace_.path,
}
-
- workspaces.append(workspace_)
+ workspaces.append(workspace_detail)
_yaml.dump({
'workspaces': workspaces
diff --git a/buildstream/_pipeline.py b/buildstream/_pipeline.py
index 268e7111c..c1f2afdef 100644
--- a/buildstream/_pipeline.py
+++ b/buildstream/_pipeline.py
@@ -188,15 +188,14 @@ class Pipeline():
raise PipelineError("{}: {}".format(plugin, e), reason=e.reason) from e
def initialize_workspaces(self):
- for element_name, workspace in self.project._list_workspaces():
+ for element_name, workspace in self.project._workspaces.list():
for target in self.targets:
element = target.search(Scope.ALL, element_name)
if element is None:
self.unused_workspaces.append((element_name, workspace))
- continue
-
- self.project._set_workspace(element, workspace)
+ else:
+ workspace.init(element)
def initialize_remote_caches(self):
def remote_failed(url, error):
@@ -627,7 +626,7 @@ class Pipeline():
raise PipelineError("The given element has no sources", detail=detail)
# Check for workspace config
- if self.project._get_workspace(target.name):
+ if self.project._workspaces.get_workspace(target):
raise PipelineError("Workspace '{}' is already defined."
.format(target.name))
@@ -674,18 +673,17 @@ class Pipeline():
except OSError as e:
raise PipelineError("Failed to create workspace directory: {}".format(e)) from e
+ workspace = self.project._workspaces.create_workspace(target, workdir)
+
if not no_checkout:
if not force and os.listdir(directory):
raise PipelineError("Checkout directory is not empty: {}".format(directory))
with target.timed_activity("Staging sources to {}".format(directory)):
- for source in target.sources():
- source._init_workspace(directory)
-
- self.project._set_workspace(target, workdir)
+ workspace.open()
with target.timed_activity("Saving workspace configuration"):
- self.project._save_workspace_config()
+ self.project._workspaces.save_config()
# close_workspace
#
@@ -700,26 +698,26 @@ class Pipeline():
# Remove workspace directory if prompted
if remove_dir:
- path = self.project._get_workspace(target.name)
- if path is not None:
+ workspace = self.project._workspaces.get_workspace(target)
+ if workspace is not None:
with target.timed_activity("Removing workspace directory {}"
- .format(path)):
+ .format(workspace.path)):
try:
- shutil.rmtree(path)
+ shutil.rmtree(workspace.path)
except OSError as e:
raise PipelineError("Could not remove '{}': {}"
- .format(path, e)) from e
+ .format(workspace.path, e)) from e
# Delete the workspace config entry
with target.timed_activity("Removing workspace"):
try:
- self.project._delete_workspace(target.name)
+ self.project._workspaces.delete_workspace(target)
except KeyError:
raise PipelineError("Workspace '{}' is currently not defined"
.format(target.name))
# Update workspace config
- self.project._save_workspace_config()
+ self.project._workspaces.save_config()
# Reset source to avoid checking out the (now empty) workspace
for source in target.sources():
@@ -738,15 +736,15 @@ class Pipeline():
def reset_workspace(self, scheduler, track, no_checkout):
# When working on workspaces we only have one target
target = self.targets[0]
- workspace_dir = self.project._get_workspace(target.name)
+ workspace = self.project._workspaces.get_workspace(target)
- if workspace_dir is None:
+ if workspace is None:
raise PipelineError("Workspace '{}' is currently not defined"
.format(target.name))
self.close_workspace(True)
- self.open_workspace(scheduler, workspace_dir, no_checkout, track, False)
+ self.open_workspace(scheduler, workspace.path, no_checkout, track, False)
# pull()
#
diff --git a/buildstream/_project.py b/buildstream/_project.py
index 33d10e1f4..003418682 100644
--- a/buildstream/_project.py
+++ b/buildstream/_project.py
@@ -33,6 +33,7 @@ from ._artifactcache import artifact_cache_specs_from_config_node
from ._elementfactory import ElementFactory
from ._sourcefactory import SourceFactory
from ._projectrefs import ProjectRefs, ProjectRefStorage
+from ._workspaces import Workspaces
# The base BuildStream format version
@@ -92,7 +93,7 @@ class Project():
self._elements = {} # Element specific configurations
self._sources = {} # Source specific configurations
self._aliases = {} # Aliases dictionary
- self._workspaces = {} # Workspaces
+ self._workspaces = None # Workspaces
self._plugin_source_origins = [] # Origins of custom sources
self._plugin_element_origins = [] # Origins of custom elements
self._options = None # Project options, the OptionPool
@@ -218,8 +219,7 @@ class Project():
self.artifact_cache_specs = artifact_cache_specs_from_config_node(config)
# Workspace configurations
- workspace_config = self._load_workspace_config()
- self._workspaces = self._parse_workspace_config(workspace_config)
+ self._workspaces = Workspaces(self)
# Assert project version
format_version = _yaml.node_get(config, int, 'format-version')
@@ -378,157 +378,6 @@ class Project():
origin_dict['path'] = os.path.join(self.directory, origin_dict['path'])
destination.append(origin_dict)
- # _list_workspaces()
- #
- # Generator function to enumerate workspaces.
- #
- # Yields:
- # A tuple in the following format: (element, path).
- def _list_workspaces(self):
- for element, _ in _yaml.node_items(self._workspaces):
- yield (element, self._workspaces[element]["path"])
-
- # _get_workspace()
- #
- # Get the path of the workspace source associated with the given
- # element's source at the given index
- #
- # Args:
- # element (str) - The element name
- #
- # Returns:
- # None if no workspace is open, the path to the workspace
- # otherwise
- #
- def _get_workspace(self, element):
- if element not in self._workspaces:
- return None
- return self._workspaces[element]["path"]
-
- # _set_workspace()
- #
- # Set the path of the workspace associated with the given
- # element's source at the given index
- #
- # Args:
- # element (str) - The element name
- # path (str) - The path to set the workspace to
- #
- def _set_workspace(self, element, path):
- if element.name not in self._workspaces:
- self._workspaces[element.name] = {}
-
- self._workspaces[element.name]["path"] = path
- element._set_source_workspaces(path)
-
- # _delete_workspace()
- #
- # Remove the workspace from the workspace element. Note that this
- # does *not* remove the workspace from the stored yaml
- # configuration, call _save_workspace_config() afterwards.
- #
- # Args:
- # element (str) - The element name
- #
- def _delete_workspace(self, element):
- del self._workspaces[element]
-
- # _load_workspace_config()
- #
- # Load the workspace configuration and return a node containing
- # all open workspaces for the project
- #
- # Returns:
- #
- # A node containing a dict that assigns elements to their
- # workspaces. For example:
- #
- # alpha.bst: /home/me/alpha
- # bravo.bst: /home/me/bravo
- #
- def _load_workspace_config(self):
- os.makedirs(os.path.join(self.directory, ".bst"), exist_ok=True)
- workspace_file = os.path.join(self.directory, ".bst", "workspaces.yml")
- try:
- open(workspace_file, "a").close()
- except IOError as e:
- raise LoadError(LoadErrorReason.MISSING_FILE,
- "Could not load workspace config: {}".format(e)) from e
-
- return _yaml.load(workspace_file)
-
- # _parse_workspace_config()
- #
- # If workspace config is in old-style format, i.e. it is using
- # source-specific workspaces, try to convert it to element-specific
- # workspaces.
- #
- # This method will rewrite workspace config, if it is in old format.
- #
- # Args:
- # workspaces (dict): current workspace config, usually output of _load_workspace_config()
- #
- # Returns:
- # (bool, dict) Whether the workspace config needs to be
- # rewritten and extracted workspaces
- #
- # Raises: LoadError if there was a problem with the workspace config
- #
- def _parse_workspace_config(self, workspaces):
- version = _yaml.node_get(workspaces, int, "format-version", default_value=0)
-
- if version == 0:
- # Pre-versioning format can be of two forms
- for element, config in _yaml.node_items(workspaces):
- if isinstance(config, str):
- pass
-
- elif isinstance(config, dict):
- sources = list(_yaml.node_items(config))
- if len(sources) > 1:
- detail = "There are multiple workspaces open for '{}'.\n" + \
- "This is not supported anymore.\n" + \
- "Please remove this element from '{}'."
- raise LoadError(LoadErrorReason.INVALID_DATA,
- detail.format(element,
- os.path.join(self.directory, ".bst", "workspaces.yml")))
-
- workspaces[element] = sources[0][1]
-
- else:
- raise LoadError(LoadErrorReason.INVALID_DATA,
- "Workspace config is in unexpected format.")
-
- res = {
- element: {"path": config}
- for element, config in _yaml.node_items(workspaces)
- }
-
- elif version == BST_WORKSPACE_FORMAT_VERSION:
- res = _yaml.node_get(workspaces, dict, "workspaces", default_value={})
-
- else:
- raise LoadError(LoadErrorReason.INVALID_DATA,
- "Workspace configuration format version {} not supported."
- "Your version of buildstream may be too old. Max supported version: {}"
- .format(version, BST_WORKSPACE_FORMAT_VERSION))
-
- return res
-
- # _save_workspace_config()
- #
- # Dump the current workspace element to the project configuration
- # file. This makes any changes performed with _delete_workspace or
- # _set_workspace permanent
- #
- def _save_workspace_config(self):
- _yaml.dump(
- {
- "format-version": BST_WORKSPACE_FORMAT_VERSION,
- "workspaces": _yaml.node_sanitize(self._workspaces)
- },
- os.path.join(self.directory, ".bst", "workspaces.yml"))
-
def _extract_plugin_paths(self, node, name):
if not node:
return
diff --git a/buildstream/_workspaces.py b/buildstream/_workspaces.py
new file mode 100644
index 000000000..9c3933bbd
--- /dev/null
+++ b/buildstream/_workspaces.py
@@ -0,0 +1,322 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2018 Codethink Limited
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+#
+# Authors:
+# Tristan Maat <tristan.maat@codethink.co.uk>
+
+import os
+from . import utils
+from . import _yaml
+
+from ._exceptions import LoadError, LoadErrorReason
+
+
+BST_WORKSPACE_FORMAT_VERSION = 1
+
+
+# Workspace()
+#
+# An object to contain various helper functions and data required for
+# workspaces.
+#
+# last_successful, path and running_files are intended to be public
+# properties, but may be best accessed using this classes' helper
+# methods.
+#
+# Args:
+# path (str): The path that should host this workspace
+# project (Project): The project this workspace is part of
+#
+class Workspace():
+ def __init__(self, path, project):
+ self.path = path
+
+ self._element = None
+ self._project = project
+ self.__key = None
+
+ @classmethod
+ def from_yaml_node(cls, node, project):
+ path = _yaml.node_get(node, str, 'path')
+
+ return cls(path, project)
+
+ # _to_dict()
+ #
+ # Convert this object to a dict for storage purposes
+ #
+ # Returns:
+ # (dict) A dict representation of the workspace
+ #
+ def _to_dict(self):
+ to_return = ['path']
+
+ return {key: val for key, val in self.__dict__.items()
+ if key in to_return and val is not None}
+
+ # open()
+ #
+ # "Open" this workspace, calling the init_workspace method of all
+ # its sources.
+ #
+ def open(self):
+ for source in self._element.sources():
+ source._init_workspace(self.path)
+
+ # init()
+ #
+ # Initialize the elements and sources associated to this
+ # workspace. Must be called before this object is used.
+ #
+ def init(self, element):
+ self._element = element
+ for source in element.sources():
+ source._set_workspace(self)
+
+ # invalidate_key()
+ #
+ # Invalidate the workspace key, forcing a recalculation next time
+ # it is accessed.
+ #
+ def invalidate_key(self):
+ self.__key = None
+
+ # stage()
+ #
+ # Stage the workspace to the given directory.
+ #
+ # Args:
+ # directory (str) - The directory into which to stage this workspace
+ #
+ def stage(self, directory):
+ fullpath = os.path.join(self._project.directory, self.path)
+ if os.path.isdir(fullpath):
+ utils.copy_files(fullpath, directory)
+ else:
+ destfile = os.path.join(directory, os.path.basename(self.path))
+ utils.safe_copy(fullpath, destfile)
+
+ # get_key()
+ #
+ # Get a unique key for this workspace.
+ #
+ # Args:
+ # recalculate (bool) - Whether to recalculate the key
+ #
+ # Returns:
+ # (str) A unique key for this workspace
+ #
+ def get_key(self, recalculate=False):
+ def unique_key(filename):
+ if os.path.isdir(filename):
+ return "0"
+ elif os.path.islink(filename):
+ return "1"
+
+ try:
+ return utils.sha256sum(filename)
+ except FileNotFoundError as e:
+ raise LoadError(LoadErrorReason.MISSING_FILE,
+ "Failed loading workspace. Did you remove the "
+ "workspace directory? {}".format(e))
+
+ if recalculate or self.__key is None:
+ fullpath = os.path.join(self._project.directory, self.path)
+
+ # Get a list of tuples of the the project relative paths and fullpaths
+ if os.path.isdir(fullpath):
+ filelist = utils.list_relative_paths(fullpath)
+ filelist = [(relpath, os.path.join(fullpath, relpath)) for relpath in filelist]
+ else:
+ filelist = [(self.path, fullpath)]
+
+ self.__key = [(relpath, unique_key(self.path)) for relpath, fullpath in filelist]
+
+ return self.__key
+
+ # get_absolute_path():
+ #
+ # Returns: The absolute path of the element's workspace.
+ #
+ def get_absolute_path(self):
+ return os.path.join(self._project.directory, self.path)
+
+
+# Workspaces()
+#
+# A class to manage Workspaces for multiple elements.
+#
+# Args:
+# project (Project): The project the workspaces should be associated to
+#
+class Workspaces():
+ def __init__(self, project):
+ self._project = project
+ workspace_config = self.__load_config()
+ self._workspaces = self.__parse_workspace_config(workspace_config)
+
+ # _list_workspaces()
+ #
+ # Generator function to enumerate workspaces.
+ #
+ # Yields:
+ # A tuple in the following format: (str, Workspace), where the
+ # first element is the name of the workspaced element.
+ def list(self):
+ for element, _ in _yaml.node_items(self._workspaces):
+ yield (element, self._workspaces[element])
+
+ # create_workspace()
+ #
+ # Create a workspace in the given path for the given element.
+ #
+ # Args:
+ # element (Element) - The element for which to create a workspace
+ # path (str) - The path in which the workspace should be kept
+ #
+ def create_workspace(self, element, path):
+ self._workspaces[element.name] = Workspace(path, self._project)
+ self._workspaces[element.name].init(element)
+
+ return self._workspaces[element.name]
+
+ # _get_workspace()
+ #
+ # Get the path of the workspace source associated with the given
+ # element's source at the given index
+ #
+ # Args:
+ # element (Element) - The element whose workspace to return
+ #
+ # Returns:
+ # (None|Workspace)
+ #
+ def get_workspace(self, element):
+ if element.name not in self._workspaces:
+ return None
+ return self._workspaces[element.name]
+
+ # delete_workspace()
+ #
+ # Remove the workspace from the workspace element. Note that this
+ # does *not* remove the workspace from the stored yaml
+ # configuration, call save_config() afterwards.
+ #
+ # Args:
+ # element (Element) - The element whose workspace to delete
+ #
+ def delete_workspace(self, element):
+ del self._workspaces[element.name]
+
+ # save_config()
+ #
+ # Dump the current workspace element to the project configuration
+ # file. This makes any changes performed with delete_workspace or
+ # create_workspace permanent
+ #
+ def save_config(self):
+ config = {
+ 'format-version': BST_WORKSPACE_FORMAT_VERSION,
+ 'workspaces': {
+ element: workspace._to_dict()
+ for element, workspace in _yaml.node_items(self._workspaces)
+ }
+ }
+
+ _yaml.dump(_yaml.node_sanitize(config),
+ os.path.join(self._project.directory, ".bst", "workspaces.yml"))
+
+ # _load_config()
+ #
+ # Load the workspace configuration and return a node containing
+ # all open workspaces for the project
+ #
+ # Returns:
+ #
+ # A node containing a dict that assigns elements to their
+ # workspaces. For example:
+ #
+ # alpha.bst: /home/me/alpha
+ # bravo.bst: /home/me/bravo
+ #
+ def __load_config(self):
+ os.makedirs(os.path.join(self._project.directory, ".bst"), exist_ok=True)
+ workspace_file = os.path.join(self._project.directory, ".bst", "workspaces.yml")
+ try:
+ open(workspace_file, "a").close()
+ except IOError as e:
+ raise LoadError(LoadErrorReason.MISSING_FILE,
+ "Could not load workspace config: {}".format(e)) from e
+
+ return _yaml.load(workspace_file)
+
+ # __parse_workspace_config_format()
+ #
+ # If workspace config is in old-style format, i.e. it is using
+ # source-specific workspaces, try to convert it to element-specific
+ # workspaces.
+ #
+ # Args:
+ # workspaces (dict): current workspace config, usually output of _load_workspace_config()
+ #
+ # Returns:
+ # (dict) The extracted workspaces
+ #
+ # Raises: LoadError if there was a problem with the workspace config
+ #
+ def __parse_workspace_config(self, workspaces):
+ version = _yaml.node_get(workspaces, int, "format-version", default_value=0)
+
+ if version == 0:
+ # Pre-versioning format can be of two forms
+ for element, config in _yaml.node_items(workspaces):
+ if isinstance(config, str):
+ pass
+
+ elif isinstance(config, dict):
+ sources = list(_yaml.node_items(config))
+ if len(sources) > 1:
+ detail = "There are multiple workspaces open for '{}'.\n" + \
+ "This is not supported anymore.\n" + \
+ "Please remove this element from '{}'."
+ raise LoadError(LoadErrorReason.INVALID_DATA,
+ detail.format(element,
+ os.path.join(self._project.directory, ".bst", "workspaces.yml")))
+
+ workspaces[element] = sources[0][1]
+
+ else:
+ raise LoadError(LoadErrorReason.INVALID_DATA,
+ "Workspace config is in unexpected format.")
+
+ res = {
+ element: Workspace(config, self._project)
+ for element, config in _yaml.node_items(workspaces)
+ }
+
+ elif version == BST_WORKSPACE_FORMAT_VERSION:
+ workspaces = _yaml.node_get(workspaces, dict, "workspaces", default_value={})
+ res = {element: Workspace.from_yaml_node(node, self._project)
+ for element, node in _yaml.node_items(workspaces)}
+
+ else:
+ raise LoadError(LoadErrorReason.INVALID_DATA,
+ "Workspace configuration format version {} not supported."
+ "Your version of buildstream may be too old. Max supported version: {}"
+ .format(version, BST_WORKSPACE_FORMAT_VERSION))
+
+ return res
diff --git a/buildstream/element.py b/buildstream/element.py
index 131a834e1..b36ae965c 100644
--- a/buildstream/element.py
+++ b/buildstream/element.py
@@ -718,6 +718,14 @@ class Element(Plugin):
# Private Methods used in BuildStream #
#############################################################
+ # _get_workspace():
+ #
+ # Returns:
+ # (Workspace|None): A workspace associated with this element
+ #
+ def _get_workspace(self):
+ return self._get_project()._workspaces.get_workspace(self)
+
# _write_script():
#
# Writes a script to the given directory.
@@ -1472,11 +1480,12 @@ class Element(Plugin):
def _stage_sources_in_sandbox(self, sandbox, directory, mount_workspaces=True):
if mount_workspaces:
+ workspace = self._get_workspace()
# First, mount sources that have an open workspace
sources_to_mount = [source for source in self.sources() if source._has_workspace()]
for source in sources_to_mount:
mount_point = source._get_staging_path(directory)
- mount_source = source._get_workspace_path()
+ mount_source = workspace.get_absolute_path()
sandbox.mark_directory(mount_point)
sandbox._set_mount_source(mount_point, mount_source)
diff --git a/buildstream/source.py b/buildstream/source.py
index 0a5c26cd1..d7fac7280 100644
--- a/buildstream/source.py
+++ b/buildstream/source.py
@@ -28,7 +28,7 @@ from contextlib import contextmanager
from . import Plugin
from . import _yaml, utils
-from ._exceptions import BstError, ImplError, LoadError, LoadErrorReason, ErrorDomain
+from ._exceptions import BstError, ImplError, ErrorDomain
from ._projectrefs import ProjectRefStorage
@@ -91,7 +91,6 @@ class Source(Plugin):
self.__tracking = False # Source is scheduled to be tracked
self.__assemble_scheduled = False # Source is scheduled to be assembled
self.__workspace = None # Directory of the currently active workspace
- self.__workspace_key = None # Cached directory content hashes for workspaced source
# Collect the composited element configuration and
# ask the element to configure itself.
@@ -328,7 +327,7 @@ class Source(Plugin):
# A workspace is considered inconsistent in the case
# that it's directory went missing
#
- fullpath = self._get_workspace_path()
+ fullpath = self.__workspace.get_absolute_path()
if not os.path.exists(fullpath):
self.__consistency = Consistency.INCONSISTENT
@@ -337,11 +336,6 @@ class Source(Plugin):
def _get_consistency(self):
return self.__consistency
- # Return the absolute path of the element's workspace
- #
- def _get_workspace_path(self):
- return os.path.join(self.get_project_directory(), self.__workspace)
-
# Mark a source as scheduled to be tracked
#
# This is used across the pipeline in sessions where the
@@ -363,7 +357,8 @@ class Source(Plugin):
self.__assemble_scheduled = True
# Invalidate workspace key as the build modifies the workspace directory
- self.__workspace_key = None
+ if self._has_workspace():
+ self.__workspace.invalidate_key()
# _assemble_done():
#
@@ -413,7 +408,8 @@ class Source(Plugin):
staging_directory = self._ensure_directory(directory)
if self._has_workspace():
- self._stage_workspace(staging_directory)
+ with self.timed_activity("Staging local files at {}".format(self.__workspace.path)):
+ self.__workspace.stage(staging_directory)
else:
self.stage(staging_directory)
@@ -434,7 +430,8 @@ class Source(Plugin):
key['directory'] = self.__directory
if self._has_workspace():
- key['workspace'] = self._get_workspace_key()
+ assert not self.__assemble_scheduled
+ key['workspace'] = self.__workspace.get_key()
else:
key['unique'] = self.get_unique_key()
@@ -615,80 +612,29 @@ class Source(Plugin):
return new_ref
- # Set the current workspace directory
+ # Set the current workspace
#
- # Note that this invalidate the workspace key.
+ # Note that this invalidates the workspace key.
#
- def _set_workspace(self, directory):
- self.__workspace = directory
- self.__workspace_key = None
+ def _set_workspace(self, workspace):
+ if self._has_workspace():
+ self.__workspace.invalidate_key()
+ self.__workspace = workspace
# Return the current workspace directory
def _get_workspace(self):
- return self.__workspace
+ return self.__workspace.path
# Delete the workspace
#
- # Note that this invalidate the workspace key.
+ # Note that this invalidates the workspace key.
#
def _del_workspace(self):
+ if self._has_workspace():
+ self.__workspace.invalidate_key()
self.__workspace = None
- self.__workspace_key = None
# Whether the source has a set workspace
#
def _has_workspace(self):
return self.__workspace is not None
-
- # Stage the workspace
- #
- def _stage_workspace(self, directory):
- fullpath = self._get_workspace_path()
-
- with self.timed_activity("Staging local files at {}".format(self.__workspace)):
- if os.path.isdir(fullpath):
- utils.copy_files(fullpath, directory)
- else:
- destfile = os.path.join(directory, os.path.basename(self.__workspace))
- utils.safe_copy(fullpath, destfile)
-
- # Get a unique key for the workspace
- #
- # Note that to avoid re-traversing the file system if this function is
- # called multiple times, the workspace key is cached. You can still force a
- # new calculation to happen by setting the 'recalculate' flag.
- #
- def _get_workspace_key(self, recalculate=False):
- assert not self.__assemble_scheduled
-
- if recalculate or self.__workspace_key is None:
- fullpath = self._get_workspace_path()
-
- # Get a list of tuples of the the project relative paths and fullpaths
- if os.path.isdir(fullpath):
- filelist = utils.list_relative_paths(fullpath)
- filelist = [(relpath, os.path.join(fullpath, relpath)) for relpath in filelist]
- else:
- filelist = [(self.__workspace, fullpath)]
-
- # Return a list of (relative filename, sha256 digest) tuples, a sorted list
- # has already been returned by list_relative_paths()
- self.__workspace_key = [(relpath, _unique_key(fullpath)) for relpath, fullpath in filelist]
-
- return self.__workspace_key
-
-
-# Get the sha256 sum for the content of a file
-def _unique_key(filename):
-
- # If it's a directory, just return 0 string
- if os.path.isdir(filename):
- return "0"
- elif os.path.islink(filename):
- return "1"
-
- try:
- return utils.sha256sum(filename)
- except FileNotFoundError as e:
- raise LoadError(LoadErrorReason.MISSING_FILE,
- "Failed loading workspace. Did you remove the workspace directory? {}".format(e))