Skip to content

Commit

Permalink
Merge pull request #96 from IBM/fix-issue-95
Browse files Browse the repository at this point in the history
Fix issue 95
  • Loading branch information
rofrano authored Jan 24, 2025
2 parents 65ebcf2 + 575f6a7 commit 2abc7d8
Show file tree
Hide file tree
Showing 76 changed files with 2,108 additions and 118 deletions.
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

0 comments on commit 2abc7d8

Please sign in to comment.