xref: /llvm-project/clang/utils/analyzer/ProjectMap.py (revision fb4b565212b0158e2b41ffec71a7c4282907cda9)
1import json
2import os
3
4from typing import Any, Dict, List, NamedTuple, Optional
5
6
7JSON = Dict[str, Any]
8
9
10DEFAULT_MAP_FILE = "projects.json"
11
12
13class ProjectInfo(NamedTuple):
14    """
15    Information about a project to analyze.
16    """
17    name: str
18    mode: int
19    enabled: bool = True
20
21
22class ProjectMap:
23    """
24    Project map stores info about all the "registered" projects.
25    """
26    def __init__(self, path: Optional[str] = None, should_exist: bool = True):
27        """
28        :param path: optional path to a project JSON file, when None defaults
29                     to DEFAULT_MAP_FILE.
30        :param should_exist: flag to tell if it's an exceptional situation when
31                             the project file doesn't exist, creates an empty
32                             project list instead if we are not expecting it to
33                             exist.
34        """
35        if path is None:
36            path = os.path.join(os.path.abspath(os.curdir), DEFAULT_MAP_FILE)
37
38        if not os.path.exists(path):
39            if should_exist:
40                raise ValueError(
41                    f"Cannot find the project map file {path}"
42                    f"\nRunning script for the wrong directory?\n")
43            else:
44                self._create_empty(path)
45
46        self.path = path
47        self._load_projects()
48
49    def save(self):
50        """
51        Save project map back to its original file.
52        """
53        self._save(self.projects, self.path)
54
55    def _load_projects(self):
56        with open(self.path) as raw_data:
57            raw_projects = json.load(raw_data)
58
59            if not isinstance(raw_projects, list):
60                raise ValueError(
61                    "Project map should be a list of JSON objects")
62
63            self.projects = self._parse(raw_projects)
64
65    @staticmethod
66    def _parse(raw_projects: List[JSON]) -> List[ProjectInfo]:
67        return [ProjectMap._parse_project(raw_project)
68                for raw_project in raw_projects]
69
70    @staticmethod
71    def _parse_project(raw_project: JSON) -> ProjectInfo:
72        try:
73            name: str = raw_project["name"]
74            build_mode: int = raw_project["mode"]
75            enabled: bool = raw_project.get("enabled", True)
76            return ProjectInfo(name, build_mode, enabled)
77
78        except KeyError as e:
79            raise ValueError(
80                f"Project info is required to have a '{e.args[0]}' field")
81
82    @staticmethod
83    def _create_empty(path: str):
84        ProjectMap._save([], path)
85
86    @staticmethod
87    def _save(projects: List[ProjectInfo], path: str):
88        with open(path, "w") as output:
89            json.dump(ProjectMap._convert_infos_to_dicts(projects),
90                      output, indent=2)
91
92    @staticmethod
93    def _convert_infos_to_dicts(projects: List[ProjectInfo]) -> List[JSON]:
94        return [project._asdict() for project in projects]
95