diff --git a/src/main/java/com/ibm/cldk/CodeAnalyzer.java b/src/main/java/com/ibm/cldk/CodeAnalyzer.java index 2490e57..1a1f54f 100644 --- a/src/main/java/com/ibm/cldk/CodeAnalyzer.java +++ b/src/main/java/com/ibm/cldk/CodeAnalyzer.java @@ -46,7 +46,7 @@ class VersionProvider implements CommandLine.IVersionProvider { public String[] getVersion() throws Exception { String version = getClass().getPackage().getImplementationVersion(); - return new String[]{version != null ? version : "unknown"}; + return new String[] { version != null ? version : "unknown" }; } } @@ -56,34 +56,43 @@ public String[] getVersion() throws Exception { @Command(name = "codeanalyzer", mixinStandardHelpOptions = true, sortOptions = false, versionProvider = VersionProvider.class, description = "Analyze java application.") public class CodeAnalyzer implements Runnable { - @Option(names = {"-i", "--input"}, description = "Path to the project root directory.") + @Option(names = { "-i", "--input" }, description = "Path to the project root directory.") private static String input; - @Option(names = {"-t", "--target-files"}, description = "Paths to files to be analyzed from the input application.") + @Option(names = { "-t", + "--target-files" }, description = "Paths to files to be analyzed from the input application.") private static List targetFiles; - @Option(names = {"-s", "--source-analysis"}, description = "Analyze a single string of java source code instead the project.") + @Option(names = { "-s", + "--source-analysis" }, description = "Analyze a single string of java source code instead the project.") private static String sourceAnalysis; - @Option(names = {"-o", "--output"}, description = "Destination directory to save the output graphs. By default, the SDG formatted as a JSON will be printed to the console.") + @Option(names = { "-o", + "--output" }, description = "Destination directory to save the output graphs. By default, the SDG formatted as a JSON will be printed to the console.") private static String output; - @Option(names = {"-b", "--build-cmd"}, description = "Custom build command. Defaults to auto build.") + @Option(names = { "-b", "--build-cmd" }, description = "Custom build command. Defaults to auto build.") private static String build; - @Option(names = {"--no-build"}, description = "Do not build your application. Use this option if you have already built your application.") + @Option(names = { + "--no-build" }, description = "Do not build your application. Use this option if you have already built your application.") private static boolean noBuild = false; - @Option(names = {"--no-clean-dependencies"}, description = "Do not attempt to auto-clean dependencies") + @Option(names = { "--no-clean-dependencies" }, description = "Do not attempt to auto-clean dependencies") public static boolean noCleanDependencies = false; - @Option(names = {"-f", "--project-root-path"}, description = "Path to the root pom.xml/build.gradle file of the project.") + @Option(names = { "-f", + "--project-root-path" }, description = "Path to the root pom.xml/build.gradle file of the project.") public static String projectRootPom; - @Option(names = {"-a", "--analysis-level"}, description = "Level of analysis to perform. Options: 1 (for just symbol table) or 2 (for call graph). Default: 1") + @Option(names = { "-a", + "--analysis-level" }, description = "Level of analysis to perform. Options: 1 (for just symbol table) or 2 (for call graph). Default: 1") private static int analysisLevel = 1; - @Option(names = {"-v", "--verbose"}, description = "Print logs to console.") + @Option(names = { "--include-test-classes" }, hidden = true, description = "Print logs to console.") + public static boolean includeTestClasses = false; + + @Option(names = { "-v", "--verbose" }, description = "Print logs to console.") private static boolean verbose = false; private static final String outputFileName = "analysis.json"; @@ -121,11 +130,13 @@ private static void analyze() throws Exception { JsonObject combinedJsonObject = new JsonObject(); Map symbolTable; projectRootPom = projectRootPom == null ? input : projectRootPom; - // First of all if, sourceAnalysis is provided, we will analyze the source code instead of the project. + // First of all if, sourceAnalysis is provided, we will analyze the source code + // instead of the project. if (sourceAnalysis != null) { // Construct symbol table for source code Log.debug("Single file analysis."); - Pair, Map>> symbolTableExtractionResult = SymbolTable.extractSingle(sourceAnalysis); + Pair, Map>> symbolTableExtractionResult = SymbolTable + .extractSingle(sourceAnalysis); symbolTable = symbolTableExtractionResult.getLeft(); } else { // download library dependencies of project for type resolution @@ -140,9 +151,11 @@ private static void analyze() throws Exception { Log.warn("Failed to download library dependencies of project"); } - boolean analysisFileExists = output != null && Files.exists(Paths.get(output + File.separator + outputFileName)); + boolean analysisFileExists = output != null + && Files.exists(Paths.get(output + File.separator + outputFileName)); - // if target files are specified, compute symbol table information for the given files + // if target files are specified, compute symbol table information for the given + // files if (targetFiles != null) { Log.info(targetFiles.size() + "target files specified for analysis: " + targetFiles); @@ -154,17 +167,22 @@ private static void analyze() throws Exception { } // Previous code was pointing to toList, which has been introduced in Java 16 - // symbolTable = SymbolTable.extract(Paths.get(input), targetFiles.stream().map(Paths::get).toList()).getLeft(); + // symbolTable = SymbolTable.extract(Paths.get(input), + // targetFiles.stream().map(Paths::get).toList()).getLeft(); // extract symbol table for the specified files - symbolTable = SymbolTable.extract(Paths.get(input), targetFiles.stream().map(Paths::get).collect(Collectors.toList())).getLeft(); + symbolTable = SymbolTable + .extract(Paths.get(input), targetFiles.stream().map(Paths::get).collect(Collectors.toList())) + .getLeft(); - // if analysis file exists, update it with new symbol table information for the specified fiels + // if analysis file exists, update it with new symbol table information for the + // specified fiels if (analysisFileExists) { // read symbol table information from existing analysis file Map existingSymbolTable = readSymbolTableFromFile( new File(output, outputFileName)); if (existingSymbolTable != null) { - // for each file, tag its symbol table information as "updated" and update existing symbol table + // for each file, tag its symbol table information as "updated" and update + // existing symbol table for (String targetFile : targetFiles) { String targetPathAbs = Paths.get(targetFile).toAbsolutePath().toString(); JavaCompilationUnit javaCompilationUnit = symbolTable.get(targetPathAbs); @@ -175,16 +193,18 @@ private static void analyze() throws Exception { symbolTable = existingSymbolTable; } } else { - // construct symbol table for project, write parse problems to file in output directory if specified - Pair, Map>> symbolTableExtractionResult - = SymbolTable.extractAll(Paths.get(input)); + // construct symbol table for project, write parse problems to file in output + // directory if specified + Pair, Map>> symbolTableExtractionResult = SymbolTable + .extractAll(Paths.get(input)); symbolTable = symbolTableExtractionResult.getLeft(); } if (analysisLevel > 1) { // Save SDG, and Call graph as JSON - // If noBuild is not true, and build is also not provided, we will use "auto" as the build command + // If noBuild is not true, and build is also not provided, we will use "auto" as + // the build command build = build == null ? "auto" : build; // Is noBuild is true, we will not build the project build = noBuild ? null : build; diff --git a/src/main/java/com/ibm/cldk/utils/BuildProject.java b/src/main/java/com/ibm/cldk/utils/BuildProject.java index afc88ed..b76cc4b 100644 --- a/src/main/java/com/ibm/cldk/utils/BuildProject.java +++ b/src/main/java/com/ibm/cldk/utils/BuildProject.java @@ -1,7 +1,5 @@ package com.ibm.cldk.utils; -import com.ibm.cldk.CodeAnalyzer; - import java.io.BufferedReader; import java.io.File; import java.io.IOException; @@ -17,6 +15,7 @@ import static com.ibm.cldk.utils.ProjectDirectoryScanner.classFilesStream; import static com.ibm.cldk.CodeAnalyzer.projectRootPom; import static com.ibm.cldk.CodeAnalyzer.noCleanDependencies; +import static com.ibm.cldk.CodeAnalyzer.includeTestClasses; public class BuildProject { public static Path libDownloadPath; @@ -140,7 +139,14 @@ private static boolean mavenBuild(String projectPath) { Log.info("Checking if Maven is installed."); return false; } - String[] mavenCommand = {MAVEN_CMD, "clean", "compile", "-f", projectPath + "/pom.xml", "-B", "-V", "-e", "-Drat.skip", "-Dfindbugs.skip", "-Dcheckstyle.skip", "-Dpmd.skip=true", "-Dspotbugs.skip", "-Denforcer.skip", "-Dmaven.javadoc.skip", "-DskipTests", "-Dmaven.test.skip.exec", "-Dlicense.skip=true", "-Drat.skip=true", "-Dspotless.check.skip=true"}; + + String[] mavenCommand; + if (includeTestClasses) { + Log.warn("Hidden flag `--include-test-classes` is turned on. We'll including test classes in WALA analysis"); + mavenCommand = new String[]{MAVEN_CMD, "clean", "test-compile", "-f", projectPath + "/pom.xml", "-B", "-V", "-e", "-Drat.skip", "-Dfindbugs.skip", "-Dcheckstyle.skip", "-Dpmd.skip=true", "-Dspotbugs.skip", "-Denforcer.skip", "-Dmaven.javadoc.skip", "-DskipTests", "-Dmaven.test.skip.exec", "-Dlicense.skip=true", "-Drat.skip=true", "-Dspotless.check.skip=true"}; + } + else + mavenCommand = new String[]{MAVEN_CMD, "clean", "compile", "-f", projectPath + "/pom.xml", "-B", "-V", "-e", "-Drat.skip", "-Dfindbugs.skip", "-Dcheckstyle.skip", "-Dpmd.skip=true", "-Dspotbugs.skip", "-Denforcer.skip", "-Dmaven.javadoc.skip", "-DskipTests", "-Dmaven.test.skip.exec", "-Dlicense.skip=true", "-Drat.skip=true", "-Dspotless.check.skip=true"}; return buildWithTool(mavenCommand); } @@ -151,7 +157,12 @@ public static boolean gradleBuild(String projectPath) { if (GRADLE_CMD.equals("gradlew") || GRADLE_CMD.equals("gradlew.bat")) { gradleCommand = new String[]{projectPath + File.separator + GRADLE_CMD, "clean", "compileJava", "-p", projectPath}; } else { - gradleCommand = new String[]{GRADLE_CMD, "clean", "compileJava", "-p", projectPath}; + if (includeTestClasses) { + Log.warn("Hidden flag `--include-test-classes` is turned on. We'll including test classes in WALA analysis"); + gradleCommand = new String[]{GRADLE_CMD, "clean", "compileTestJava", "-p", projectPath}; + } + else + gradleCommand = new String[]{GRADLE_CMD, "clean", "compileJava", "-p", projectPath}; } return buildWithTool(gradleCommand); } diff --git a/src/main/java/com/ibm/cldk/utils/ProjectDirectoryScanner.java b/src/main/java/com/ibm/cldk/utils/ProjectDirectoryScanner.java index a431482..c10752e 100644 --- a/src/main/java/com/ibm/cldk/utils/ProjectDirectoryScanner.java +++ b/src/main/java/com/ibm/cldk/utils/ProjectDirectoryScanner.java @@ -9,20 +9,15 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import static com.ibm.cldk.CodeAnalyzer.includeTestClasses; + public class ProjectDirectoryScanner { public static List classFilesStream(String projectPath) throws IOException { Path projectDir = Paths.get(projectPath).toAbsolutePath(); Log.info("Finding *.class files in " + projectDir); if (Files.exists(projectDir)) { try (Stream paths = Files.walk(projectDir)) { - return paths.filter(file -> !Files.isDirectory(file) && file.toString().endsWith(".class")) - .filter(file -> { - // Let's find the path relative to the project directory - Path relativePath = projectDir.relativize(file.toAbsolutePath()); - String relativePathAsString = relativePath.toString().replace("\\", "/"); // Windows fix - return !relativePathAsString.contains("test/resources/") && !relativePathAsString.contains("main/resources/"); - }) - .collect(Collectors.toList()); + return paths.filter(file -> !Files.isDirectory(file) && file.toString().endsWith(".class")).collect(Collectors.toList()); } } return null; @@ -47,13 +42,13 @@ public static List sourceFilesStream(String projectPath) throws IOExceptio if (Files.exists(projectDir)) { try (Stream paths = Files.walk(projectDir)) { return paths - .filter(file -> !Files.isDirectory(file)) - .filter(file -> file.toString().endsWith(".java")) - .filter(file -> !file.toAbsolutePath().toString().contains("build/")) - .filter(file -> !file.toAbsolutePath().toString().contains("target/")) - .filter(file -> !file.toAbsolutePath().toString().contains("main/resources/")) - .filter(file -> !file.toAbsolutePath().toString().contains("test/resources/")) - .collect(Collectors.toList()); + .filter(file -> !Files.isDirectory(file)) + .filter(file -> file.toString().endsWith(".java")) + .filter(file -> !file.toAbsolutePath().toString().contains("build/")) + .filter(file -> !file.toAbsolutePath().toString().contains("target/")) + .filter(file -> !file.toAbsolutePath().toString().contains("main/resources/")) + .filter(file -> !file.toAbsolutePath().toString().contains("test/resources/")) + .collect(Collectors.toList()); } } return null;