Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(419)

Side by Side Diff: tools/protoc_wrapper/protoc_wrapper.py

Issue 2239383002: GN proto_libary refactoring. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: review iteration Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """ 6 """
7 A simple wrapper for protoc. 7 A simple wrapper for protoc.
8 8 Script for //third_party/protobuf/proto_library.gni .
9 - Adds includes in generated headers. 9 Features:
10 - Handles building with system protobuf as an option. 10 - Inserts #include for extra header automatically.
11 - Prevents bad proto names.
11 """ 12 """
12 13
13 import fnmatch 14 from __future__ import print_function
14 import optparse 15 import argparse
15 import os.path 16 import os.path
16 import shutil
17 import subprocess 17 import subprocess
18 import sys 18 import sys
19 import tempfile 19 import tempfile
20 20
21 PROTOC_INCLUDE_POINT = '// @@protoc_insertion_point(includes)\n' 21 PROTOC_INCLUDE_POINT = "// @@protoc_insertion_point(includes)"
22
23 def ModifyHeader(header_file, extra_header):
24 """Adds |extra_header| to |header_file|. Returns 0 on success.
25
26 |extra_header| is the name of the header file to include.
27 |header_file| is a generated protobuf cpp header.
28 """
29 include_point_found = False
30 header_contents = []
31 with open(header_file) as f:
32 for line in f:
33 header_contents.append(line)
34 if line == PROTOC_INCLUDE_POINT:
35 extra_header_msg = '#include "%s"\n' % extra_header
36 header_contents.append(extra_header_msg)
37 include_point_found = True;
38 if not include_point_found:
39 return 1
40
41 with open(header_file, 'wb') as f:
42 f.write(''.join(header_contents))
43 return 0
44
45 def ScanForBadFiles(scan_root):
46 """Scan for bad file names, see http://crbug.com/386125 for details.
47 Returns True if any filenames are bad. Outputs errors to stderr.
48
49 |scan_root| is the path to the directory to be recursively scanned.
50 """
51 badname = False
52 real_scan_root = os.path.realpath(scan_root)
53 for dirpath, dirnames, filenames in os.walk(real_scan_root):
54 matches = fnmatch.filter(filenames, '*-*.proto')
55 if len(matches) > 0:
56 if not badname:
57 badname = True
58 sys.stderr.write('proto files must not have hyphens in their names ('
59 'see http://crbug.com/386125 for more information):\n')
60 for filename in matches:
61 sys.stderr.write(' ' + os.path.join(real_scan_root,
62 dirpath, filename) + '\n')
63 return badname
64 22
65 23
66 def RewriteProtoFilesForSystemProtobuf(path): 24 def FormatGeneratorOptions(options):
67 wrapper_dir = tempfile.mkdtemp() 25 if not options:
68 try: 26 return ""
69 for filename in os.listdir(path): 27 if options.endswith(":"):
70 if not filename.endswith('.proto'): 28 return options
71 continue 29 return options + ":"
72 with open(os.path.join(path, filename), 'r') as src_file:
73 with open(os.path.join(wrapper_dir, filename), 'w') as dst_file:
74 for line in src_file:
75 # Remove lines that break build with system protobuf.
76 # We cannot optimize for lite runtime, because system lite runtime
77 # does not have a Chromium-specific hack to retain unknown fields.
78 # Similarly, it does not understand corresponding option to control
79 # the usage of that hack.
80 if 'LITE_RUNTIME' in line or 'retain_unknown_fields' in line:
81 continue
82 dst_file.write(line)
83 30
84 return wrapper_dir 31
85 except: 32 def StripProtoExtension(filename):
86 shutil.rmtree(wrapper_dir) 33 if not filename.endswith(".proto"):
87 raise 34 raise RuntimeError("Invalid proto filename extension: "
35 "{0} .".format(filename))
petrcermak 2016/08/16 14:10:31 nit: Please remove space before period.
kraynov 2016/08/16 16:11:26 It's just to separate from an identifier.
36 if "-" in filename:
37 raise RuntimeError("Proto file names must not contents hyphens "
38 "(see http://crbug.com/386125 for more information).")
39 return filename.rsplit(".", 1)[0]
40
41
42 def WriteIncludes(headers, include):
43 for filename in headers:
44 include_point_found = False
45 contents = []
46 with open(filename) as f:
47 for line in f:
48 stripped_line = line.strip()
49 contents.append(stripped_line)
50 if include_point_found:
51 continue
petrcermak 2016/08/16 14:10:31 Wouldn't it be better to add this if check inside
kraynov 2016/08/16 16:11:26 Done.
52 if stripped_line == PROTOC_INCLUDE_POINT:
53 extra_statement = "#include \"{0}\"".format(include)
54 contents.append(extra_statement)
55 include_point_found = True
56
57 if not include_point_found:
58 raise RuntimeError("Include point not found in header: "
59 "{0} .".format(filename))
60
61 with open(filename, "w") as f:
62 for line in contents:
63 print(line, file=f)
88 64
89 65
90 def main(argv): 66 def main(argv):
91 parser = optparse.OptionParser() 67 parser = argparse.ArgumentParser()
92 parser.add_option('--include', dest='extra_header', 68 parser.add_argument("--protoc",
93 help='The extra header to include. This must be specified ' 69 help="Relative path to compiler.")
94 'along with --protobuf.')
95 parser.add_option('--protobuf', dest='generated_header',
96 help='The c++ protobuf header to add the extra header to. '
97 'This must be specified along with --include.')
98 parser.add_option('--proto-in-dir',
99 help='The directory containing .proto files.')
100 parser.add_option('--proto-in-file', help='Input file to compile.')
101 parser.add_option('--use-system-protobuf', type=int, default=0,
102 help='Option to use system-installed protobuf '
103 'instead of bundled one.')
104 (options, args) = parser.parse_args(sys.argv)
105 if len(args) < 2:
106 return 1
107 70
108 if ScanForBadFiles(options.proto_in_dir): 71 parser.add_argument("--proto-in-dir",
109 return 1 72 help="Base directory with source protos.")
73 parser.add_argument("--cc-out-dir",
74 help="Output directory for standard C++ generator.")
75 parser.add_argument("--py-out-dir",
76 help="Output directory for standard Python generator.")
77 parser.add_argument("--plugin-out-dir",
78 help="Output directory for custom generator plugin.")
110 79
111 proto_path = options.proto_in_dir 80 parser.add_argument("--plugin",
112 if options.use_system_protobuf == 1: 81 help="Relative path to custom generator plugin.")
113 proto_path = RewriteProtoFilesForSystemProtobuf(proto_path) 82 parser.add_argument("--plugin-options",
114 try: 83 help="Custom generator plugin options.")
115 # Run what is hopefully protoc. 84 parser.add_argument("--cc-options",
116 protoc_args = args[1:] 85 help="Standard C++ generator options.")
117 protoc_args += ['--proto_path=%s' % proto_path, 86 parser.add_argument("--include",
118 os.path.join(proto_path, options.proto_in_file)] 87 help="Name of include to insert into generated headers.")
119 ret = subprocess.call(protoc_args)
120 if ret != 0:
121 return ret
122 finally:
123 if options.use_system_protobuf == 1:
124 # Remove temporary directory holding re-written files.
125 shutil.rmtree(proto_path)
126 88
127 # protoc succeeded, check to see if the generated cpp header needs editing. 89 parser.add_argument("protos", nargs="+",
128 if not options.extra_header or not options.generated_header: 90 help="Input protobuf definition file(s).")
129 return 0 91
130 return ModifyHeader(options.generated_header, options.extra_header) 92 options = parser.parse_args()
93
94 proto_dir = os.path.relpath(options.proto_in_dir)
95 protoc_cmd = [
96 os.path.realpath(options.protoc),
97 "--proto_path",
98 proto_dir
99 ]
100
101 protos = options.protos
102 stripped_protos = [StripProtoExtension(filename) for filename in protos]
petrcermak 2016/08/16 14:10:31 You only need this inside one of the if statements
kraynov 2016/08/16 16:11:26 Done.
103 headers = []
104
105 if options.cc_out_dir:
106 cc_out_dir = options.cc_out_dir
107 cc_options = FormatGeneratorOptions(options.cc_options)
108 protoc_cmd += [
109 "--cpp_out",
110 cc_options + cc_out_dir
111 ]
112 for name in stripped_protos:
113 headers.append(os.path.join(cc_out_dir, name + ".pb.h"))
114
115 if options.py_out_dir is not None:
petrcermak 2016/08/16 14:10:31 (also applies to line 121) Unless empty strings ar
kraynov 2016/08/16 16:11:26 Done.
116 protoc_cmd += [
117 "--python_out",
118 options.py_out_dir
119 ]
120
121 if options.plugin_out_dir is not None:
122 plugin_options = FormatGeneratorOptions(options.plugin_options)
123 protoc_cmd += [
124 "--plugin",
125 "protoc-gen-plugin=" + os.path.relpath(options.plugin),
126 "--plugin_out",
127 plugin_options + options.plugin_out_dir,
128 ]
129
130 protoc_cmd += [os.path.join(proto_dir, name) for name in protos]
131 ret = subprocess.call(protoc_cmd)
132 if ret != 0:
133 raise RuntimeError("Protoc has returned non-zero status: "
134 "{0} .".format(str(ret)))
petrcermak 2016/08/16 14:10:31 I don't think you need to call str(...) explicitly
kraynov 2016/08/16 16:11:26 Done.
135
136 if options.include:
137 WriteIncludes(headers, options.include)
131 138
132 139
133 if __name__ == '__main__': 140 if __name__ == "__main__":
134 sys.exit(main(sys.argv)) 141 try:
142 main(sys.argv)
143 except RuntimeError as e:
144 print(e, file=sys.stderr)
145 sys.exit(1)
OLDNEW
« third_party/protobuf/proto_library.gni ('K') | « third_party/protobuf/proto_library.gni ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698