Add script to print R8 API not covered by API usage sample
Add a script api_sample_coverage.py that runs two programs PrintUses and
PrintSeeds and prints the difference in the outputs.
PrintUses prints the entry points in a given library (e.g. r8.jar)
used by a given program (e.g. d8_api_usage_sample.jar).
PrintSeeds prints the entry points in a given library (e.g. r8.jar)
kept by a given ProGuard configuration (e.g. src/main/keep.txt).
* Add 'include' predicate to RootSetBuilder.writeSeeds() to allow
PrintSeeds to skip printing classes in the Java library.
* Enqueuer: Add a helpful assertion error for when PrintSeeds discovers
that a library class is missing.
* Sort SwissArmyKnife to reduce the opportunity for merge conflicts
* Add utils.R8LIB_KEEP_RULES with path to keep-rules for keeping @Keep
Change-Id: I5963a1094a5bb9a99795a5cdeee6b6698551726a
diff --git a/tools/api_sample_coverage.py b/tools/api_sample_coverage.py
new file mode 100755
index 0000000..b14ad1a
--- /dev/null
+++ b/tools/api_sample_coverage.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python
+# Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+'''
+Compare the R8 API used by the API usage sample to the API kept by @Keep.
+'''
+
+import argparse
+import os
+import subprocess
+import utils
+
+parser = argparse.ArgumentParser(description=__doc__.strip(),
+ formatter_class=argparse.RawTextHelpFormatter)
+parser.add_argument('-o', '--output-dir')
+
+API_SAMPLE_JAR = 'tests/d8_api_usage_sample.jar'
+
+
+def main(output_dir=None):
+ if output_dir is None:
+ output_dir = ''
+
+ printseeds_path = os.path.join(output_dir, 'keep-seeds.txt')
+ printseeds_args = [
+ 'java', '-jar', utils.R8_JAR, 'printseeds',
+ utils.RT_JAR, utils.R8_JAR, utils.R8LIB_KEEP_RULES,
+ ]
+ write_sorted_lines(printseeds_args, printseeds_path)
+
+ printuses_path = os.path.join(output_dir, 'sample-uses.txt')
+ printuses_args = [
+ 'java', '-jar', utils.R8_JAR, 'printuses',
+ utils.RT_JAR, utils.R8_JAR, API_SAMPLE_JAR,
+ ]
+ write_sorted_lines(printuses_args, printuses_path)
+
+ print_diff(printseeds_path, printuses_path)
+
+
+def write_sorted_lines(cmd_args, output_path):
+ utils.PrintCmd(cmd_args)
+ output_lines = subprocess.check_output(cmd_args).splitlines(True)
+ print("Write output to %s" % output_path)
+ output_lines.sort()
+ with open(output_path, 'w') as fp:
+ for line in output_lines:
+ fp.write(line)
+
+
+def print_diff(printseeds_path, printuses_path):
+ with open(printseeds_path) as fp:
+ seeds = set(fp.read().splitlines())
+ with open(printuses_path) as fp:
+ uses = set(fp.read().splitlines())
+ only_in_seeds = seeds - uses
+ only_in_uses = uses - seeds
+ if only_in_seeds:
+ print("%s lines with '-' are marked @Keep " % len(only_in_seeds) +
+ "but not used by sample.")
+ if only_in_uses:
+ print("%s lines with '+' are used by sample " % len(only_in_uses) +
+ "but are missing @Keep annotations.")
+ for line in sorted(only_in_seeds):
+ print('-' + line)
+ for line in sorted(only_in_uses):
+ print('+' + line)
+ if not only_in_seeds and not only_in_uses:
+ print('Sample uses the entire set of members marked @Keep. Well done!')
+
+
+if __name__ == '__main__':
+ main(**vars(parser.parse_args()))
diff --git a/tools/build_r8lib.py b/tools/build_r8lib.py
index abaa619..c855bc5 100755
--- a/tools/build_r8lib.py
+++ b/tools/build_r8lib.py
@@ -18,7 +18,6 @@
formatter_class=argparse.RawTextHelpFormatter)
SAMPLE_JAR = os.path.join(utils.REPO_ROOT, 'tests/d8_api_usage_sample.jar')
-KEEP_RULES = os.path.join(utils.REPO_ROOT, 'src/main/keep.txt')
R8LIB_JAR = os.path.join(utils.LIBS, 'r8lib.jar')
R8LIB_MAP_FILE = os.path.join(utils.LIBS, 'r8lib-map.txt')
@@ -34,7 +33,7 @@
'--lib', utils.RT_JAR,
utils.R8_JAR,
'--output', R8LIB_JAR,
- '--pg-conf', KEEP_RULES,
+ '--pg-conf', utils.R8LIB_KEEP_RULES,
'--pg-map-output', R8LIB_MAP_FILE))
diff --git a/tools/printseeds.py b/tools/printseeds.py
new file mode 100755
index 0000000..4f389d5
--- /dev/null
+++ b/tools/printseeds.py
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+# Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+import sys
+import toolhelper
+
+if __name__ == '__main__':
+ sys.exit(toolhelper.run('printseeds', sys.argv[1:]))
diff --git a/tools/printuses.py b/tools/printuses.py
new file mode 100755
index 0000000..17d3df1
--- /dev/null
+++ b/tools/printuses.py
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+# Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+import sys
+import toolhelper
+
+if __name__ == '__main__':
+ sys.exit(toolhelper.run('printuses', sys.argv[1:]))
diff --git a/tools/utils.py b/tools/utils.py
index 859da97..851f3bd 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -37,6 +37,7 @@
MAVEN_ZIP = os.path.join(LIBS, 'r8.zip')
GENERATED_LICENSE = os.path.join(GENERATED_LICENSE_DIR, 'LICENSE')
RT_JAR = os.path.join(REPO_ROOT, 'third_party/openjdk/openjdk-rt-1.8/rt.jar')
+R8LIB_KEEP_RULES = os.path.join(REPO_ROOT, 'src/main/keep.txt')
def PrintCmd(s):
if type(s) is list: