xref: /aosp_15_r20/external/grpc-grpc/tools/buildgen/build_cleaner.py (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
1#!/usr/bin/env python3
2# Copyright 2015 gRPC authors.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#     http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16# produces cleaner build.yaml files
17
18import collections
19import os
20import sys
21
22import yaml
23
24TEST = os.environ.get("TEST", "false") == "true"
25
26_TOP_LEVEL_KEYS = [
27    "settings",
28    "proto_deps",
29    "filegroups",
30    "libs",
31    "targets",
32    "vspackages",
33]
34_ELEM_KEYS = [
35    "name",
36    "gtest",
37    "cpu_cost",
38    "flaky",
39    "build",
40    "run",
41    "language",
42    "plugin_option",
43    "public_headers",
44    "headers",
45    "src",
46    "deps",
47]
48
49
50def repr_ordered_dict(dumper, odict):
51    return dumper.represent_mapping(
52        "tag:yaml.org,2002:map", list(odict.items())
53    )
54
55
56yaml.add_representer(collections.OrderedDict, repr_ordered_dict)
57
58
59def _rebuild_as_ordered_dict(indict, special_keys):
60    outdict = collections.OrderedDict()
61    for key in sorted(indict.keys()):
62        if "#" in key:
63            outdict[key] = indict[key]
64    for key in special_keys:
65        if key in indict:
66            outdict[key] = indict[key]
67    for key in sorted(indict.keys()):
68        if key in special_keys:
69            continue
70        if "#" in key:
71            continue
72        outdict[key] = indict[key]
73    return outdict
74
75
76def _clean_elem(indict):
77    for name in ["public_headers", "headers", "src"]:
78        if name not in indict:
79            continue
80        inlist = indict[name]
81        protos = list(x for x in inlist if os.path.splitext(x)[1] == ".proto")
82        others = set(x for x in inlist if x not in protos)
83        indict[name] = protos + sorted(others)
84    return _rebuild_as_ordered_dict(indict, _ELEM_KEYS)
85
86
87def cleaned_build_yaml_dict_as_string(indict):
88    """Takes dictionary which represents yaml file and returns the cleaned-up yaml string"""
89    js = _rebuild_as_ordered_dict(indict, _TOP_LEVEL_KEYS)
90    for grp in ["filegroups", "libs", "targets"]:
91        if grp not in js:
92            continue
93        js[grp] = sorted(
94            [_clean_elem(x) for x in js[grp]],
95            key=lambda x: (x.get("language", "_"), x["name"]),
96        )
97    output = yaml.dump(js, indent=2, width=80, default_flow_style=False)
98    # massage out trailing whitespace
99    lines = []
100    for line in output.splitlines():
101        lines.append(line.rstrip() + "\n")
102    output = "".join(lines)
103    return output
104
105
106if __name__ == "__main__":
107    for filename in sys.argv[1:]:
108        with open(filename) as f:
109            js = yaml.safe_load(f)
110        output = cleaned_build_yaml_dict_as_string(js)
111        if TEST:
112            with open(filename) as f:
113                if not f.read() == output:
114                    raise Exception(
115                        "Looks like build-cleaner.py has not been run for file"
116                        ' "%s"?' % filename
117                    )
118        else:
119            with open(filename, "w") as f:
120                f.write(output)
121