[analyzer] SATestBuild.py: Refactor and add type annotations

Summary:
SATest scripts should be more python-style than they are now.
This includes better architecture, type annotations, naming
convesions, and up-to-date language features.  This commit starts
with two scripts SATestBuild and SATestAdd.

Differential Revision: https://reviews.llvm.org/D80423
diff --git a/clang/utils/analyzer/SATestAdd.py b/clang/utils/analyzer/SATestAdd.py
index e0e267b..012d8ec 100755
--- a/clang/utils/analyzer/SATestAdd.py
+++ b/clang/utils/analyzer/SATestAdd.py
@@ -42,75 +42,80 @@
                                           diff -ur CachedSource PatchedSource \
                                               > changes_for_analyzer.patch
 """
-from __future__ import absolute_import, division, print_function
 import SATestBuild
 
-import os
 import csv
+import os
 import sys
 
-
-def isExistingProject(PMapFile, projectID):
-    PMapReader = csv.reader(PMapFile)
-    for ProjectInfo in PMapReader:
-        if projectID == ProjectInfo[0]:
-            return True
-    return False
+from typing import IO
 
 
-def addNewProject(ID, BuildMode):
+def add_new_project(name: str, build_mode: int):
     """
     Add a new project for testing: build it and add to the Project Map file.
-    :param ID: is a short string used to identify a project.
+    :param name: is a short string used to identify a project.
     """
 
-    CurDir = os.path.abspath(os.curdir)
-    Dir = SATestBuild.getProjectDir(ID)
-    if not os.path.exists(Dir):
-        print("Error: Project directory is missing: %s" % Dir)
+    project_info = SATestBuild.ProjectInfo(name, build_mode,
+                                           is_reference_build=True)
+    tester = SATestBuild.ProjectTester(project_info)
+
+    project_dir = tester.get_project_dir()
+    if not os.path.exists(project_dir):
+        print(f"Error: Project directory is missing: {project_dir}")
         sys.exit(-1)
 
     # Build the project.
-    # TODO: Repair this call.  We give it a wrong amount wrong arguments and it
-    #       is not trivial to construct argparse arguments in here.
-    #       Requires refactoring of the 'testProject' function.
-    SATestBuild.testProject(ID, BuildMode, IsReferenceBuild=True)
+    tester.test()
 
-    # Add the project ID to the project map.
-    ProjectMapPath = os.path.join(CurDir, SATestBuild.ProjectMapFile)
+    # Add the project name to the project map.
+    project_map_path = SATestBuild.get_project_map_path(should_exist=False)
 
-    if os.path.exists(ProjectMapPath):
-        FileMode = "r+"
+    if os.path.exists(project_map_path):
+        file_mode = "r+"
     else:
-        print("Warning: Creating the Project Map file!!")
-        FileMode = "w+"
+        print("Warning: Creating the project map file!")
+        file_mode = "w+"
 
-    with open(ProjectMapPath, FileMode) as PMapFile:
-        if (isExistingProject(PMapFile, ID)):
-            print('Warning: Project with ID \'', ID,
-                  '\' already exists.', file=sys.stdout)
+    with open(project_map_path, file_mode) as map_file:
+        if is_existing_project(map_file, name):
+            print(f"Warning: Project with name '{name}' already exists.",
+                  file=sys.stdout)
             print("Reference output has been regenerated.", file=sys.stdout)
         else:
-            PMapWriter = csv.writer(PMapFile)
-            PMapWriter.writerow((ID, int(BuildMode)))
-            print("The project map is updated: ", ProjectMapPath)
+            map_writer = csv.writer(map_file)
+            map_writer.writerow((name, build_mode))
+            print(f"The project map is updated: {project_map_path}")
 
 
+def is_existing_project(map_file: IO, project_name: str) -> bool:
+    map_reader = csv.reader(map_file)
+
+    for raw_info in map_reader:
+        if project_name == raw_info[0]:
+            return True
+
+    return False
+
+
+# TODO: Use argparse
 # TODO: Add an option not to build.
 # TODO: Set the path to the Repository directory.
-if __name__ == '__main__':
-    if len(sys.argv) < 2 or sys.argv[1] in ('-h', '--help'):
-        print('Add a new project for testing to the analyzer'
-              '\nUsage: ', sys.argv[0],
-              'project_ID <mode>\n'
-              'mode: 0 for single file project, '
-              '1 for scan_build, '
-              '2 for single file c++11 project', file=sys.stderr)
+if __name__ == "__main__":
+    if len(sys.argv) < 2 or sys.argv[1] in ("-h", "--help"):
+        print("Add a new project for testing to the analyzer"
+              "\nUsage: ", sys.argv[0],
+              "project_ID <mode>\n"
+              "mode: 0 for single file project, "
+              "1 for scan_build, "
+              "2 for single file c++11 project", file=sys.stderr)
         sys.exit(-1)
 
-    BuildMode = 1
-    if (len(sys.argv) >= 3):
-        BuildMode = int(sys.argv[2])
-    assert((BuildMode == 0) | (BuildMode == 1) | (BuildMode == 2))
+    build_mode = 1
+    if len(sys.argv) >= 3:
+        build_mode = int(sys.argv[2])
 
-    addNewProject(sys.argv[1], BuildMode)
+    assert((build_mode == 0) | (build_mode == 1) | (build_mode == 2))
+
+    add_new_project(sys.argv[1], build_mode)