xref: /aosp_15_r20/external/cronet/third_party/protobuf/gen_chromium_file_lists.py (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1#!/usr/bin/env python3
2# Copyright 2022 The Chromium Authors
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5"""Generates GN build files for protobuf. See update_file_lists.sh from
6protobuf."""
7
8import os.path
9
10PROTO_DIR = os.path.dirname(__file__)
11MAKEFILE = os.path.join(PROTO_DIR, 'Makefile.am')
12SRC_MAKEFILE = os.path.join(PROTO_DIR, 'src', 'Makefile.am')
13PROTO_SOURCES_GNI = os.path.join(PROTO_DIR, 'proto_sources.gni')
14
15
16def read_makefile_lines(f):
17    # Roughly implement https://www.gnu.org/software/make/manual/html_node/Splitting-Lines.html
18    # but skip condensing whitespace. That can be handled by the reader. Also
19    # make no distinction between recipe and non-recipe lines.
20    cont = []
21    while True:
22        line = f.readline()
23        if line == '':
24            if len(cont) != 0:
25                raise ValueError('Continuation at end of file')
26            break
27        line = line.rstrip('\n')
28        if line.endswith('\\'):
29            cont.append(line[:-1])
30        else:
31            cont.append(line)
32            yield ' '.join(cont)
33            cont = []
34
35
36def read_makefile_variables(f):
37    ret = {}
38    for line in read_makefile_lines(f):
39        if not line or line[0].isspace():
40            continue
41        # Sometimes there aren't spaces surrounding equals.
42        line = line.replace('=', ' = ')
43        # Do a very rough parse.
44        tokens = line.split()
45        if len(tokens) >= 2 and tokens[1] == '=':
46            value = []
47            for token in tokens[2:]:
48                if token.startswith('$(') and token.endswith(')'):
49                    value.extend(ret.get(token[2:-1], []))
50                else:
51                    value.append(token)
52            ret[tokens[0]] = value
53    return ret
54
55
56def is_protoc_header(path):
57    if '/compiler/' not in path:
58        return False
59    # compiler/importer.h and compiler/parser.h should be part of libprotobuf
60    # itself.
61    return not path.endswith("/importer.h") and not path.endswith("/parser.h")
62
63
64def prefix_paths(paths):
65    return [f'src/{p}' for p in paths]
66
67
68def write_gn_variable(f, name, value):
69    f.write(f'\n{name} = [\n')
70    # Sort and deduplicate the file lists. Protobuf has some duplicate entries.
71    for path in sorted(set(value)):
72        f.write(f'  "{path}",\n')
73    f.write(']\n')
74
75
76def main():
77    with open(SRC_MAKEFILE) as f:
78        vars = read_makefile_variables(f)
79    protobuf_headers = [
80        p for p in vars['nobase_include_HEADERS'] if not is_protoc_header(p)
81    ]
82    protobuf_lite_sources = vars['libprotobuf_lite_la_SOURCES']
83    protobuf_sources = [
84        p for p in vars['libprotobuf_la_SOURCES']
85        if p not in protobuf_lite_sources
86    ]
87    protoc_sources = vars['libprotoc_la_SOURCES']
88    protoc_headers = [
89        p for p in vars['nobase_include_HEADERS'] if is_protoc_header(p)
90    ]
91
92    protoc_java_sources = [p for p in protoc_sources if 'compiler/java' in p]
93    protoc_java_headers = [p for p in protoc_headers if 'compiler/java' in p]
94
95    protoc_python_sources = [p for p in protoc_sources if 'compiler/python' in p]
96    protoc_python_headers = [p for p in protoc_headers if 'compiler/python' in p]
97
98    protoc_sources =[p for p in protoc_sources if not p in protoc_java_sources]
99    protoc_headers =[p for p in protoc_headers if not p in protoc_java_headers]
100
101    protoc_sources =[p for p in protoc_sources if not p in protoc_python_sources]
102    protoc_headers =[p for p in protoc_headers if not p in protoc_python_headers]
103
104    protobuf_headers = prefix_paths(protobuf_headers)
105    protobuf_lite_sources = prefix_paths(protobuf_lite_sources)
106    protobuf_sources = prefix_paths(protobuf_sources)
107    protoc_sources = prefix_paths(protoc_sources)
108    protoc_headers = prefix_paths(protoc_headers)
109    protoc_java_sources = prefix_paths(protoc_java_sources)
110    protoc_java_headers = prefix_paths(protoc_java_headers)
111    protoc_python_sources = prefix_paths(protoc_python_sources)
112    protoc_python_headers = prefix_paths(protoc_python_headers)
113
114    # Not upstream protobuf, added via Chromium patch.
115    protobuf_lite_sources.append("src/google/protobuf/arenastring.cc")
116
117    with open(MAKEFILE) as f:
118        vars = read_makefile_variables(f)
119    all_python_sources = [
120        p for p in vars['python_EXTRA_DIST']
121        if p.endswith('.py') and 'test' not in p
122    ]
123    # The copy rules in BUILD.gn can only handle files in the same directory, so
124    # the list must be split into per-directory lists.
125    pyproto_sources = [
126        p for p in all_python_sources
127        if os.path.dirname(p) == 'python/google/protobuf'
128    ]
129    pyproto_internal_sources = [
130        p for p in all_python_sources
131        if os.path.dirname(p) == 'python/google/protobuf/internal'
132    ]
133
134    with open(PROTO_SOURCES_GNI, 'w') as f:
135        f.write('''# Copyright 2022 The Chromium Authors
136# Use of this source code is governed by a BSD-style license that can be
137# found in the LICENSE file.
138
139# Generated by gen_chromium_file_lists.py. Do not edit by hand.
140''')
141        write_gn_variable(f, 'protobuf_headers', protobuf_headers)
142        write_gn_variable(f, 'protobuf_lite_sources', protobuf_lite_sources)
143        write_gn_variable(f, 'protobuf_sources', protobuf_sources)
144        write_gn_variable(f, 'protoc_sources', protoc_sources)
145        write_gn_variable(f, 'protoc_headers', protoc_headers)
146        write_gn_variable(f, 'protoc_java_sources', protoc_java_sources)
147        write_gn_variable(f, 'protoc_java_headers', protoc_java_headers)
148        write_gn_variable(f, 'protoc_python_sources', protoc_python_sources)
149        write_gn_variable(f, 'protoc_python_headers', protoc_python_headers)
150        write_gn_variable(f, 'pyproto_sources', pyproto_sources)
151        write_gn_variable(f, 'pyproto_internal_sources',
152                          pyproto_internal_sources)
153
154
155if __name__ == '__main__':
156    main()
157