forked from OSchip/llvm-project
149 lines
4.5 KiB
Python
149 lines
4.5 KiB
Python
import json
|
|
import os
|
|
|
|
from enum import Enum
|
|
from typing import Any, Dict, List, NamedTuple, Optional, Tuple
|
|
|
|
|
|
JSON = Dict[str, Any]
|
|
|
|
|
|
DEFAULT_MAP_FILE = "projects.json"
|
|
|
|
|
|
class DownloadType(str, Enum):
|
|
GIT = "git"
|
|
ZIP = "zip"
|
|
SCRIPT = "script"
|
|
|
|
|
|
class ProjectInfo(NamedTuple):
|
|
"""
|
|
Information about a project to analyze.
|
|
"""
|
|
name: str
|
|
mode: int
|
|
source: DownloadType = DownloadType.SCRIPT
|
|
origin: str = ""
|
|
commit: str = ""
|
|
enabled: bool = True
|
|
|
|
def with_fields(self, **kwargs) -> "ProjectInfo":
|
|
"""
|
|
Create a copy of this project info with customized fields.
|
|
NamedTuple is immutable and this is a way to create modified copies.
|
|
|
|
info.enabled = True
|
|
info.mode = 1
|
|
|
|
can be done as follows:
|
|
|
|
modified = info.with_fields(enbled=True, mode=1)
|
|
"""
|
|
return ProjectInfo(**{**self._asdict(), **kwargs})
|
|
|
|
|
|
class ProjectMap:
|
|
"""
|
|
Project map stores info about all the "registered" projects.
|
|
"""
|
|
def __init__(self, path: Optional[str] = None, should_exist: bool = True):
|
|
"""
|
|
:param path: optional path to a project JSON file, when None defaults
|
|
to DEFAULT_MAP_FILE.
|
|
:param should_exist: flag to tell if it's an exceptional situation when
|
|
the project file doesn't exist, creates an empty
|
|
project list instead if we are not expecting it to
|
|
exist.
|
|
"""
|
|
if path is None:
|
|
path = os.path.join(os.path.abspath(os.curdir), DEFAULT_MAP_FILE)
|
|
|
|
if not os.path.exists(path):
|
|
if should_exist:
|
|
raise ValueError(
|
|
f"Cannot find the project map file {path}"
|
|
f"\nRunning script for the wrong directory?\n")
|
|
else:
|
|
self._create_empty(path)
|
|
|
|
self.path = path
|
|
self._load_projects()
|
|
|
|
def save(self):
|
|
"""
|
|
Save project map back to its original file.
|
|
"""
|
|
self._save(self.projects, self.path)
|
|
|
|
def _load_projects(self):
|
|
with open(self.path) as raw_data:
|
|
raw_projects = json.load(raw_data)
|
|
|
|
if not isinstance(raw_projects, list):
|
|
raise ValueError(
|
|
"Project map should be a list of JSON objects")
|
|
|
|
self.projects = self._parse(raw_projects)
|
|
|
|
@staticmethod
|
|
def _parse(raw_projects: List[JSON]) -> List[ProjectInfo]:
|
|
return [ProjectMap._parse_project(raw_project)
|
|
for raw_project in raw_projects]
|
|
|
|
@staticmethod
|
|
def _parse_project(raw_project: JSON) -> ProjectInfo:
|
|
try:
|
|
name: str = raw_project["name"]
|
|
build_mode: int = raw_project["mode"]
|
|
enabled: bool = raw_project.get("enabled", True)
|
|
source: DownloadType = raw_project.get("source", "zip")
|
|
|
|
if source == DownloadType.GIT:
|
|
origin, commit = ProjectMap._get_git_params(raw_project)
|
|
else:
|
|
origin, commit = "", ""
|
|
|
|
return ProjectInfo(name, build_mode, source, origin, commit,
|
|
enabled)
|
|
|
|
except KeyError as e:
|
|
raise ValueError(
|
|
f"Project info is required to have a '{e.args[0]}' field")
|
|
|
|
@staticmethod
|
|
def _get_git_params(raw_project: JSON) -> Tuple[str, str]:
|
|
try:
|
|
return raw_project["origin"], raw_project["commit"]
|
|
except KeyError as e:
|
|
raise ValueError(
|
|
f"Profect info is required to have a '{e.args[0]}' field "
|
|
f"if it has a 'git' source")
|
|
|
|
@staticmethod
|
|
def _create_empty(path: str):
|
|
ProjectMap._save([], path)
|
|
|
|
@staticmethod
|
|
def _save(projects: List[ProjectInfo], path: str):
|
|
with open(path, "w") as output:
|
|
json.dump(ProjectMap._convert_infos_to_dicts(projects),
|
|
output, indent=2)
|
|
|
|
@staticmethod
|
|
def _convert_infos_to_dicts(projects: List[ProjectInfo]) -> List[JSON]:
|
|
return [ProjectMap._convert_info_to_dict(project)
|
|
for project in projects]
|
|
|
|
@staticmethod
|
|
def _convert_info_to_dict(project: ProjectInfo) -> JSON:
|
|
whole_dict = project._asdict()
|
|
defaults = project._field_defaults
|
|
|
|
# there is no need in serializing fields with default values
|
|
for field, default_value in defaults.items():
|
|
if whole_dict[field] == default_value:
|
|
del whole_dict[field]
|
|
|
|
return whole_dict
|