Add support for running libanalyzer on local maven repository
Bug: b/483568398
Change-Id: Ia7ef4e45d335af95964f35723c9f9e47bf64837c
diff --git a/src/libanalyzer/java/com/android/tools/r8/libanalyzer/LibraryAnalyzer.java b/src/libanalyzer/java/com/android/tools/r8/libanalyzer/LibraryAnalyzer.java
index 107570a..0eef23d 100644
--- a/src/libanalyzer/java/com/android/tools/r8/libanalyzer/LibraryAnalyzer.java
+++ b/src/libanalyzer/java/com/android/tools/r8/libanalyzer/LibraryAnalyzer.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.D8Command;
import com.android.tools.r8.R8;
import com.android.tools.r8.R8Command;
+import com.android.tools.r8.Version;
import com.android.tools.r8.keepanno.annotations.KeepForApi;
import com.android.tools.r8.libanalyzer.proto.D8CompileResult;
import com.android.tools.r8.libanalyzer.proto.LibraryAnalysisResult;
@@ -78,8 +79,7 @@
return;
}
if (command.isPrintVersion()) {
- // TODO(b/479726064): Read the version from a field.
- System.out.println("LibraryAnalyzer 0.0.1");
+ System.out.println("LibraryAnalyzer " + Version.getVersionString());
return;
}
ExceptionUtils.withR8CompilationHandler(
@@ -96,7 +96,9 @@
private InternalD8CompileResult runD8(ExecutorService executorService) {
DexIndexedSizeConsumer sizeConsumer = new DexIndexedSizeConsumer();
D8Command.Builder commandBuilder =
- D8Command.builder(options.reporter).setProgramConsumer(sizeConsumer);
+ D8Command.builder(options.reporter)
+ .setClassConflictResolver((reference, origins, handler) -> origins.iterator().next())
+ .setProgramConsumer(sizeConsumer);
configure(commandBuilder);
try {
D8.run(commandBuilder.build(), executorService);
diff --git a/src/libanalyzer/java/com/android/tools/r8/libanalyzer/LibraryAnalyzerCommandParser.java b/src/libanalyzer/java/com/android/tools/r8/libanalyzer/LibraryAnalyzerCommandParser.java
index b47858f..78b9c65 100644
--- a/src/libanalyzer/java/com/android/tools/r8/libanalyzer/LibraryAnalyzerCommandParser.java
+++ b/src/libanalyzer/java/com/android/tools/r8/libanalyzer/LibraryAnalyzerCommandParser.java
@@ -21,10 +21,15 @@
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableSet;
+import java.io.File;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Set;
+import java.util.stream.Stream;
@KeepForApi
public class LibraryAnalyzerCommandParser {
@@ -32,6 +37,7 @@
private static final String AAR_FLAG = "--aar";
private static final String BLAST_RADIUS_OUTPUT_FLAG = "--blast-radius-output";
private static final String JAR_FLAG = "--jar";
+ private static final String REPO_FLAG = "--repo";
private static final Set<String> OPTIONS_WITH_ONE_PARAMETER =
ImmutableSet.of(
@@ -41,6 +47,7 @@
LIB_FLAG,
MIN_API_FLAG,
OUTPUT_FLAG,
+ REPO_FLAG,
THREAD_COUNT_FLAG);
private static final String USAGE_MESSAGE =
@@ -55,6 +62,7 @@
" # Only allowed after --aar <path> or --jar <path>.",
" --min-api <major.minor> # Minimum API level to use for analysis.",
" --output <path> # Path where to write analysis result (protobuf).",
+ " --repo <path> # Path to local Maven repository.",
" --thread-count <int> # Number of threads to use.",
" --help # Print this message.",
" --version # Print the version.");
@@ -130,6 +138,26 @@
builder.setMinApiLevel(AndroidApiLevel.parseAndroidApiLevel(nextArg));
} else if (arg.equals(OUTPUT_FLAG)) {
builder.setOutputPath(Paths.get(nextArg));
+ } else if (arg.equals(REPO_FLAG)) {
+ Path repoPath = Paths.get(nextArg);
+ if (!Files.isDirectory(repoPath)) {
+ throw new IllegalArgumentException(
+ "Invalid parameter for --repo. Expected directory, got: " + nextArg);
+ }
+ try (Stream<Path> paths = Files.walk(repoPath)) {
+ paths
+ .filter(path -> FileUtils.isAarFile(path) || FileUtils.isJarFile(path))
+ .forEach(
+ path -> {
+ if (FileUtils.isAarFile(path)) {
+ builder.addAarPath(path, getMavenOriginFromArchive(repoPath, path));
+ } else {
+ builder.addJarPath(path, getMavenOriginFromArchive(repoPath, path));
+ }
+ });
+ } catch (IOException e) {
+ throw new UncheckedIOException("Failed to walk repository path: " + nextArg, e);
+ }
} else if (arg.equals(THREAD_COUNT_FLAG)) {
parsePositiveIntArgument(
reporter::error, THREAD_COUNT_FLAG, nextArg, origin, builder::setThreadCount);
@@ -140,6 +168,17 @@
return builder;
}
+ private static Origin getMavenOriginFromArchive(Path repoPath, Path path) {
+ String directoryName = repoPath.relativize(path).getParent().toString();
+ int lastSeparator = directoryName.lastIndexOf(File.separatorChar);
+ int prevSeparator = directoryName.lastIndexOf(File.separatorChar, lastSeparator - 1);
+ String group =
+ StringUtils.replaceAll(directoryName.substring(0, prevSeparator), File.separator, ".");
+ String module = directoryName.substring(prevSeparator + 1, lastSeparator);
+ String version = directoryName.substring(lastSeparator + 1);
+ return new PathBasedMavenOrigin(path, group, module, version);
+ }
+
private static String peekArg(String[] expandedArgs, int index) {
return ArrayUtils.getOrDefault(expandedArgs, index, null);
}