|  | // Copyright (c) 2017, the Rex 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. | 
|  | package com.android.tools.r8; | 
|  |  | 
|  | import static com.android.tools.r8.utils.StringUtils.quote; | 
|  |  | 
|  | import com.android.tools.r8.dex.ApplicationReader; | 
|  | import com.android.tools.r8.dex.Marker; | 
|  | import com.android.tools.r8.graph.DexApplication; | 
|  | import com.android.tools.r8.keepanno.annotations.KeepForApi; | 
|  | import com.android.tools.r8.origin.Origin; | 
|  | import com.android.tools.r8.utils.AndroidApiLevel; | 
|  | import com.android.tools.r8.utils.AndroidApp; | 
|  | import com.android.tools.r8.utils.ExceptionUtils; | 
|  | import com.android.tools.r8.utils.InternalOptions; | 
|  | import com.android.tools.r8.utils.ListUtils; | 
|  | import com.android.tools.r8.utils.MarkerInfoConsumerDataImpl; | 
|  | import com.android.tools.r8.utils.MarkerInfoImpl; | 
|  | import com.android.tools.r8.utils.Reporter; | 
|  | import com.android.tools.r8.utils.Timing; | 
|  | import java.io.IOException; | 
|  | import java.io.PrintStream; | 
|  | import java.util.ArrayList; | 
|  | import java.util.Collection; | 
|  | import java.util.Comparator; | 
|  | import java.util.List; | 
|  |  | 
|  | /** | 
|  | * A tool to extract markers generated by the R8 compiler and related tools. | 
|  | * | 
|  | * <p>The tool is run by defining a consumer and then building the command, such as: | 
|  | * | 
|  | * <pre> | 
|  | *   ExtractMarker.run(ExtractMarkerCommand.builder() | 
|  | *       .addProgramFiles(inputPathA, inputPathB) | 
|  | *       .setMarkerInfoConsumer(new MyInfoConsumer()) | 
|  | *       .build()); | 
|  | * </pre> | 
|  | */ | 
|  | @KeepForApi | 
|  | public class ExtractMarker { | 
|  |  | 
|  | private static class MarkerInfoPrintConsumer implements MarkerInfoConsumer { | 
|  |  | 
|  | private final PrintStream stream; | 
|  |  | 
|  | public MarkerInfoPrintConsumer(PrintStream stream) { | 
|  | this.stream = stream; | 
|  | } | 
|  |  | 
|  | private void printRow(Origin inputOrigin, String rawMarker) { | 
|  | stream.print(inputOrigin.toString()); | 
|  | stream.print(": "); | 
|  | stream.print(rawMarker); | 
|  | stream.println(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void acceptMarkerInfo(MarkerInfoConsumerData data) { | 
|  | if (data.hasMarkers()) { | 
|  | for (MarkerInfo marker : data.getMarkers()) { | 
|  | printRow(data.getInputOrigin(), marker.getRawEncoding()); | 
|  | } | 
|  | } else { | 
|  | printRow(data.getInputOrigin(), quote("no marker")); | 
|  | } | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void finished() {} | 
|  | } | 
|  |  | 
|  | private static class InterceptedException extends RuntimeException { | 
|  | private final CompilationFailedException compilationFailedException; | 
|  |  | 
|  | private InterceptedException(CompilationFailedException e) { | 
|  | this.compilationFailedException = e; | 
|  | } | 
|  | } | 
|  |  | 
|  | private static Collection<Marker> extractMarker(AndroidApp app) throws IOException { | 
|  | InternalOptions options = new InternalOptions(); | 
|  | options.skipReadingDexCode = true; | 
|  | options.setMinApiLevel(AndroidApiLevel.P); | 
|  | DexApplication dexApp = new ApplicationReader(app, options, new Timing("ExtractMarker")).read(); | 
|  | return dexApp.dexItemFactory.extractMarkers(); | 
|  | } | 
|  |  | 
|  | private static void extractForConsumer( | 
|  | MarkerInfoConsumer consumer, Reporter reporter, Origin origin, AndroidApp.Builder builder) { | 
|  | List<Marker> markers = new ArrayList<>(); | 
|  | try { | 
|  | ExceptionUtils.withCompilationHandler( | 
|  | reporter, () -> markers.addAll(extractMarker(builder.build()))); | 
|  | } catch (CompilationFailedException e) { | 
|  | throw new InterceptedException(e); | 
|  | } | 
|  | // Ensure the markers are sorted, so we have deterministic callback/print order. | 
|  | markers.sort(Comparator.comparing(Marker::toString)); | 
|  | List<MarkerInfo> infos = ListUtils.map(markers, MarkerInfoImpl::new); | 
|  | consumer.acceptMarkerInfo(new MarkerInfoConsumerDataImpl(origin, infos)); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Main API entry for the extract marker tool. | 
|  | * | 
|  | * @param command Extract marker command specification. | 
|  | */ | 
|  | public static void run(ExtractMarkerCommand command) throws CompilationFailedException { | 
|  | MarkerInfoConsumer consumer = command.getMarkerInfoConsumer(); | 
|  | Reporter reporter = new Reporter(command.getDiagnosticsHandler()); | 
|  | try { | 
|  | command.forEachEntry( | 
|  | (path, origin) -> | 
|  | extractForConsumer( | 
|  | consumer, reporter, origin, AndroidApp.builder().addProgramFile(path)), | 
|  | (data, origin) -> | 
|  | extractForConsumer( | 
|  | consumer, reporter, origin, AndroidApp.builder().addDexProgramData(data, origin)), | 
|  | (data, origin) -> | 
|  | extractForConsumer( | 
|  | consumer, | 
|  | reporter, | 
|  | origin, | 
|  | AndroidApp.builder().addClassProgramData(data, origin))); | 
|  | } catch (InterceptedException e) { | 
|  | throw e.compilationFailedException; | 
|  | } | 
|  | } | 
|  |  | 
|  | private static void run(String[] args) throws CompilationFailedException { | 
|  | PrintStream out = System.out; | 
|  | ExtractMarkerCommand.Builder builder = ExtractMarkerCommand.parse(args); | 
|  | MarkerInfoConsumer consumer = new MarkerInfoPrintConsumer(out); | 
|  | ExtractMarkerCommand command = builder.setMarkerInfoConsumer(consumer).build(); | 
|  | if (command.isPrintHelp()) { | 
|  | System.out.println(ExtractMarkerCommand.USAGE_MESSAGE); | 
|  | return; | 
|  | } | 
|  | run(command); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Command-line entry to the extract marker tool. | 
|  | * | 
|  | * <p>See {@link ExtractMarkerCommand#USAGE_MESSAGE} or run with {@code --help} for usage | 
|  | * information. | 
|  | */ | 
|  | public static void main(String[] args) throws Exception { | 
|  | ExceptionUtils.withMainProgramHandler(() -> run(args)); | 
|  | } | 
|  | } |