You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@buildstream.apache.org by no...@apache.org on 2020/12/29 12:43:38 UTC
[buildstream] 02/04: Implement basic handling of multiple projects
to a single workspace
This is an automated email from the ASF dual-hosted git repository.
not-in-ldap pushed a commit to branch jonathan/workspace-fragment-multi-project
in repository https://gitbox.apache.org/repos/asf/buildstream.git
commit 965da28f342944181b60a3dce72e76cfd153bffe
Author: Jonathan Maw <jo...@codethink.co.uk>
AuthorDate: Wed Dec 12 15:19:23 2018 +0000
Implement basic handling of multiple projects to a single workspace
This change is related to #222
---
buildstream/_stream.py | 14 ++++--
buildstream/_workspaces.py | 115 +++++++++++++++++++++++++++++++++++----------
2 files changed, 100 insertions(+), 29 deletions(-)
diff --git a/buildstream/_stream.py b/buildstream/_stream.py
index caaa489..e107a30 100644
--- a/buildstream/_stream.py
+++ b/buildstream/_stream.py
@@ -656,6 +656,9 @@ class Stream():
self._message(MessageType.INFO, "Creating workspace for element {}"
.format(target.name))
+ # Ensure the WorkspaceProject is loaded before we delete it
+ workspaces.get_workspace_project(directory)
+
workspace = workspaces.get_workspace(target._get_full_name())
if workspace:
workspaces.delete_workspace(target._get_full_name())
@@ -670,7 +673,7 @@ class Stream():
todo_elements = "\nDid not try to create workspaces for " + todo_elements
raise StreamError("Failed to create workspace directory: {}".format(e) + todo_elements) from e
- workspaces.create_workspace(target, directory, checkout=not no_checkout)
+ workspaces.create_workspace(target, directory, checkout=not no_checkout, append=force)
self._message(MessageType.INFO, "Created a workspace for element: {}"
.format(target._get_full_name()))
@@ -744,6 +747,9 @@ class Stream():
.format(element.name, workspace_path))
continue
+ # Ensure the WorkspaceProject is in the cache before it gets deleted
+ workspaces.get_workspace_project(workspace_path)
+
with element.timed_activity("Removing workspace directory {}"
.format(workspace_path)):
try:
@@ -752,8 +758,10 @@ class Stream():
raise StreamError("Could not remove '{}': {}"
.format(workspace_path, e)) from e
- workspaces.delete_workspace(element._get_full_name())
- workspaces.create_workspace(element, workspace_path, checkout=True)
+ workspaces.delete_workspace(element._get_full_name(),
+ preserve_workspace_project=True)
+ workspaces.create_workspace(element, workspace_path, checkout=True,
+ append=False, preserve_workspace_project=True)
self._message(MessageType.INFO,
"Reset workspace for {} at: {}".format(element.name,
diff --git a/buildstream/_workspaces.py b/buildstream/_workspaces.py
index 24a3cc8..0eac81e 100644
--- a/buildstream/_workspaces.py
+++ b/buildstream/_workspaces.py
@@ -143,8 +143,38 @@ class WorkspaceProject():
# element_name (str): The name of the element that the workspace belongs to.
#
def add_project(self, project_path, element_name):
- assert (project_path and element_name)
- self._projects.append({'project-path': project_path, 'element-name': element_name})
+ # TODO: Decide whether to raise an exception if the project already exists.
+ project = {'project-path': project_path, 'element-name': element_name}
+ if project not in self._projects:
+ self._projects.append(project)
+
+ # set_project()
+ #
+ # Sets the project to only contain the project_path and element_name specified.
+ #
+ # Args:
+ # project_path (str): the path to the project that opened the workspace.
+ # element_name (str): the name of the element that the workspace belongs to.
+ #
+ def set_project(self, project_path, element_name):
+ self._projects = [{'project-path': project_path, 'element-name': element_name}]
+
+ # remove_project()
+ #
+ # Removes the first project entry that matches the project_path and element_name
+ def remove_project(self, project_path, element_name):
+ # NOTE: This will need revisiting if projects' data format changes
+ # TODO: Figure out what to do if there is no project for those parameters
+ self._projects.remove({'project-path': project_path, 'element-name': element_name})
+
+ # has_projects()
+ #
+ # Returns whether there are any projects in this WorkspaceProject
+ #
+ # Returns:
+ # (bool): True if there are any projects, or False if there aren't any
+ def has_projects(self):
+ return any(self._projects)
# WorkspaceProjectCache()
@@ -186,17 +216,21 @@ class WorkspaceProjectCache():
# directory (str): The directory to search for a WorkspaceProject.
# project_path (str): The path to the project that refers to this workspace
# element_name (str): The element in the project that was refers to this workspace
+ # append (bool): Whether the project_path and element_name should be appended
#
# Returns:
# (WorkspaceProject): The WorkspaceProject that was found for that directory.
#
- def add(self, directory, project_path, element_name):
+ def add(self, directory, project_path, element_name, *, append):
workspace_project = self.get(directory)
if not workspace_project:
workspace_project = WorkspaceProject(directory)
self._projects[directory] = workspace_project
- workspace_project.add_project(project_path, element_name)
+ if append:
+ workspace_project.add_project(project_path, element_name)
+ else:
+ workspace_project.set_project(project_path, element_name)
return workspace_project
# remove()
@@ -204,23 +238,28 @@ class WorkspaceProjectCache():
# Removes the project path and element name from the WorkspaceProject that exists
# for that directory.
#
- # NOTE: This currently just deletes the file, but with support for multiple
- # projects opening the same workspace, this will involve decreasing the count
- # and deleting the file if there are no more projects.
- #
# Args:
# directory (str): The directory to search for a WorkspaceProject.
+ # project_path (str): the path to the project that should be removed.
+ # element_name (str): the name of the element in the project that should be removed.
#
- def remove(self, directory):
+ def remove(self, directory, project_path, element_name):
workspace_project = self.get(directory)
if not workspace_project:
raise LoadError(LoadErrorReason.MISSING_FILE,
"Failed to find a {} file to remove".format(WORKSPACE_PROJECT_FILE))
- path = workspace_project.get_filename()
- try:
- os.unlink(path)
- except FileNotFoundError:
- pass
+
+ workspace_project.remove_project(project_path, element_name)
+
+ if workspace_project.has_projects():
+ workspace_project.write()
+ else:
+ # Remove the WorkspaceProject file if it's now empty
+ path = workspace_project.get_filename()
+ try:
+ os.unlink(path)
+ except FileNotFoundError:
+ pass
# Workspace()
@@ -429,8 +468,10 @@ class Workspaces():
# target (Element) - The element to create a workspace for
# path (str) - The path in which the workspace should be kept
# checkout (bool): Whether to check-out the element's sources into the directory
+ # append (bool): Whether the WorkspaceProject file should append this project
+ # preserve_workspace_project (bool): Whether the WorkspaceProject should be altered
#
- def create_workspace(self, target, path, *, checkout):
+ def create_workspace(self, target, path, *, checkout, append, preserve_workspace_project=False):
element_name = target._get_full_name()
project_dir = self._toplevel_project.directory
if path.startswith(project_dir):
@@ -444,7 +485,11 @@ class Workspaces():
with target.timed_activity("Staging sources to {}".format(path)):
target._open_workspace()
- workspace_project = self._workspace_project_cache.add(path, project_dir, element_name)
+ if preserve_workspace_project:
+ workspace_project = self._workspace_project_cache.get(path)
+ else:
+ workspace_project = self._workspace_project_cache.add(path, project_dir, element_name, append=append)
+
project_file_path = workspace_project.get_filename()
if os.path.exists(project_file_path):
@@ -469,6 +514,21 @@ class Workspaces():
return None
return self._workspaces[element_name]
+ # get_workspace_project()
+ #
+ # Returns a WorkspaceProject for a given directory, retrieving from the cache if
+ # present.
+ #
+ # Args:
+ # directory (str): The directory to search for a WorkspaceProject.
+ #
+ # Returns:
+ # (WorkspaceProject): The WorkspaceProject that was found for that directory.
+ # or (NoneType): None, if no WorkspaceProject can be found.
+ #
+ def get_workspace_project(self, directory):
+ return self._workspace_project_cache.get(directory)
+
# update_workspace()
#
# Update the datamodel with a new Workspace instance
@@ -498,20 +558,23 @@ class Workspaces():
#
# Args:
# element_name (str) - The element name whose workspace to delete
+ # preserve_workspace_project (bool): Whether the WorkspaceProject should be altered
#
- def delete_workspace(self, element_name):
+ def delete_workspace(self, element_name, preserve_workspace_project=False):
workspace = self.get_workspace(element_name)
del self._workspaces[element_name]
- # Remove from the cache if it exists
- try:
- self._workspace_project_cache.remove(workspace.get_absolute_path())
- except LoadError as e:
- # We might be closing a workspace with a deleted directory
- if e.reason == LoadErrorReason.MISSING_FILE:
- pass
- else:
- raise
+ if not preserve_workspace_project:
+ # Remove from the cache if it exists
+ project_dir = self._toplevel_project.directory
+ try:
+ self._workspace_project_cache.remove(workspace.get_absolute_path(), project_dir, element_name)
+ except LoadError as e:
+ # We might be closing a workspace with a deleted directory
+ if e.reason == LoadErrorReason.MISSING_FILE:
+ pass
+ else:
+ raise
# save_config()
#