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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
|
#!/usr/bin/env python3
#
# Copyright (C) 2018 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
"""This file generates project.xml and lint.xml files used to drive the Android Lint CLI tool."""
import argparse
from xml.dom import minidom
from ninja_rsp import NinjaRspFileReader
def check_action(check_type):
"""
Returns an action that appends a tuple of check_type and the argument to the dest.
"""
class CheckAction(argparse.Action):
def __init__(self, option_strings, dest, nargs=None, **kwargs):
if nargs is not None:
raise ValueError("nargs must be None, was %s" % nargs)
super(CheckAction, self).__init__(option_strings, dest, **kwargs)
def __call__(self, parser, namespace, values, option_string=None):
checks = getattr(namespace, self.dest, [])
checks.append((check_type, values))
setattr(namespace, self.dest, checks)
return CheckAction
def parse_args():
"""Parse commandline arguments."""
def convert_arg_line_to_args(arg_line):
for arg in arg_line.split():
if arg.startswith('#'):
return
if not arg.strip():
continue
yield arg
parser = argparse.ArgumentParser(fromfile_prefix_chars='@')
parser.convert_arg_line_to_args = convert_arg_line_to_args
parser.add_argument('--project_out', dest='project_out',
help='file to which the project.xml contents will be written.')
parser.add_argument('--config_out', dest='config_out',
help='file to which the lint.xml contents will be written.')
parser.add_argument('--name', dest='name',
help='name of the module.')
parser.add_argument('--srcs', dest='srcs', action='append', default=[],
help='file containing whitespace separated list of source files.')
parser.add_argument('--generated_srcs', dest='generated_srcs', action='append', default=[],
help='file containing whitespace separated list of generated source files.')
parser.add_argument('--resources', dest='resources', action='append', default=[],
help='file containing whitespace separated list of resource files.')
parser.add_argument('--classes', dest='classes', action='append', default=[],
help='file containing the module\'s classes.')
parser.add_argument('--classpath', dest='classpath', action='append', default=[],
help='file containing classes from dependencies.')
parser.add_argument('--extra_checks_jar', dest='extra_checks_jars', action='append', default=[],
help='file containing extra lint checks.')
parser.add_argument('--manifest', dest='manifest',
help='file containing the module\'s manifest.')
parser.add_argument('--merged_manifest', dest='merged_manifest',
help='file containing merged manifest for the module and its dependencies.')
parser.add_argument('--baseline', dest='baseline_path',
help='file containing baseline lint issues.')
parser.add_argument('--library', dest='library', action='store_true',
help='mark the module as a library.')
parser.add_argument('--test', dest='test', action='store_true',
help='mark the module as a test.')
parser.add_argument('--cache_dir', dest='cache_dir',
help='directory to use for cached file.')
parser.add_argument('--root_dir', dest='root_dir',
help='directory to use for root dir.')
group = parser.add_argument_group('check arguments', 'later arguments override earlier ones.')
group.add_argument('--fatal_check', dest='checks', action=check_action('fatal'), default=[],
help='treat a lint issue as a fatal error.')
group.add_argument('--error_check', dest='checks', action=check_action('error'), default=[],
help='treat a lint issue as an error.')
group.add_argument('--warning_check', dest='checks', action=check_action('warning'), default=[],
help='treat a lint issue as a warning.')
group.add_argument('--disable_check', dest='checks', action=check_action('ignore'), default=[],
help='disable a lint issue.')
group.add_argument('--disallowed_issues', dest='disallowed_issues', default=[],
help='lint issues disallowed in the baseline file')
return parser.parse_args()
def write_project_xml(f, args):
test_attr = "test='true' " if args.test else ""
f.write("<?xml version='1.0' encoding='utf-8'?>\n")
f.write("<project>\n")
if args.root_dir:
f.write(" <root dir='%s' />\n" % args.root_dir)
f.write(" <module name='%s' android='true' %sdesugar='full' >\n" % (args.name, "library='true' " if args.library else ""))
if args.manifest:
f.write(" <manifest file='%s' %s/>\n" % (args.manifest, test_attr))
if args.merged_manifest:
f.write(" <merged-manifest file='%s' %s/>\n" % (args.merged_manifest, test_attr))
for src_file in args.srcs:
for src in NinjaRspFileReader(src_file):
f.write(" <src file='%s' %s/>\n" % (src, test_attr))
for src_file in args.generated_srcs:
for src in NinjaRspFileReader(src_file):
f.write(" <src file='%s' generated='true' %s/>\n" % (src, test_attr))
for res_file in args.resources:
for res in NinjaRspFileReader(res_file):
f.write(" <resource file='%s' %s/>\n" % (res, test_attr))
for classes in args.classes:
f.write(" <classes jar='%s' />\n" % classes)
for classpath in args.classpath:
f.write(" <classpath jar='%s' />\n" % classpath)
for extra in args.extra_checks_jars:
f.write(" <lint-checks jar='%s' />\n" % extra)
f.write(" </module>\n")
if args.cache_dir:
f.write(" <cache dir='%s'/>\n" % args.cache_dir)
f.write("</project>\n")
def write_config_xml(f, args):
f.write("<?xml version='1.0' encoding='utf-8'?>\n")
f.write("<lint>\n")
for check in args.checks:
f.write(" <issue id='%s' severity='%s' />\n" % (check[1], check[0]))
f.write("</lint>\n")
def check_baseline_for_disallowed_issues(baseline, forced_checks):
issues_element = baseline.documentElement
if issues_element.tagName != 'issues':
raise RuntimeError('expected issues tag at root')
issues = issues_element.getElementsByTagName('issue')
disallowed = set()
for issue in issues:
id = issue.getAttribute('id')
if id in forced_checks:
disallowed.add(id)
return disallowed
def main():
"""Program entry point."""
args = parse_args()
if args.baseline_path:
baseline = minidom.parse(args.baseline_path)
disallowed_issues = check_baseline_for_disallowed_issues(baseline, args.disallowed_issues)
if bool(disallowed_issues):
raise RuntimeError('disallowed issues %s found in lint baseline file %s for module %s'
% (disallowed_issues, args.baseline_path, args.name))
if args.project_out:
with open(args.project_out, 'w') as f:
write_project_xml(f, args)
if args.config_out:
with open(args.config_out, 'w') as f:
write_config_xml(f, args)
if __name__ == '__main__':
main()
|