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