Skip to content

Fix issue 97, 95, 93, 84, 39 #96

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ repositories {
mavenLocal()
}

java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

if (project.hasProperty('mainClass')) {
mainClassName = project.getProperty('mainClass')
Expand Down Expand Up @@ -119,7 +123,25 @@ dependencies {
implementation('org.jgrapht:jgrapht-ext:1.5.2')
implementation('com.github.javaparser:javaparser-symbol-solver-core:3.25.9')

testImplementation group: 'junit', name: 'junit', version: '4.13.2'
// TestContainers
testImplementation 'org.testcontainers:testcontainers:1.19.3'
testImplementation 'org.testcontainers:junit-jupiter:1.19.3'

// JUnit 5
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.1'
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.10.1' // for @ParameterizedTest
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.1'

// SLF4J - for TestContainers logging
testImplementation 'org.slf4j:slf4j-api:2.0.9'
testImplementation 'org.slf4j:slf4j-simple:2.0.9'

}

test {
useJUnitPlatform()
// Optional: Enable TestContainers reuse to speed up tests
systemProperty 'testcontainers.reuse.enable', 'true'
}

task fatJar(type: Jar) {
Expand Down
3 changes: 2 additions & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
version=1.0.10
version=1.1.0

2 changes: 1 addition & 1 deletion src/main/java/com/ibm/cldk/SystemDependencyGraph.java
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ public static String construct(

// Initialize scope
AnalysisScope scope = ScopeUtils.createScope(input, dependencies, build);
IClassHierarchy cha = ClassHierarchyFactory.make(scope,
IClassHierarchy cha = ClassHierarchyFactory.makeWithRoot(scope,
new ECJClassLoaderFactory(scope.getExclusions()));
Log.done("There were a total of " + cha.getNumberOfClasses() + " classes of which "
+ AnalysisUtils.getNumberOfApplicationClasses(cha) + " are application classes.");
Expand Down
141 changes: 51 additions & 90 deletions src/main/java/com/ibm/cldk/utils/BuildProject.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.MessageFormat;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
Expand All @@ -22,47 +23,33 @@ public class BuildProject {
public static Path libDownloadPath;
private static final String LIB_DEPS_DOWNLOAD_DIR = "_library_dependencies";
private static final String MAVEN_CMD = BuildProject.getMavenCommand();
private static final String GRADLE_CMD = BuildProject.getGradleCmd();
private static final String GRADLE_CMD = BuildProject.getGradleCommand();

/**
* Gets the maven command to be used for building the project.
*
* @return the maven command
*/
private static String getMavenCommand() {
Boolean isWindows = System.getProperty("os.name").toLowerCase().contains("windows");
String mvnCommand;
if (isWindows) {
mvnCommand = new File(projectRootPom, "mvnw.cmd").exists() ? String.valueOf(new File(projectRootPom, "mvnw.cmd")) : "mvn.cmd";
} else {
mvnCommand = new File(projectRootPom, "mvnw").exists() ? String.valueOf(new File(projectRootPom, "mvnw")) : "mvn";
}
return mvnCommand;
public static String getMavenCommand() {
String mvnSystemCommand = Arrays.stream(System.getenv("PATH").split(System.getProperty("path.separator"))).map(path -> new File(path, System.getProperty("os.name").toLowerCase().contains("windows") ? "mvn.cmd" : "mvn")).filter(File::exists).findFirst().map(File::getAbsolutePath).orElse(null);
File mvnWrapper = System.getProperty("os.name").toLowerCase().contains("windows") ? new File(projectRootPom, "mvnw.cmd") : new File(projectRootPom, "mvnw");
return commandExists(mvnWrapper).getKey() ? mvnWrapper.toString() : mvnSystemCommand;
}

/**
* Gets the gradle command to be used for building the project.
*
* @return the gradle command
*/
private static String getGradleCmd() {
String GRADLE_CMD;
String osName = System.getProperty("os.name").toLowerCase();
boolean isWindows = osName.contains("windows");
String gradleWrapper = isWindows ? "gradlew.bat" : "gradlew";
String gradle = isWindows ? "gradle.bat" : "gradle";

String gradleWrapperExists = new File(projectRootPom, gradleWrapper).exists() ? "true" : "false";
public static String getGradleCommand() {
String gradleSystemCommand = Arrays.stream(System.getenv("PATH").split(System.getProperty("path.separator"))).map(path -> new File(path, System.getProperty("os.name").toLowerCase().contains("windows") ? "gradle.bat" : "gradle")).filter(File::exists).findFirst().map(File::getAbsolutePath).orElse(null);
File gradleWrapper = System.getProperty("os.name").toLowerCase().contains("windows") ? new File(projectRootPom, "gradlew.bat") : new File(projectRootPom, "gradlew");

if (new File(projectRootPom, gradleWrapper).exists()) {
GRADLE_CMD = String.valueOf(new File(projectRootPom, gradleWrapper));
} else {
GRADLE_CMD = gradle;
}
return GRADLE_CMD;
return commandExists(gradleWrapper).getKey() ? gradleWrapper.toString() : gradleSystemCommand;
}

public static Path tempInitScript;
public static Path tempInitScript;

static {
try {
tempInitScript = Files.createTempFile("gradle-init-", ".gradle");
Expand All @@ -71,63 +58,39 @@ private static String getGradleCmd() {
}
}

private static final String GRADLE_DEPENDENCIES_TASK = "allprojects { afterEvaluate { project -> task downloadDependencies(type: Copy) {\n" +
" def configs = project.configurations.findAll { it.canBeResolved }\n\n" +
" dependsOn configs\n" +
" from configs\n" +
" into project.hasProperty('outputDir') ? project.property('outputDir') : \"${project.buildDir}/libs\"\n\n" +
" doFirst {\n" +
" println \"Downloading dependencies for project ${project.name} to: ${destinationDir}\"\n" +
" configs.each { config ->\n" +
" println \"Configuration: ${config.name}\"\n" +
" config.resolvedConfiguration.resolvedArtifacts.each { artifact ->\n" +
" println \"\t${artifact.moduleVersion.id}:${artifact.extension}\"\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
private static final String GRADLE_DEPENDENCIES_TASK = "allprojects { afterEvaluate { project -> task downloadDependencies(type: Copy) {\n" + " def configs = project.configurations.findAll { it.canBeResolved }\n\n" + " dependsOn configs\n" + " from configs\n" + " into project.hasProperty('outputDir') ? project.property('outputDir') : \"${project.buildDir}/libs\"\n\n" + " doFirst {\n" + " println \"Downloading dependencies for project ${project.name} to: ${destinationDir}\"\n" + " configs.each { config ->\n" + " println \"Configuration: ${config.name}\"\n" + " config.resolvedConfiguration.resolvedArtifacts.each { artifact ->\n" + " println \"\t${artifact.moduleVersion.id}:${artifact.extension}\"\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}";

private static AbstractMap.SimpleEntry<Boolean, String> commandExists(String command) {
private static AbstractMap.SimpleEntry<Boolean, String> commandExists(File command) {
StringBuilder output = new StringBuilder();
if (!command.exists()) {
return new AbstractMap.SimpleEntry<>(false, MessageFormat.format("Command {0} does not exist.", command));
}
try {
Process process = new ProcessBuilder().directory(new File(projectRootPom)).command(command, "--version").start();
Process process = new ProcessBuilder().directory(new File(projectRootPom)).command(String.valueOf(command), "--version").start();
// Read the output stream
BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream())
);
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
output.append(line).append("\n");
}

// Read the error stream
BufferedReader errorReader = new BufferedReader(
new InputStreamReader(process.getErrorStream())
);
BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
while ((line = errorReader.readLine()) != null) {
output.append(line).append("\n");
}


int exitCode = process.waitFor();
return new AbstractMap.SimpleEntry<>(
exitCode == 0,
output.toString().trim()
);
return new AbstractMap.SimpleEntry<>(exitCode == 0, output.toString().trim());
} catch (IOException | InterruptedException exceptions) {
return new AbstractMap.SimpleEntry<>(
false,
exceptions.getMessage()
);
Log.error(exceptions.getMessage());
return new AbstractMap.SimpleEntry<>(false, exceptions.getMessage());
}
}

private static boolean buildWithTool(String[] buildCommand) {
Log.info("Building the project using " + buildCommand[0] + ".");
ProcessBuilder processBuilder = new ProcessBuilder().directory(new File(projectRootPom)).command(buildCommand);

try {
Process process = processBuilder.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
Expand Down Expand Up @@ -157,7 +120,6 @@ private static boolean isMavenInstalled() {
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = reader.readLine(); // Read the first line of the output
if (line != null && line.contains("Apache Maven")) {
Log.info("Maven is installed: " + line);
return true;
}
} catch (IOException e) {
Expand All @@ -179,11 +141,7 @@ 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 = {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);
}
Expand All @@ -193,8 +151,7 @@ public static boolean gradleBuild(String projectPath) {
String[] gradleCommand;
if (GRADLE_CMD.equals("gradlew") || GRADLE_CMD.equals("gradlew.bat")) {
gradleCommand = new String[]{projectPath + File.separator + GRADLE_CMD, "clean", "compileJava", "-p", projectPath};
}
else {
} else {
gradleCommand = new String[]{GRADLE_CMD, "clean", "compileJava", "-p", projectPath};
}
return buildWithTool(gradleCommand);
Expand Down Expand Up @@ -252,31 +209,38 @@ public static boolean downloadLibraryDependencies(String projectPath, String pro
}
File pomFile = new File(projectRoot, "pom.xml");
if (pomFile.exists()) {
if (MAVEN_CMD == null || !commandExists(new File(MAVEN_CMD)).getKey()) {
String msg = MAVEN_CMD == null ?
"Could not find Maven or a valid Maven Wrapper" :
MessageFormat.format("Could not verify that {0} exists", MAVEN_CMD);
Log.error(msg);
throw new IllegalStateException("Unable to execute Maven command. " +
(MAVEN_CMD == null ?
"Could not find Maven or a valid Maven Wrapper" :
"Attempt failed with message\n" + commandExists(new File(MAVEN_CMD)).getValue()
));
}
Log.info("Found pom.xml in the project directory. Using Maven to download dependencies.");
AbstractMap.SimpleEntry<Boolean, String> mavenCheck = commandExists(MAVEN_CMD);
if (!mavenCheck.getKey())
throw new IllegalStateException("Unable to execute Maven command. Attempt failed with message\n" + mavenCheck.getValue());

String[] mavenCommand = {
MAVEN_CMD, "--no-transfer-progress", "-f",
Paths.get(projectRoot, "pom.xml").toString(),
"dependency:copy-dependencies",
"-DoutputDirectory=" + libDownloadPath.toString()
};
String[] mavenCommand = {MAVEN_CMD, "--no-transfer-progress", "-f", Paths.get(projectRoot, "pom.xml").toString(), "dependency:copy-dependencies", "-DoutputDirectory=" + libDownloadPath.toString()};
return buildWithTool(mavenCommand);
} else if (new File(projectRoot, "build.gradle").exists() || new File(projectRoot, "build.gradle.kts").exists()) {
Log.info("Found build.gradle or build.gradle.kts in the project directory. Using gradle to download dependencies.");
AbstractMap.SimpleEntry<Boolean, String> gradleCheck = commandExists(GRADLE_CMD);
if (!gradleCheck.getKey())
throw new IllegalStateException("Could not execute Gradle command. Attempt failed with message\n" + gradleCheck.getValue());

Log.info("Found build.gradle[.kts] in the project directory. Using Gradle to download dependencies.");
if (GRADLE_CMD == null || !commandExists(new File(GRADLE_CMD)).getKey()) {
String msg = GRADLE_CMD == null ?
"Could not find Gradle or valid Gradle Wrapper" :
MessageFormat.format("Could not verify that {0} exists", GRADLE_CMD);
Log.error(msg);
throw new IllegalStateException("Unable to execute Maven command. " +
(GRADLE_CMD == null ?
"Could not find Gradle or valid Gradle Wrapper" :
"Attempt failed with message\n" + commandExists(new File(GRADLE_CMD)).getValue()
));
}
Log.info("Found build.gradle or build.gradle.kts in the project directory. Using Gradle to download dependencies.");
tempInitScript = Files.writeString(tempInitScript, GRADLE_DEPENDENCIES_TASK);
String[] gradleCommand;
if (GRADLE_CMD.equals("gradlew") || GRADLE_CMD.equals("gradlew.bat")) {
gradleCommand = new String[]{projectRoot + File.separator + GRADLE_CMD, "--init-script", tempInitScript.toFile().getAbsolutePath(), "downloadDependencies", "-PoutputDir=" + libDownloadPath.toString()};
}
else {
} else {
gradleCommand = new String[]{GRADLE_CMD, "--init-script", tempInitScript.toFile().getAbsolutePath(), "downloadDependencies", "-PoutputDir=" + libDownloadPath.toString()};
}
return buildWithTool(gradleCommand);
Expand All @@ -288,10 +252,7 @@ public static void cleanLibraryDependencies() {
if (libDownloadPath != null) {
Log.info("Cleaning up library dependency directory: " + libDownloadPath);
try {
Files.walk(libDownloadPath)
.filter(Files::isRegularFile)
.map(Path::toFile)
.forEach(File::delete);
Files.walk(libDownloadPath).filter(Files::isRegularFile).map(Path::toFile).forEach(File::delete);
Files.delete(libDownloadPath);
} catch (IOException e) {
Log.error("Error deleting library dependency directory: " + e.getMessage());
Expand Down
17 changes: 16 additions & 1 deletion src/main/java/com/ibm/cldk/utils/ScopeUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileVisitOption;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.jar.JarFile;
import java.util.stream.Stream;

import org.apache.commons.io.FileUtils;

Expand Down Expand Up @@ -70,7 +72,7 @@ public static AnalysisScope createScope(String projectPath, String applicationDe
throw new RuntimeException("JAVA_HOME is not set.");
}

String[] stdlibs = Files.walk(Paths.get(System.getenv("JAVA_HOME"), "jmods"))
String[] stdlibs = Files.walk(getJmodsPath())
.filter(path -> path.toString().endsWith(".jmod"))
.map(path -> path.toAbsolutePath().toString())
.toArray(String[]::new);
Expand Down Expand Up @@ -130,6 +132,19 @@ public static AnalysisScope createScope(String projectPath, String applicationDe
return scope;
}

private static Path getJmodsPath() {
try {
try (Stream<Path> paths = Files.walk(Path.of(System.getenv("JAVA_HOME")), Integer.MAX_VALUE, FileVisitOption.FOLLOW_LINKS)) {
return paths
.filter(path -> path.getFileName().toString().equals("jmods"))
.findFirst()
.orElseThrow(() -> new RuntimeException("jmods directory not found in " + System.getenv("JAVA_HOME")));
}
} catch (IOException e) {
throw new RuntimeException("Error searching for jmods directory", e);
}
}

private static AnalysisScope addDefaultExclusions(AnalysisScope scope)
throws IOException {
Log.info("Add exclusions to scope.");
Expand Down
Loading