From 6b591a7e20f05224898b31ad5b03ba514eff6118 Mon Sep 17 00:00:00 2001 From: sgjesse Date: Fri, 14 Aug 2020 02:47:33 -0700 Subject: [PATCH] Prepare for bazel to run with shrunken r8.jar With Android Platform SDK build tools 30.0.1 the r8.jar included is shrunken (r8lib), so some of the internal APIs are not available. Change the integration to deal with that. This uses reflection to access the internal APIs if available and otherwise stick to the public API. If a shrunked r8.jar is used, bazel will not support the configuration of "check main dex" and "minimal main dex" flags which are not supported in the public D8 API. This will also make it possible for bazel to pull r8.jar from Google Maven, where only the shrunken r8.jar is published. RELNOTES: None. PiperOrigin-RevId: 326619633 --- .../com/android/tools/r8/CompatDxSupport.java | 86 ++++++++++++++----- .../devtools/build/android/r8/CompatDx.java | 20 ++--- .../android/r8/CompatDxCompilationError.java | 24 ++++++ .../android/r8/CompatDxUnimplemented.java | 24 ++++++ 4 files changed, 121 insertions(+), 33 deletions(-) create mode 100644 src/tools/android/java/com/google/devtools/build/android/r8/CompatDxCompilationError.java create mode 100644 src/tools/android/java/com/google/devtools/build/android/r8/CompatDxUnimplemented.java diff --git a/src/tools/android/java/com/android/tools/r8/CompatDxSupport.java b/src/tools/android/java/com/android/tools/r8/CompatDxSupport.java index 31141b313f7dd2..e6a305b2a01c6e 100644 --- a/src/tools/android/java/com/android/tools/r8/CompatDxSupport.java +++ b/src/tools/android/java/com/android/tools/r8/CompatDxSupport.java @@ -13,8 +13,6 @@ // limitations under the License. package com.android.tools.r8; -import com.android.tools.r8.utils.AndroidApp; -import com.android.tools.r8.utils.InternalOptions; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -25,30 +23,74 @@ public class CompatDxSupport { public static void run(D8Command command, boolean minimalMainDex) throws CompilationFailedException { - AndroidApp app = command.getInputApp(); - InternalOptions options = command.getInternalOptions(); - // DX allows --multi-dex without specifying a main dex list for legacy devices. - // That is broken, but for CompatDX we do the same to not break existing builds - // that are trying to transition. + try { + // bazel can point to both an full r8.jar and to the shrunken r8.jar (r8lib.jar), as the + // r8.jar is currently referenced from the Android SDK build-tools, where different versions + // have either full or shrunken jar. From build-tools version 30.0.1 the shrunken jar is + // shipped. If the full jar is used some additional internal APIs are used for additional + // configuration of "check main dex" and "minimal main dex" flags which are not + // supported in the public D8 API. + Class androidAppClass = Class.forName("com.android.tools.r8.utils.AndroidApp"); + Class internalOptionsClass = Class.forName("com.android.tools.r8.utils.InternalOptions"); + runOnFullJar(command, minimalMainDex, androidAppClass, internalOptionsClass); + } catch (ClassNotFoundException e) { + D8.run(command); + } + } + + public static void runOnFullJar( + D8Command command, + boolean minimalMainDex, + Class androidAppClass, + Class internalOptionsClass) { + Method getInputAppMethod; + Method getInternalOptionsMethod; + Method runForTestingMethod; + try { + getInputAppMethod = BaseCommand.class.getDeclaredMethod("getInputApp"); + getInternalOptionsMethod = D8Command.class.getDeclaredMethod("getInternalOptions"); + runForTestingMethod = + D8.class.getDeclaredMethod("runForTesting", androidAppClass, internalOptionsClass); + } catch (NoSuchMethodException e) { + throw new AssertionError("Unsupported r8.jar", e); + } + try { // Use reflection for: - // options.enableMainDexListCheck = false; - // as bazel might link to an old r8.jar which does not have this field. - Field enableMainDexListCheck = options.getClass().getField("enableMainDexListCheck"); + // AndroidApp app = command.getInputApp(); + // InternalOptions options = command.getInternalOptions(); + // as bazel might link to a shrunken r8.jar which does not have these APIs. + Object app = getInputAppMethod.invoke(command); + Object options = getInternalOptionsMethod.invoke(command); + // DX allows --multi-dex without specifying a main dex list for legacy devices. + // That is broken, but for CompatDX we do the same to not break existing builds + // that are trying to transition. try { - enableMainDexListCheck.setBoolean(options, false); - } catch (IllegalAccessException e) { - throw new AssertionError(e); + Field enableMainDexListCheckField = internalOptionsClass.getField("enableMainDexListCheck"); + // DX has a minimal main dex flag. In compat mode only do minimal main dex + // if the flag is actually set. + Field minimalMainDexField = internalOptionsClass.getField("minimalMainDex"); + try { + // Use reflection for: + // options.enableMainDexListCheck = false; + // as bazel might link to an old r8.jar which does not have this field. + enableMainDexListCheckField.setBoolean(options, false); + // Use reflection for: + // options.minimalMainDex = minimalMainDex; + // as bazel might link to an old r8.jar which does not have this field. + minimalMainDexField.setBoolean(options, minimalMainDex); + } catch (IllegalAccessException e) { + throw new AssertionError("Unsupported r8.jar", e); + } + } catch (NoSuchFieldException e) { + // Ignore if bazel is linking to an old r8.jar. } - } catch (NoSuchFieldException e) { - // Ignore if bazel is linking to an old r8.jar. - } - - // DX has a minimal main dex flag. In compat mode only do minimal main dex - // if the flag is actually set. - options.minimalMainDex = minimalMainDex; - D8.runForTesting(app, options); + runForTestingMethod.invoke(null, app, options); + } catch (ReflectiveOperationException e) { + // This is an unsupported r8.jar. + throw new AssertionError("Unsupported r8.jar", e); + } } public static void enableDesugarBackportStatics(D8Command.Builder builder) { @@ -61,7 +103,7 @@ public static void enableDesugarBackportStatics(D8Command.Builder builder) { try { enableDesugarBackportStatics.invoke(builder); } catch (ReflectiveOperationException e) { - throw new AssertionError(e); + throw new AssertionError("Unsupported r8.jar", e); } } catch (NoSuchMethodException e) { // Ignore if bazel is linking to an old r8.jar. diff --git a/src/tools/android/java/com/google/devtools/build/android/r8/CompatDx.java b/src/tools/android/java/com/google/devtools/build/android/r8/CompatDx.java index 3adec8f9f79deb..7f0ad8f4bf0517 100644 --- a/src/tools/android/java/com/google/devtools/build/android/r8/CompatDx.java +++ b/src/tools/android/java/com/google/devtools/build/android/r8/CompatDx.java @@ -27,8 +27,6 @@ import com.android.tools.r8.DiagnosticsHandler; import com.android.tools.r8.ProgramConsumer; import com.android.tools.r8.Version; -import com.android.tools.r8.errors.CompilationError; -import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.origin.Origin; import com.android.tools.r8.origin.PathOrigin; import com.android.tools.r8.utils.ArchiveResourceProvider; @@ -523,11 +521,11 @@ private static void run(String[] args) } if (dexArgs.verboseDump) { - throw new Unimplemented("verbose dump file not yet supported"); + throw new CompatDxUnimplemented("verbose dump file not yet supported"); } if (dexArgs.methodToDump != null) { - throw new Unimplemented("method-dump not yet supported"); + throw new CompatDxUnimplemented("method-dump not yet supported"); } if (dexArgs.output != null) { @@ -557,7 +555,7 @@ private static void run(String[] args) } if (dexArgs.incremental) { - throw new Unimplemented("incremental merge not supported yet"); + throw new CompatDxUnimplemented("incremental merge not supported yet"); } if (dexArgs.forceJumbo && dexArgs.verbose) { @@ -573,11 +571,11 @@ private static void run(String[] args) } if (dexArgs.optimizeList != null) { - throw new Unimplemented("no support for optimize-method list"); + throw new CompatDxUnimplemented("no support for optimize-method list"); } if (dexArgs.noOptimizeList != null) { - throw new Unimplemented("no support for dont-optimize-method list"); + throw new CompatDxUnimplemented("no support for dont-optimize-method list"); } if (dexArgs.statistics && dexArgs.verbose) { @@ -647,7 +645,7 @@ private static void run(String[] args) setMinimalMainDex.invoke(builder, dexArgs.minimalMainDex); D8.run(builder.build()); } catch (ReflectiveOperationException e) { - // Go through the support support code accessing the internals for the compilation. + // Go through the support code accessing the internals for the compilation. CompatDxSupport.run(builder.build(), dexArgs.minimalMainDex); } } finally { @@ -696,7 +694,7 @@ public SingleDexFileConsumer(DexIndexedConsumer consumer) { public void accept( int fileIndex, ByteDataView data, Set descriptors, DiagnosticsHandler handler) { if (fileIndex > 0) { - throw new CompilationError( + throw new CompatDxCompilationError( "Compilation result could not fit into a single dex file. " + "Reduce the input-program size or run with --multi-dex enabled"); } @@ -870,7 +868,7 @@ private void writeInputClassesToArchive(DiagnosticsHandler handler) throws IOExc private static void processPath(Path path, List files) throws IOException { if (!Files.exists(path)) { - throw new CompilationError("File does not exist: " + path); + throw new CompatDxCompilationError("File does not exist: " + path); } if (Files.isDirectory(path)) { processDirectory(path, files); @@ -881,7 +879,7 @@ private static void processPath(Path path, List files) throws IOException return; } if (FileUtils.isApkFile(path)) { - throw new Unimplemented("apk files not yet supported: " + path); + throw new CompatDxUnimplemented("apk files not yet supported: " + path); } } diff --git a/src/tools/android/java/com/google/devtools/build/android/r8/CompatDxCompilationError.java b/src/tools/android/java/com/google/devtools/build/android/r8/CompatDxCompilationError.java new file mode 100644 index 00000000000000..c7cbbeb3226215 --- /dev/null +++ b/src/tools/android/java/com/google/devtools/build/android/r8/CompatDxCompilationError.java @@ -0,0 +1,24 @@ +// Copyright 2020 The Bazel Authors. All rights reserved. +// +// 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. +package com.google.devtools.build.android.r8; + +/** Compilation error in CompatDx */ +public class CompatDxCompilationError extends RuntimeException { + + public CompatDxCompilationError() {} + + public CompatDxCompilationError(String message) { + super(message); + } +} diff --git a/src/tools/android/java/com/google/devtools/build/android/r8/CompatDxUnimplemented.java b/src/tools/android/java/com/google/devtools/build/android/r8/CompatDxUnimplemented.java new file mode 100644 index 00000000000000..e03d3269368c6c --- /dev/null +++ b/src/tools/android/java/com/google/devtools/build/android/r8/CompatDxUnimplemented.java @@ -0,0 +1,24 @@ +// Copyright 2020 The Bazel Authors. All rights reserved. +// +// 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. +package com.google.devtools.build.android.r8; + +/** Unimplemented in CompatDx */ +public class CompatDxUnimplemented extends RuntimeException { + + public CompatDxUnimplemented() {} + + public CompatDxUnimplemented(String message) { + super(message); + } +}