1# Copyright (C) 2023 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#      http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14import argparse
15import os
16from enum import Enum
17from collections import defaultdict
18
19class Severity(Enum):
20  ENABLED_ERRORS=0
21  ENABLED_WARNINGS=1
22  DISABLED_CHECKS=2
23
24  def to_flag_string(self):
25    if self == Severity.ENABLED_ERRORS:
26      return "ERROR"
27    elif self == Severity.ENABLED_WARNINGS:
28      return "WARN"
29    else:
30      return "OFF"
31
32def extract_error_prone_default_checks_by_severity(filename):
33  """
34  Args:
35    filename: filename containing the default checks in a version of Errorprone
36
37  Returns:
38    default_checks: a dictionary representing the Default Errorprone checks, a
39    check_name maps to a severity.
40  """
41  default_checks = {}
42
43  with open(filename) as f:
44    lines = f.readlines()
45
46  current_severity = ""
47  for line in lines:
48    line = line[:-1]
49    if line in [severity.name for severity in Severity]:
50      current_severity = Severity[line]
51    else:
52      default_checks[line] = current_severity
53  return default_checks
54
55def get_bazel_compatibility_checks(soong_file, bazel_file):
56  """
57  Args:
58    soong_file: name of file containing the default checks in the Soong version
59      of Errorprone
60    bazel_file: name of  file containing the default checks in the Bazel version
61      of Errorprone
62
63
64  Returns:
65    a dictionary representing the checks that need to be modified in
66    Bazel in order for its checks to match Soong's default checks.
67    check_name maps to a severity.
68  """
69  soong_defaults = extract_error_prone_default_checks_by_severity(soong_file)
70  bazel_defaults= extract_error_prone_default_checks_by_severity(bazel_file)
71
72  return {
73      check:Severity.DISABLED_CHECKS
74      for check in bazel_defaults
75      if check not in soong_defaults and bazel_defaults[check] != Severity.DISABLED_CHECKS
76  }|{
77      check:soong_severity
78      for check, soong_severity in soong_defaults.items()
79      if check not in bazel_defaults or soong_severity != bazel_defaults[check]
80  }
81
82
83def check_to_flag_string(check_name, severity):
84  return "-Xep:" + check_name + ":" + severity.to_flag_string()
85
86def checks_to_flags(compatibility_checks):
87  """
88  iterates over items in compatibility_checks dic and returns a dic of the flags
89  using the -Xep:<checkName>[:severity] format
90
91  Args:
92    compatibility_checks: output from checks_dic.
93
94  Returns:
95    severity_to_flag: Dic mapping severities to the command-line flag
96
97  """
98  severity_to_flag = defaultdict(list)
99
100  for check_name, severity in compatibility_checks.items():
101    severity_to_flag[severity].append(check_to_flag_string(check_name, severity))
102
103  for severity in severity_to_flag:
104    severity_to_flag[severity].sort()
105
106  return severity_to_flag
107
108
109def license_header():
110  return "# Copyright (C) 2023 The Android Open Source Project\n" \
111         "#\n" \
112         "# Licensed under the Apache License, Version 2.0 (the \"License\");\n" \
113         "# you may not use this file except in compliance with the License.\n" \
114         "# You may obtain a copy of the License at\n" \
115         "#\n" \
116         "#      http://www.apache.org/licenses/LICENSE-2.0\n" \
117         "#\n" \
118         "# Unless required by applicable law or agreed to in writing, software\n" \
119         "# distributed under the License is distributed on an \"AS IS\" BASIS,\n" \
120         "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" \
121         "# See the License for the specific language governing permissions and\n" \
122         "# limitations under the License.\n\n" \
123
124def output_as_bzl_file(flags):
125  filename =  os.path.dirname(os.path.abspath(__file__)) + "/errorprone_flags.bzl"
126
127  with open(filename, 'w') as f:
128    f.write(license_header())
129    f.write("#  DO NOT MODIFY: This file is auto-generated by errorProneCompatibilityFlags.sh\n")
130    f.write("errorprone_soong_bazel_diffs = [\n")
131    for severity in Severity:
132      f.write("    # Errorprone {severity}\n".format(severity=severity.name))
133      for flag in flags[severity]:
134        f.write("    \"{flag}\",\n".format(flag=flag))
135
136    f.write("]\n")
137
138def main():
139  parser = argparse.ArgumentParser()
140  parser.add_argument("--soong_file", required=True,
141                      help="file containing the default checks in the "
142                           "Soong version of Error Prone")
143  parser.add_argument("--bazel_file",required=True,
144                      help="file containing the default checks in the"
145                           "Bazel version of the Error Prone")
146  args = parser.parse_args()
147
148  compatibility_checks = get_bazel_compatibility_checks(args.soong_file, args.bazel_file)
149  compatibility_flags = checks_to_flags(compatibility_checks)
150
151  output_as_bzl_file(compatibility_flags)
152
153if __name__ == '__main__':
154  main()