| /* |
| * 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> "); |
| } |
| |
| } |