Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
protoc_wrapper.py
Go to the documentation of this file.
1#!/usr/bin/env python3.8
2# Copyright 2012 Google Inc. All Rights Reserved.
3
4"""
5A simple wrapper for protoc.
6Script for //third_party/protobuf/proto_library.gni .
7Features:
8- Inserts #include for extra header automatically.
9- Prevents bad proto names.
10"""
11
12from __future__ import print_function
13import argparse
14import os
15import os.path
16import re
17import subprocess
18import sys
19import tempfile
20
21PROTOC_INCLUDE_POINT = "// @@protoc_insertion_point(includes)"
22
23
25 if not options:
26 return ""
27 if options.endswith(":"):
28 return options
29 return options + ":"
30
31
32def VerifyProtoNames(protos):
33 for filename in protos:
34 if "-" in filename:
35 raise RuntimeError("Proto file names must not contain hyphens "
36 "(see http://crbug.com/386125 for more information).")
37
38
39def StripProtoExtension(filename):
40 if not filename.endswith(".proto"):
41 raise RuntimeError("Invalid proto filename extension: "
42 "{0} .".format(filename))
43 return filename.rsplit(".", 1)[0]
44
45
46def WriteIncludes(headers, include):
47 for filename in headers:
48 include_point_found = False
49 contents = []
50 with open(filename) as f:
51 for line in f:
52 stripped_line = line.strip()
53 contents.append(stripped_line)
54 if stripped_line == PROTOC_INCLUDE_POINT:
55 if include_point_found:
56 raise RuntimeError("Multiple include points found.")
57 include_point_found = True
58 extra_statement = "#include \"{0}\"".format(include)
59 contents.append(extra_statement)
60
61 if not include_point_found:
62 raise RuntimeError("Include point not found in header: "
63 "{0} .".format(filename))
64
65 with open(filename, "w") as f:
66 for line in contents:
67 print(line, file=f)
68
69
70def WriteDepfile(depfile, out_list, dep_list):
71 os.makedirs(os.path.dirname(depfile), exist_ok=True)
72 with open(depfile, 'w') as f:
73 print("{0}: {1}".format(" ".join(out_list), " ".join(dep_list)), file=f)
74
75
76def WritePluginDepfile(depfile, outputs, dependencies):
77 with open(outputs) as f:
78 outs = [line.strip() for line in f]
79 with open(dependencies) as f:
80 deps = [line.strip() for line in f]
81 WriteDepfile(depfile, outs, deps)
82
83
84def WriteProtocDepfile(depfile, outputs, deps_list):
85 with open(outputs) as f:
86 outs = [line.strip() for line in f]
87 WriteDepfile(depfile, outs, deps_list)
88
89
90def ExtractImports(proto, proto_dir, import_dirs):
91 filename = os.path.join(proto_dir, proto)
92 imports = set()
93
94 with open(filename) as f:
95 # Search the file for import.
96 for line in f:
97 match = re.match(r'^\s*import(?:\s+public)?\s+"([^"]+)"\s*;\s*$', line)
98 if match:
99 imported = match[1]
100
101 # Check import directories to find the imported file.
102 for candidate_dir in [proto_dir] + import_dirs:
103 candidate_path = os.path.join(candidate_dir, imported)
104 if os.path.exists(candidate_path):
105 imports.add(candidate_path)
106 # Transitively check imports.
107 imports.update(ExtractImports(imported, candidate_dir, import_dirs))
108 break
109
110 return imports
111
112
113def main(argv):
114 parser = argparse.ArgumentParser()
115 parser.add_argument("--protoc",
116 help="Relative path to compiler.")
117
118 parser.add_argument("--proto-in-dir",
119 help="Base directory with source protos.")
120 parser.add_argument("--cc-out-dir",
121 help="Output directory for standard C++ generator.")
122 parser.add_argument("--py-out-dir",
123 help="Output directory for standard Python generator.")
124 parser.add_argument("--plugin-out-dir",
125 help="Output directory for custom generator plugin.")
126
127 parser.add_argument("--depfile",
128 help="Output location for the protoc depfile.")
129 parser.add_argument("--depfile-outputs",
130 help="File containing a list of files to be generated.")
131
132 parser.add_argument("--plugin",
133 help="Relative path to custom generator plugin.")
134 parser.add_argument("--plugin-depfile",
135 help="Output location for the plugin depfile.")
136 parser.add_argument("--plugin-depfile-deps",
137 help="File containing plugin deps not set as other input.")
138 parser.add_argument("--plugin-depfile-outputs",
139 help="File containing a list of files that will be generated.")
140 parser.add_argument("--plugin-options",
141 help="Custom generator plugin options.")
142 parser.add_argument("--cc-options",
143 help="Standard C++ generator options.")
144 parser.add_argument("--include",
145 help="Name of include to insert into generated headers.")
146 parser.add_argument("--import-dir", action="append", default=[],
147 help="Extra import directory for protos, can be repeated."
148 )
149 parser.add_argument("--descriptor-set-out",
150 help="Passed through to protoc as --descriptor_set_out")
151
152 parser.add_argument("protos", nargs="+",
153 help="Input protobuf definition file(s).")
154
155 options = parser.parse_args()
156
157 proto_dir = os.path.relpath(options.proto_in_dir)
158 protoc_cmd = [os.path.realpath(options.protoc)]
159
160 protos = options.protos
161 headers = []
162 VerifyProtoNames(protos)
163
164 if options.descriptor_set_out:
165 protoc_cmd += ["--descriptor_set_out", options.descriptor_set_out]
166
167 if options.py_out_dir:
168 protoc_cmd += ["--python_out", options.py_out_dir]
169
170 if options.cc_out_dir:
171 cc_out_dir = options.cc_out_dir
172 cc_options = FormatGeneratorOptions(options.cc_options)
173 protoc_cmd += ["--cpp_out", cc_options + cc_out_dir]
174 for filename in protos:
175 stripped_name = StripProtoExtension(filename)
176 headers.append(os.path.join(cc_out_dir, stripped_name + ".pb.h"))
177
178 if options.plugin_out_dir:
179 plugin_options = FormatGeneratorOptions(options.plugin_options)
180 protoc_cmd += [
181 "--plugin", "protoc-gen-plugin=" + os.path.relpath(options.plugin),
182 "--plugin_out", plugin_options + options.plugin_out_dir
183 ]
184
185 if options.plugin_depfile:
186 if not options.plugin_depfile_deps or not options.plugin_depfile_outputs:
187 raise RuntimeError("If plugin depfile is supplied, then the plugin "
188 "depfile deps and outputs must be set.")
189 depfile = options.plugin_depfile
190 dep_info = options.plugin_depfile_deps
191 outputs = options.plugin_depfile_outputs
192 WritePluginDepfile(depfile, outputs, dep_info)
193
194 if options.depfile:
195 if not options.depfile_outputs:
196 raise RuntimeError("If depfile is supplied, depfile outputs must also"
197 "be supplied.")
198
199 depfile = options.depfile
200 outputs = options.depfile_outputs
201 deps = set()
202
203 for proto in protos:
204 deps.update(ExtractImports(proto, proto_dir, options.import_dir))
205 WriteProtocDepfile(depfile, outputs, deps)
206
207 protoc_cmd += ["--proto_path", proto_dir]
208 for path in options.import_dir:
209 protoc_cmd += ["--proto_path", path]
210
211 protoc_cmd += [os.path.join(proto_dir, name) for name in protos]
212
213 ret = subprocess.call(protoc_cmd)
214 if ret != 0:
215 raise RuntimeError("Protoc has returned non-zero status: "
216 "{0} .".format(ret))
217
218 if options.include:
219 WriteIncludes(headers, options.include)
220
221
222if __name__ == "__main__":
223 try:
224 main(sys.argv)
225 except RuntimeError as e:
226 print(e, file=sys.stderr)
227 sys.exit(1)
void print(void *str)
Definition bridge.cpp:126
uint32_t uint32_t * format
Definition main.py:1
WritePluginDepfile(depfile, outputs, dependencies)
VerifyProtoNames(protos)
WriteDepfile(depfile, out_list, dep_list)
StripProtoExtension(filename)
FormatGeneratorOptions(options)
WriteProtocDepfile(depfile, outputs, deps_list)
ExtractImports(proto, proto_dir, import_dirs)
WriteIncludes(headers, include)