Merge "Apilint updates" into qt-dev
This commit is contained in:
147
tools/apilint/apilint
Executable file
147
tools/apilint/apilint
Executable file
@@ -0,0 +1,147 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright (C) 2019 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.
|
||||
|
||||
if [ "$1" == "--help" -o "$1" == "-h" ]; then
|
||||
echo "Usage: apilint [FILTERS...]"
|
||||
echo " Shows lint from currently open files (as diffed from HEAD), i.e. errors"
|
||||
echo " you will receive if you upload this CL."
|
||||
echo
|
||||
echo "Usage: apilint --all [FILTERS...]"
|
||||
echo " Shows all lint errors present in the current working directory, regardless"
|
||||
echo " of when they were added."
|
||||
echo
|
||||
echo "Usage: apilint --level API_LEVEL [FILTERS...]"
|
||||
echo " Shows lint as it stands in API_LEVEL"
|
||||
echo
|
||||
echo "Usage: apilint --shal SHA [FILTERS...]"
|
||||
echo " Shows lint from locally commited git change SHA."
|
||||
echo
|
||||
echo "Usage: apilint --unreleased [FILTERS...]"
|
||||
echo " Shows all lint errors in the current working directory directory added since"
|
||||
echo " the last released SDK version."
|
||||
echo
|
||||
echo "FILTERS"
|
||||
echo " List of class or package names by which to filter the results."
|
||||
echo
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ \( -z "$ANDROID_BUILD_TOP" \) \
|
||||
-a \( ! -f frameworks/base/api/current.txt \) \
|
||||
-a \( ! -f frameworks/base/api/system-current.txt \) \
|
||||
]; then
|
||||
echo "apilint must be run either with ANDROID_BUILD_TOP set or from the" 1>&2
|
||||
echo "root of the android source tree" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ${ANDROID_BUILD_TOP:0:1} != "/" ]; then
|
||||
echo "ANDROID_BUILD_TOP must be an absolute path, not: $ANDROID_BUILD_TOP" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$ANDROID_BUILD_TOP" ]; then
|
||||
ANDROID_BUILD_TOP=$(pwd)
|
||||
fi
|
||||
|
||||
FW_BASE=$ANDROID_BUILD_TOP/frameworks/base
|
||||
|
||||
MODE=open
|
||||
|
||||
OPTIONS=$(getopt -n apilint -o "" -l "all,sha:,unreleased" -- "$@")
|
||||
|
||||
[ $? -eq 0 ] || {
|
||||
exit 1
|
||||
}
|
||||
|
||||
eval set -- "$OPTIONS"
|
||||
while true; do
|
||||
case "$1" in
|
||||
--all)
|
||||
MODE=all
|
||||
;;
|
||||
--sha)
|
||||
shift; # The arg is next in position args
|
||||
MODE=sha
|
||||
SHA=$1
|
||||
;;
|
||||
--unreleased)
|
||||
MODE=unreleased
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
FILTERS=
|
||||
for var in "$@"
|
||||
do
|
||||
FILTERS="$FILTERS --filter $var"
|
||||
done
|
||||
|
||||
if [ $MODE = "all" ]; then
|
||||
python2.7 -B $ANDROID_BUILD_TOP/frameworks/base/tools/apilint/apilint.py \
|
||||
--title "SDK" \
|
||||
$FILTERS \
|
||||
$ANDROID_BUILD_TOP/frameworks/base/api/current.txt
|
||||
python2.7 -B $ANDROID_BUILD_TOP/frameworks/base/tools/apilint/apilint.py \
|
||||
--title "SystemApi" \
|
||||
$FILTERS \
|
||||
--base-current $ANDROID_BUILD_TOP/frameworks/base/api/current.txt \
|
||||
$ANDROID_BUILD_TOP/frameworks/base/api/system-current.txt
|
||||
elif [ $MODE = "open" ]; then
|
||||
python2.7 -B $ANDROID_BUILD_TOP/frameworks/base/tools/apilint/apilint.py \
|
||||
--title "SDK" \
|
||||
$FILTERS \
|
||||
$ANDROID_BUILD_TOP/frameworks/base/api/current.txt \
|
||||
<(cd $FW_BASE ; git show HEAD:api/current.txt)
|
||||
python2.7 -B $ANDROID_BUILD_TOP/frameworks/base/tools/apilint/apilint.py \
|
||||
--title "SystemApi" \
|
||||
$FILTERS \
|
||||
--base-current $ANDROID_BUILD_TOP/frameworks/base/api/current.txt \
|
||||
--base-previous <(cd $FW_BASE ; git show HEAD:api/current.txt) \
|
||||
$ANDROID_BUILD_TOP/frameworks/base/api/system-current.txt \
|
||||
<(cd $FW_BASE ; git show HEAD:api/system-current.txt)
|
||||
elif [ $MODE = "sha" ]; then
|
||||
python2.7 -B $ANDROID_BUILD_TOP/frameworks/base/tools/apilint/apilint.py \
|
||||
--title "SDK" \
|
||||
$FILTERS \
|
||||
<(cd $FW_BASE ; git show $SHA:api/current.txt) \
|
||||
<(cd $FW_BASE ; git show $SHA^:api/current.txt)
|
||||
python2.7 -B $ANDROID_BUILD_TOP/frameworks/base/tools/apilint/apilint.py \
|
||||
--title "SystemApi" \
|
||||
$FILTERS \
|
||||
--base-current <(cd $FW_BASE ; git show $SHA:api/current.txt) \
|
||||
--base-previous <(cd $FW_BASE ; git show $SHA^:api/current.txt) \
|
||||
<(cd $FW_BASE ; git show $SHA:api/system-current.txt) \
|
||||
<(cd $FW_BASE ; git show $SHA^:api/system-current.txt)
|
||||
elif [ $MODE = "unreleased" ]; then
|
||||
LAST_SDK=$(ls $ANDROID_BUILD_TOP/prebuilts/sdk | grep "^[0-9][0-9]*$" | sort -n | tail -n 1)
|
||||
python2.7 -B $ANDROID_BUILD_TOP/frameworks/base/tools/apilint/apilint.py \
|
||||
--title "SDK" \
|
||||
$FILTERS \
|
||||
$ANDROID_BUILD_TOP/frameworks/base/api/current.txt \
|
||||
$ANDROID_BUILD_TOP/prebuilts/sdk/$LAST_SDK/public/api/android.txt
|
||||
python2.7 -B $ANDROID_BUILD_TOP/frameworks/base/tools/apilint/apilint.py \
|
||||
--title "SystemApi" \
|
||||
$FILTERS \
|
||||
--base-current $ANDROID_BUILD_TOP/frameworks/base/api/current.txt \
|
||||
--base-previous $ANDROID_BUILD_TOP/prebuilts/sdk/$LAST_SDK/public/api/android.txt \
|
||||
$ANDROID_BUILD_TOP/frameworks/base/api/system-current.txt \
|
||||
$ANDROID_BUILD_TOP/prebuilts/sdk/$LAST_SDK/system/api/android.txt
|
||||
fi
|
||||
@@ -707,6 +707,7 @@ def _yield_until_matching_class(classes, needle):
|
||||
|
||||
class Failure():
|
||||
def __init__(self, sig, clazz, detail, error, rule, msg):
|
||||
self.clazz = clazz
|
||||
self.sig = sig
|
||||
self.error = error
|
||||
self.rule = rule
|
||||
@@ -2126,6 +2127,15 @@ def verify_compat(cur, prev):
|
||||
return failures
|
||||
|
||||
|
||||
def match_filter(filters, fullname):
|
||||
for f in filters:
|
||||
if fullname == f:
|
||||
return True
|
||||
if fullname.startswith(f + '.'):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def show_deprecations_at_birth(cur, prev):
|
||||
"""Show API deprecations at birth."""
|
||||
global failures
|
||||
@@ -2199,12 +2209,9 @@ def show_stats(cur, prev):
|
||||
print " ", "".join([ str(stats[k]).ljust(20) for k in sorted(stats.keys()) ])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Enforces common Android public API design \
|
||||
patterns. It ignores lint messages from a previous API level, if provided.")
|
||||
parser.add_argument("current.txt", type=argparse.FileType('r'), help="current.txt")
|
||||
parser.add_argument("previous.txt", nargs='?', type=argparse.FileType('r'), default=None,
|
||||
help="previous.txt")
|
||||
parser.add_argument("--base-current", nargs='?', type=argparse.FileType('r'), default=None,
|
||||
help="The base current.txt to use when examining system-current.txt or"
|
||||
" test-current.txt")
|
||||
@@ -2213,6 +2220,8 @@ if __name__ == "__main__":
|
||||
" test-previous.txt")
|
||||
parser.add_argument("--no-color", action='store_const', const=True,
|
||||
help="Disable terminal colors")
|
||||
parser.add_argument("--color", action='store_const', const=True,
|
||||
help="Use terminal colors")
|
||||
parser.add_argument("--allow-google", action='store_const', const=True,
|
||||
help="Allow references to Google")
|
||||
parser.add_argument("--show-noticed", action='store_const', const=True,
|
||||
@@ -2221,10 +2230,21 @@ if __name__ == "__main__":
|
||||
help="Show API deprecations at birth")
|
||||
parser.add_argument("--show-stats", action='store_const', const=True,
|
||||
help="Show API stats")
|
||||
parser.add_argument("--title", action='store', default=None,
|
||||
help="Title to put in for display purposes")
|
||||
parser.add_argument("--filter", action="append",
|
||||
help="If provided, only show lint for the given packages or classes.")
|
||||
parser.add_argument("current.txt", type=argparse.FileType('r'), help="current.txt")
|
||||
parser.add_argument("previous.txt", nargs='?', type=argparse.FileType('r'), default=None,
|
||||
help="previous.txt")
|
||||
args = vars(parser.parse_args())
|
||||
|
||||
if args['no_color']:
|
||||
USE_COLOR = False
|
||||
elif args['color']:
|
||||
USE_COLOR = True
|
||||
else:
|
||||
USE_COLOR = sys.stdout.isatty()
|
||||
|
||||
if args['allow_google']:
|
||||
ALLOW_GOOGLE = True
|
||||
@@ -2233,6 +2253,12 @@ if __name__ == "__main__":
|
||||
base_current_file = args['base_current']
|
||||
previous_file = args['previous.txt']
|
||||
base_previous_file = args['base_previous']
|
||||
filters = args['filter']
|
||||
if not filters:
|
||||
filters = []
|
||||
title = args['title']
|
||||
if not title:
|
||||
title = current_file.name
|
||||
|
||||
if args['show_deprecations_at_birth']:
|
||||
with current_file as f:
|
||||
@@ -2290,6 +2316,11 @@ if __name__ == "__main__":
|
||||
print
|
||||
"""
|
||||
|
||||
# ignore everything but the given filters, if provided
|
||||
if filters:
|
||||
cur_fail = dict([(key, failure) for key, failure in cur_fail.iteritems()
|
||||
if match_filter(filters, failure.clazz.fullname)])
|
||||
|
||||
if args['show_noticed'] and len(cur_noticed) != 0:
|
||||
print "%s API changes noticed %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True)))
|
||||
for f in sorted(cur_noticed.keys()):
|
||||
@@ -2297,8 +2328,20 @@ if __name__ == "__main__":
|
||||
print
|
||||
|
||||
if len(cur_fail) != 0:
|
||||
print "%s API style issues %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True)))
|
||||
print "%s API style issues: %s %s" % ((format(fg=WHITE, bg=BLUE, bold=True),
|
||||
title, format(reset=True)))
|
||||
for f in filters:
|
||||
print "%s filter: %s %s" % ((format(fg=WHITE, bg=BLUE, bold=True),
|
||||
f, format(reset=True)))
|
||||
print
|
||||
for f in sorted(cur_fail):
|
||||
print cur_fail[f]
|
||||
print
|
||||
print "%d errors" % len(cur_fail)
|
||||
sys.exit(77)
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(1)
|
||||
|
||||
@@ -392,5 +392,23 @@ class PackageTests(unittest.TestCase):
|
||||
p = self._package("package @Rt(a.b.L_G_P) @RestrictTo(a.b.C) an.pref.int {")
|
||||
self.assertEquals('an.pref.int', p.name)
|
||||
|
||||
class FilterTests(unittest.TestCase):
|
||||
def test_filter_match_prefix(self):
|
||||
self.assertTrue(apilint.match_filter(["a"], "a.B"))
|
||||
self.assertTrue(apilint.match_filter(["a.B"], "a.B.C"))
|
||||
|
||||
def test_filter_dont_match_prefix(self):
|
||||
self.assertFalse(apilint.match_filter(["c"], "a.B"))
|
||||
self.assertFalse(apilint.match_filter(["a."], "a.B"))
|
||||
self.assertFalse(apilint.match_filter(["a.B."], "a.B.C"))
|
||||
|
||||
def test_filter_match_exact(self):
|
||||
self.assertTrue(apilint.match_filter(["a.B"], "a.B"))
|
||||
|
||||
def test_filter_dont_match_exact(self):
|
||||
self.assertFalse(apilint.match_filter([""], "a.B"))
|
||||
self.assertFalse(apilint.match_filter(["a.C"], "a.B"))
|
||||
self.assertFalse(apilint.match_filter(["a.C"], "a.B"))
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user