Skip to content

Commit

Permalink
Prepare for bazel to run with shrunken r8.jar
Browse files Browse the repository at this point in the history
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
  • Loading branch information
sgjesse authored and copybara-github committed Aug 14, 2020
1 parent 8d656cf commit 6b591a7
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 33 deletions.
86 changes: 64 additions & 22 deletions src/tools/android/java/com/android/tools/r8/CompatDxSupport.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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:
// <code>options.enableMainDexListCheck = false;</code>
// as bazel might link to an old r8.jar which does not have this field.
Field enableMainDexListCheck = options.getClass().getField("enableMainDexListCheck");
// <code>AndroidApp app = command.getInputApp();</code>
// <code>InternalOptions options = command.getInternalOptions();</code>
// 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:
// <code>options.enableMainDexListCheck = false;</code>
// as bazel might link to an old r8.jar which does not have this field.
enableMainDexListCheckField.setBoolean(options, false);
// Use reflection for:
// <code>options.minimalMainDex = minimalMainDex;</code>
// 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) {
Expand All @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -696,7 +694,7 @@ public SingleDexFileConsumer(DexIndexedConsumer consumer) {
public void accept(
int fileIndex, ByteDataView data, Set<String> 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");
}
Expand Down Expand Up @@ -870,7 +868,7 @@ private void writeInputClassesToArchive(DiagnosticsHandler handler) throws IOExc

private static void processPath(Path path, List<Path> 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);
Expand All @@ -881,7 +879,7 @@ private static void processPath(Path path, List<Path> 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);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}

0 comments on commit 6b591a7

Please sign in to comment.