Loading tools/apilint/apilint 0 → 100755 +147 −0 Original line number Diff line number Diff line #!/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 tools/apilint/apilint.py +48 −5 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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") Loading @@ -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, Loading @@ -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 Loading @@ -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: Loading Loading @@ -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()): Loading @@ -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) tools/apilint/apilint_test.py +18 −0 Original line number Diff line number Diff line Loading @@ -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() Loading
tools/apilint/apilint 0 → 100755 +147 −0 Original line number Diff line number Diff line #!/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
tools/apilint/apilint.py +48 −5 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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 Loading Loading @@ -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") Loading @@ -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, Loading @@ -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 Loading @@ -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: Loading Loading @@ -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()): Loading @@ -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)
tools/apilint/apilint_test.py +18 −0 Original line number Diff line number Diff line Loading @@ -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()