bsp_server/scip_sync_util/workspace.py (119 lines of code) (raw):

import os from dataclasses import asdict, dataclass, field from enum import Enum from typing import Union from bsp_server.scip_sync_util.mnemonics import ScipMnemonics from bsp_server.util import utils WORKSPACE_FILE_NAME = "workspace.json" class WorkspaceLinkType(Enum): JAVA_MANIFEST = "1" BAZEL_TARGET = "2" @dataclass class ScipWorkspace: # Single file could be related to multiple artifacts. To avoid duplication # we will keep only a reference to that artifact. # We will refer to it as "link". files: dict[str, dict[str, str]] = field(default_factory=dict) # This will maintain unique entries for each artifact. links: dict[str, dict[str, str]] = field(default_factory=dict) # This will be used to generate unique link id _last_link_id: int = 0 def add_file(self, file: str, link_id: int, link_type: WorkspaceLinkType) -> None: if file not in self.files: self.files[file] = {} self.files[file].update({link_type.name: str(link_id)}) def get_file(self, file: str): return self.files.get(file, None) def add_link(self, link: str, link_type: WorkspaceLinkType): self._last_link_id += 1 # use str since parsing json will convert int to string id = str(self._last_link_id) type_links = self.links.get(link_type.name, {}) type_links.update({id: link}) self.links[link_type.name] = type_links return id def get_link(self, link_id: str, link_type: WorkspaceLinkType): return self.links.get(link_type.name, {}).get(link_id, None) def clear(self): self.files = {} self.links = {} self._last_link_id = 0 def workspace_to_dictionary( workspace: ScipWorkspace, ) -> dict[str, dict[str, dict[str, str]]]: workspace_dict = {} for file in workspace.files: target = None manifest = None for link_type, link_id in workspace.files[file].items(): if link_type == WorkspaceLinkType.BAZEL_TARGET.name: target = workspace.get_link(link_id, WorkspaceLinkType.BAZEL_TARGET) if link_type == WorkspaceLinkType.JAVA_MANIFEST.name: manifest = workspace.get_link(link_id, WorkspaceLinkType.JAVA_MANIFEST) if target: workspace_dict.setdefault(target, {}).setdefault( ScipMnemonics.UNPACKED_JAVA_SOURCES_MNEMONIC.value, set() ).add(file) if manifest: workspace_dict.setdefault(target, {}).setdefault( ScipMnemonics.JAVA_TARGET_MANIFEST_MNEMONIC.value, manifest ) return workspace_dict def create_workspace(cwd: str) -> ScipWorkspace: return ScipWorkspace() def populate_workspace( cwd: str, target_to_output: dict[str, dict[str, list[str]]] ) -> ScipWorkspace: workspace = create_workspace(cwd) for target, target_mnemonics in target_to_output.items(): # Skip 3rd party targets for workspace if target.startswith("//3rdparty/") or target.startswith("bazel-out"): continue add_to_workspace(cwd, workspace, target, target_mnemonics) return workspace def add_to_workspace( cwd: str, workspace: ScipWorkspace, target: str, target_mnemonics: dict[str, list[str]], ) -> None: manifests = target_mnemonics.get( ScipMnemonics.JAVA_TARGET_MANIFEST_MNEMONIC.value, [None] ) # Filter the list to include only files that end with "_options" # bin and test bins are getting picked by mnemonic query, this should work for now manifests = [file for file in manifests if file and file.endswith("_options")] sources_lists = target_mnemonics.get( ScipMnemonics.UNPACKED_JAVA_SOURCES_MNEMONIC.value, [None] ) for index, manifest in enumerate(manifests): # for given target we can have only 1 manifest and 1 sources list if index >= len(sources_lists): # safeguard, should not happen continue sources_list = sources_lists[index] if sources_list is None: continue if os.path.exists(os.path.join(cwd, sources_list)): lines = utils.get_string_lines(os.path.join(cwd, sources_list)) add_files_for_target( workspace=workspace, target=target, files=lines, manifest=manifest, ) def add_files_for_target( workspace: ScipWorkspace, target: str, files: list[str], manifest: str ) -> None: target_id = workspace.add_link(target, WorkspaceLinkType.BAZEL_TARGET) manifest_id = workspace.add_link(manifest, WorkspaceLinkType.JAVA_MANIFEST) for file in files: workspace.add_file(file, target_id, WorkspaceLinkType.BAZEL_TARGET) workspace.add_file(file, manifest_id, WorkspaceLinkType.JAVA_MANIFEST) def write_workspace(workspace: ScipWorkspace, dest: str) -> None: utils.safe_create(dest, is_dir=True) workspace_dict = asdict(workspace) utils.write_json( workspace_dict, os.path.join(dest, WORKSPACE_FILE_NAME), default_serializer=utils.set_to_list, pretty=True, ) def get_manifest_for_file(file: str, dest: str) -> Union[str, None]: if os.path.exists(os.path.join(dest, WORKSPACE_FILE_NAME)): json_obj = utils.get_json(os.path.join(dest, WORKSPACE_FILE_NAME)) workspace = ScipWorkspace(**json_obj) # update workspace with new data file_links = workspace.get_file(file) if file_links: manifest_link = file_links.get(WorkspaceLinkType.JAVA_MANIFEST.name, None) # we are expecting only 1 manifest for a file return workspace.get_link(manifest_link, WorkspaceLinkType.JAVA_MANIFEST) return None