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(prefix, 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 protobuf_headers = prefix_paths('src', protobuf_headers) 93 protobuf_lite_sources = prefix_paths('src', protobuf_lite_sources) 94 protobuf_sources = prefix_paths('src', protobuf_sources) 95 protoc_sources = prefix_paths('src', protoc_sources) 96 protoc_headers = prefix_paths('src', protoc_headers) 97 98 # Not upstream protobuf, added via Chromium patch. 99 protobuf_lite_sources.append("src/google/protobuf/arenastring.cc") 100 101 with open(MAKEFILE) as f: 102 vars = read_makefile_variables(f) 103 all_python_sources = [ 104 p for p in vars['python_EXTRA_DIST'] 105 if p.endswith('.py') and 'test' not in p 106 ] 107 # The copy rules in BUILD.gn can only handle files in the same directory, so 108 # the list must be split into per-directory lists. 109 pyproto_sources = [ 110 p for p in all_python_sources 111 if os.path.dirname(p) == 'python/google/protobuf' 112 ] 113 pyproto_internal_sources = [ 114 p for p in all_python_sources 115 if os.path.dirname(p) == 'python/google/protobuf/internal' 116 ] 117 118 with open(PROTO_SOURCES_GNI, 'w') as f: 119 f.write('''# Copyright 2022 The Chromium Authors 120# Use of this source code is governed by a BSD-style license that can be 121# found in the LICENSE file. 122 123# Generated by gen_chromium_file_lists.py. Do not edit by hand. 124''') 125 write_gn_variable(f, 'protobuf_headers', protobuf_headers) 126 write_gn_variable(f, 'protobuf_lite_sources', protobuf_lite_sources) 127 write_gn_variable(f, 'protobuf_sources', protobuf_sources) 128 write_gn_variable(f, 'protoc_sources', protoc_sources) 129 write_gn_variable(f, 'protoc_headers', protoc_headers) 130 write_gn_variable(f, 'pyproto_sources', pyproto_sources) 131 write_gn_variable(f, 'pyproto_internal_sources', 132 pyproto_internal_sources) 133 134 135if __name__ == '__main__': 136 main() 137