| /* | |
| * Copyright (C) 2025 The Android Open Source Project | |
| * | |
| * 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.android.dependencymapper; | |
| import static com.android.dependencymapper.Utils.listClassesInJar; | |
| import java.io.IOException; | |
| import java.nio.file.Files; | |
| import java.nio.file.Path; | |
| import java.util.HashSet; | |
| import java.util.List; | |
| import java.util.Set; | |
| public class Main { | |
| public static void main(String[] args) throws IOException, InterruptedException { | |
| try { | |
| InputData input = parseAndValidateInput(args); | |
| generateDependencyMap(input); | |
| } catch (IllegalArgumentException e) { | |
| System.err.println("Error: " + e.getMessage()); | |
| showUsage(); | |
| } | |
| } | |
| private static class InputData { | |
| public Path srcList; | |
| public Path classesJar; | |
| public Path dependencyMapProto; | |
| public Path crossModuleJarList; | |
| public InputData(Path srcList, Path classesJar, Path dependencyMapProto, Path crossModuleJarList) { | |
| this.srcList = srcList; | |
| this.classesJar = classesJar; | |
| this.dependencyMapProto = dependencyMapProto; | |
| this.crossModuleJarList = crossModuleJarList; | |
| } | |
| } | |
| private static InputData parseAndValidateInput(String[] args) { | |
| for (String arg : args) { | |
| if ("--help".equals(arg)) { | |
| showUsage(); | |
| System.exit(0); // Indicate successful exit after showing help | |
| } | |
| } | |
| if (args.length != 8) { // Explicitly check for the correct number of arguments | |
| throw new IllegalArgumentException("Incorrect number of arguments"); | |
| } | |
| Path srcList = null; | |
| Path classesJar = null; | |
| Path dependencyMapProto = null; | |
| Path crossModuleJars = null; | |
| for (int i = 0; i < args.length; i += 2) { | |
| String arg = args[i].trim(); | |
| String argValue = args[i + 1].trim(); | |
| switch (arg) { | |
| case "--src-path" -> srcList = Path.of(argValue); | |
| case "--jar-path" -> classesJar = Path.of(argValue); | |
| case "--dependency-map-path" -> dependencyMapProto = Path.of(argValue); | |
| case "--cross-module-jar-list" -> crossModuleJars = Path.of(argValue); | |
| default -> throw new IllegalArgumentException("Unknown argument: " + arg); | |
| } | |
| } | |
| // Validate file existence and readability | |
| validateFile(srcList, "--src-path"); | |
| validateFile(classesJar, "--jar-path"); | |
| return new InputData(srcList, classesJar, dependencyMapProto, crossModuleJars); | |
| } | |
| private static void validateFile(Path path, String argName) { | |
| if (path == null) { | |
| throw new IllegalArgumentException(argName + " is required"); | |
| } | |
| if (!Files.exists(path)) { | |
| throw new IllegalArgumentException(argName + " does not exist: " + path); | |
| } | |
| if (!Files.isReadable(path)) { | |
| throw new IllegalArgumentException(argName + " is not readable: " + path); | |
| } | |
| } | |
| private static void generateDependencyMap(InputData input) { | |
| // First collect all classes in the jar. | |
| Set<String> classesInJar = listClassesInJar(input.classesJar); | |
| Set<String> crossModuleJars = Utils.parseRspFile(input.crossModuleJarList); | |
| Set<String> crossModuleClasses = new HashSet<>(); | |
| for (String jar : crossModuleJars) { | |
| crossModuleClasses.addAll(listClassesInJar(Path.of(jar))); | |
| } | |
| // Perform dependency analysis. | |
| List<ClassDependencyData> classDependencyDataList = ClassDependencyAnalyzer.analyze( | |
| input.classesJar, | |
| new ClassRelevancyFilter(classesInJar), | |
| new ClassRelevancyFilter(crossModuleClasses)); | |
| // Perform java source analysis. | |
| List<JavaSourceData> javaSourceDataList = JavaSourceAnalyzer.parse(input.srcList); | |
| // Collect all dependencies and map them as DependencyProto.FileDependencyList | |
| DependencyMapper dp = new DependencyMapper(classDependencyDataList, javaSourceDataList); | |
| DependencyProto.FileDependencyList dependencyList = dp.buildDependencyMaps(); | |
| // Write the proto to output file | |
| // Use Utils.writeContentsToJson() to get a radable json view for debugging. | |
| Utils.writeContentsToProto(dependencyList, input.dependencyMapProto); | |
| } | |
| private static void showUsage() { | |
| System.err.println( | |
| "Usage: dependency-mapper " | |
| + "--src-path <src-list.rsp> " | |
| + "--jar-path <classes.jar> " | |
| + "--cross-module-jar-list <jar-list.rsp>" | |
| + "--dependency-map-path <dependency-map.proto> "); | |
| } | |
| } |