blob: e94927616a460010f078a52d278321bd29905ce7 [file] [log] [blame]
// Copyright (c) 2017, 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.
package com.android.tools.r8;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.PathOrigin;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.IOExceptionDiagnostic;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Path;
/**
* Interface for receiving usage feedback from R8.
*
* The data is in the format defined for Proguard's <code>-printusage</code> flag. The information
* will be produced if a consumer is provided. A consumer is automatically setup when running R8
* with the Proguard <code>-printusage</code> flag set.
*/
public interface UsageInformationConsumer {
/**
* Callback to receive the usage-information data.
*
* <p>The consumer is expected not to throw, but instead report any errors via the diagnostics
* {@param handler}. If an error is reported via {@param handler} and no exceptions are thrown,
* then the compiler guaranties to exit with an error.
*
* @param data UTF-8 encoded usage information.
* @param handler Diagnostics handler for reporting.
*/
void acceptUsageInformation(byte[] data, DiagnosticsHandler handler);
static EmptyConsumer emptyConsumer() {
return EmptyConsumer.EMPTY_CONSUMER;
}
/** Empty consumer to request usage information but ignore the result. */
class EmptyConsumer implements UsageInformationConsumer {
private static final EmptyConsumer EMPTY_CONSUMER = new EmptyConsumer();
private EmptyConsumer() {}
@Override
public void acceptUsageInformation(byte[] data, DiagnosticsHandler handler) {
// Ignore content.
}
}
/** Forwarding consumer to delegate to an optional existing consumer. */
class ForwardingConsumer implements UsageInformationConsumer {
private final UsageInformationConsumer consumer;
/** @param consumer Consumer to forward to, if null, nothing will be forwarded. */
public ForwardingConsumer(UsageInformationConsumer consumer) {
this.consumer = consumer;
}
@Override
public void acceptUsageInformation(byte[] data, DiagnosticsHandler handler) {
if (consumer != null) {
consumer.acceptUsageInformation(data, handler);
}
}
}
/** File consumer to write contents to a file-system file. */
class FileConsumer extends ForwardingConsumer {
private final Path outputPath;
/** Consumer that writes to {@param outputPath}. */
public FileConsumer(Path outputPath) {
this(outputPath, null);
}
/** Consumer that forwards to {@param consumer} and also writes to {@param outputPath}. */
public FileConsumer(Path outputPath, UsageInformationConsumer consumer) {
super(consumer);
this.outputPath = outputPath;
}
@Override
public void acceptUsageInformation(byte[] data, DiagnosticsHandler handler) {
super.acceptUsageInformation(data, handler);
try {
FileUtils.writeToFile(outputPath, null, data);
} catch (IOException e) {
handler.error(new IOExceptionDiagnostic(e, new PathOrigin(outputPath)));
}
}
}
/**
* Stream consumer to write contents to an output stream.
*
* <p>Note: No close events are given to this stream so it should either be a permanent stream or
* the closing needs to happen outside of the compilation itself. If the stream is not one of the
* standard streams, i.e., System.out or System.err, you should likely implement yor own consumer.
*/
class StreamConsumer extends ForwardingConsumer {
private final Origin origin;
private final OutputStream outputStream;
/** Consumer that writes to {@param outputStream}. */
public StreamConsumer(Origin origin, OutputStream outputStream) {
this(origin, outputStream, null);
}
/** Consumer that forwards to {@param consumer} and also writes to {@param outputStream}. */
public StreamConsumer(
Origin origin, OutputStream outputStream, UsageInformationConsumer consumer) {
super(consumer);
this.origin = origin;
this.outputStream = outputStream;
}
@Override
public void acceptUsageInformation(byte[] data, DiagnosticsHandler handler) {
super.acceptUsageInformation(data, handler);
try {
outputStream.write(data);
} catch (IOException e) {
handler.error(new IOExceptionDiagnostic(e, origin));
}
}
}
}