forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathjava_cpp_features.py
More file actions
executable file
·151 lines (119 loc) · 5.05 KB
/
java_cpp_features.py
File metadata and controls
executable file
·151 lines (119 loc) · 5.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#!/usr/bin/env python3
#
# Copyright 2020 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import argparse
import re
import sys
import zipfile
from util import build_utils # pylint: disable=unused-import
from util import java_cpp_utils
import action_helpers # build_utils adds //build to sys.path.
import zip_helpers
class FeatureParserDelegate(java_cpp_utils.CppConstantParser.Delegate):
# Ex. 'BASE_FEATURE(kConstantName, "StringNameOfTheFeature", ...);'
# or 'BASE_FEATURE(kConstantName, ...);'
# would parse as:
# ExtractConstantName() -> 'ConstantName'
# ExtractValue() -> '"StringNameOfTheFeature"' or '"ConstantName"'
_FEATURE_RE = re.compile(r'BASE_FEATURE\(\s*(k\w+),')
_STRING_LITERAL_RE = re.compile(r'"(?:\\"|[^"])*"')
_constant_name = None # The name of the current macro.
_comma_count = 0 # Number of commas seen in the current macro.
_string_literals = [] # All string literals seen in the current macro.
def ExtractConstantName(self, line):
# Reset to a clean state when extracting constant name.
self._comma_count = 0
self._string_literals = []
self._constant_name = None
match = self._FEATURE_RE.match(line)
if match:
# The regex ensures that the feature name starts with 'k'.
feature_name = match.group(1)
self._constant_name = feature_name[1:]
return self._constant_name
return None
def ExtractValue(self, line):
if line.strip().startswith('#') or line.strip().startswith("//"):
# Skip C++ directives and comments within macros
return None
# Track all string literals.
self._string_literals.extend(self._STRING_LITERAL_RE.findall(line))
self._comma_count += line.count(',')
# The value of a feature is determined once the whole macro has been parsed.
if ')' not in line:
return None
# If there is exactly one string literal, that is the value. This handles
# the 3-arg form of BASE_FEATURE.
if len(self._string_literals) == 1:
return self._string_literals[0]
# If there are multiple string literals, we can't be sure which is correct.
if len(self._string_literals) > 1:
return None
# If there is no string literal, this might be the 2-arg form. The value is
# the constant name, quoted.
if self._comma_count == 1:
return f'"{self._constant_name}"'
return None
def CreateJavaConstant(self, name, value, comments):
return java_cpp_utils.JavaString(name, value, comments)
# This delegate requires multiline parsing to determine the feature string.
def RequiresMultilineValueParsing(self):
return True
def _GenerateOutput(template, source_paths, features):
description_template = """
// This following string constants were inserted by
// {SCRIPT_NAME}
// From
// {SOURCE_PATHS}
"""
values = {
'SCRIPT_NAME': java_cpp_utils.GetScriptName(),
'SOURCE_PATHS': ',\n // '.join(source_paths),
}
description = description_template.format(**values)
native_features = '\n\n'.join(x.Format() for x in features)
# TODO(agrieve): Remove {{ and }} from input templates.
template = template.replace('{{', '{').replace('}}', '}')
return template.replace('{NATIVE_FEATURES}', description + native_features)
def _ParseFeatureFile(path):
with open(path, encoding='utf-8') as f:
feature_file_parser = java_cpp_utils.CppConstantParser(
FeatureParserDelegate(), f.readlines())
return feature_file_parser.Parse()
def _Generate(source_paths, template):
package, class_name = java_cpp_utils.ParseTemplateFile(template)
output_path = java_cpp_utils.GetJavaFilePath(package, class_name)
features = []
for source_path in source_paths:
features.extend(_ParseFeatureFile(source_path))
output = _GenerateOutput(template, source_paths, features)
return output, output_path
def _Main(argv):
parser = argparse.ArgumentParser()
parser.add_argument('--srcjar',
required=True,
help='The path at which to generate the .srcjar file')
parser.add_argument('--template',
help='The template file with which to generate the Java '
'class. Must have "{NATIVE_FEATURES}" somewhere in '
'the template.')
parser.add_argument('--class-name', help='FQN of Java class to generate')
parser.add_argument('inputs',
nargs='+',
help='Input file(s)',
metavar='INPUTFILE')
args = parser.parse_args(argv)
if args.template:
with open(args.template, encoding='utf-8') as f:
template = f.read()
else:
template = java_cpp_utils.CreateDefaultTemplate(args.class_name,
'{NATIVE_FEATURES}')
with action_helpers.atomic_output(args.srcjar) as f:
with zipfile.ZipFile(f, 'w', zipfile.ZIP_STORED) as srcjar:
data, path = _Generate(args.inputs, template)
zip_helpers.add_to_zip_hermetic(srcjar, path, data=data)
if __name__ == '__main__':
_Main(sys.argv[1:])