Skip to content

Commit 2abc7d8

Browse files
authored
Merge pull request #96 from IBM/fix-issue-95
Fix issue 95
2 parents 65ebcf2 + 575f6a7 commit 2abc7d8

File tree

76 files changed

+2108
-118
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+2108
-118
lines changed

build.gradle

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ repositories {
2929
mavenLocal()
3030
}
3131

32+
java {
33+
sourceCompatibility = JavaVersion.VERSION_17
34+
targetCompatibility = JavaVersion.VERSION_17
35+
}
3236

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

122-
testImplementation group: 'junit', name: 'junit', version: '4.13.2'
126+
// TestContainers
127+
testImplementation 'org.testcontainers:testcontainers:1.19.3'
128+
testImplementation 'org.testcontainers:junit-jupiter:1.19.3'
129+
130+
// JUnit 5
131+
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.1'
132+
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.10.1' // for @ParameterizedTest
133+
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.1'
134+
135+
// SLF4J - for TestContainers logging
136+
testImplementation 'org.slf4j:slf4j-api:2.0.9'
137+
testImplementation 'org.slf4j:slf4j-simple:2.0.9'
138+
139+
}
140+
141+
test {
142+
useJUnitPlatform()
143+
// Optional: Enable TestContainers reuse to speed up tests
144+
systemProperty 'testcontainers.reuse.enable', 'true'
123145
}
124146

125147
task fatJar(type: Jar) {

gradle.properties

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
version=1.0.10
1+
version=1.1.0
2+

src/main/java/com/ibm/cldk/SystemDependencyGraph.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ public static String construct(
206206

207207
// Initialize scope
208208
AnalysisScope scope = ScopeUtils.createScope(input, dependencies, build);
209-
IClassHierarchy cha = ClassHierarchyFactory.make(scope,
209+
IClassHierarchy cha = ClassHierarchyFactory.makeWithRoot(scope,
210210
new ECJClassLoaderFactory(scope.getExclusions()));
211211
Log.done("There were a total of " + cha.getNumberOfClasses() + " classes of which "
212212
+ AnalysisUtils.getNumberOfApplicationClasses(cha) + " are application classes.");

src/main/java/com/ibm/cldk/utils/BuildProject.java

Lines changed: 51 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.nio.file.Files;
1010
import java.nio.file.Path;
1111
import java.nio.file.Paths;
12+
import java.text.MessageFormat;
1213
import java.util.AbstractMap;
1314
import java.util.ArrayList;
1415
import java.util.Arrays;
@@ -22,47 +23,33 @@ public class BuildProject {
2223
public static Path libDownloadPath;
2324
private static final String LIB_DEPS_DOWNLOAD_DIR = "_library_dependencies";
2425
private static final String MAVEN_CMD = BuildProject.getMavenCommand();
25-
private static final String GRADLE_CMD = BuildProject.getGradleCmd();
26+
private static final String GRADLE_CMD = BuildProject.getGradleCommand();
2627

2728
/**
2829
* Gets the maven command to be used for building the project.
2930
*
3031
* @return the maven command
3132
*/
32-
private static String getMavenCommand() {
33-
Boolean isWindows = System.getProperty("os.name").toLowerCase().contains("windows");
34-
String mvnCommand;
35-
if (isWindows) {
36-
mvnCommand = new File(projectRootPom, "mvnw.cmd").exists() ? String.valueOf(new File(projectRootPom, "mvnw.cmd")) : "mvn.cmd";
37-
} else {
38-
mvnCommand = new File(projectRootPom, "mvnw").exists() ? String.valueOf(new File(projectRootPom, "mvnw")) : "mvn";
39-
}
40-
return mvnCommand;
33+
public static String getMavenCommand() {
34+
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);
35+
File mvnWrapper = System.getProperty("os.name").toLowerCase().contains("windows") ? new File(projectRootPom, "mvnw.cmd") : new File(projectRootPom, "mvnw");
36+
return commandExists(mvnWrapper).getKey() ? mvnWrapper.toString() : mvnSystemCommand;
4137
}
4238

4339
/**
4440
* Gets the gradle command to be used for building the project.
4541
*
4642
* @return the gradle command
4743
*/
48-
private static String getGradleCmd() {
49-
String GRADLE_CMD;
50-
String osName = System.getProperty("os.name").toLowerCase();
51-
boolean isWindows = osName.contains("windows");
52-
String gradleWrapper = isWindows ? "gradlew.bat" : "gradlew";
53-
String gradle = isWindows ? "gradle.bat" : "gradle";
54-
55-
String gradleWrapperExists = new File(projectRootPom, gradleWrapper).exists() ? "true" : "false";
44+
public static String getGradleCommand() {
45+
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);
46+
File gradleWrapper = System.getProperty("os.name").toLowerCase().contains("windows") ? new File(projectRootPom, "gradlew.bat") : new File(projectRootPom, "gradlew");
5647

57-
if (new File(projectRootPom, gradleWrapper).exists()) {
58-
GRADLE_CMD = String.valueOf(new File(projectRootPom, gradleWrapper));
59-
} else {
60-
GRADLE_CMD = gradle;
61-
}
62-
return GRADLE_CMD;
48+
return commandExists(gradleWrapper).getKey() ? gradleWrapper.toString() : gradleSystemCommand;
6349
}
6450

65-
public static Path tempInitScript;
51+
public static Path tempInitScript;
52+
6653
static {
6754
try {
6855
tempInitScript = Files.createTempFile("gradle-init-", ".gradle");
@@ -71,63 +58,39 @@ private static String getGradleCmd() {
7158
}
7259
}
7360

74-
private static final String GRADLE_DEPENDENCIES_TASK = "allprojects { afterEvaluate { project -> task downloadDependencies(type: Copy) {\n" +
75-
" def configs = project.configurations.findAll { it.canBeResolved }\n\n" +
76-
" dependsOn configs\n" +
77-
" from configs\n" +
78-
" into project.hasProperty('outputDir') ? project.property('outputDir') : \"${project.buildDir}/libs\"\n\n" +
79-
" doFirst {\n" +
80-
" println \"Downloading dependencies for project ${project.name} to: ${destinationDir}\"\n" +
81-
" configs.each { config ->\n" +
82-
" println \"Configuration: ${config.name}\"\n" +
83-
" config.resolvedConfiguration.resolvedArtifacts.each { artifact ->\n" +
84-
" println \"\t${artifact.moduleVersion.id}:${artifact.extension}\"\n" +
85-
" }\n" +
86-
" }\n" +
87-
" }\n" +
88-
" }\n" +
89-
" }\n" +
90-
"}";
61+
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" + "}";
9162

92-
private static AbstractMap.SimpleEntry<Boolean, String> commandExists(String command) {
63+
private static AbstractMap.SimpleEntry<Boolean, String> commandExists(File command) {
9364
StringBuilder output = new StringBuilder();
65+
if (!command.exists()) {
66+
return new AbstractMap.SimpleEntry<>(false, MessageFormat.format("Command {0} does not exist.", command));
67+
}
9468
try {
95-
Process process = new ProcessBuilder().directory(new File(projectRootPom)).command(command, "--version").start();
69+
Process process = new ProcessBuilder().directory(new File(projectRootPom)).command(String.valueOf(command), "--version").start();
9670
// Read the output stream
97-
BufferedReader reader = new BufferedReader(
98-
new InputStreamReader(process.getInputStream())
99-
);
71+
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
10072
String line;
10173
while ((line = reader.readLine()) != null) {
10274
output.append(line).append("\n");
10375
}
10476

10577
// Read the error stream
106-
BufferedReader errorReader = new BufferedReader(
107-
new InputStreamReader(process.getErrorStream())
108-
);
78+
BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
10979
while ((line = errorReader.readLine()) != null) {
11080
output.append(line).append("\n");
11181
}
11282

113-
11483
int exitCode = process.waitFor();
115-
return new AbstractMap.SimpleEntry<>(
116-
exitCode == 0,
117-
output.toString().trim()
118-
);
84+
return new AbstractMap.SimpleEntry<>(exitCode == 0, output.toString().trim());
11985
} catch (IOException | InterruptedException exceptions) {
120-
return new AbstractMap.SimpleEntry<>(
121-
false,
122-
exceptions.getMessage()
123-
);
86+
Log.error(exceptions.getMessage());
87+
return new AbstractMap.SimpleEntry<>(false, exceptions.getMessage());
12488
}
12589
}
12690

12791
private static boolean buildWithTool(String[] buildCommand) {
12892
Log.info("Building the project using " + buildCommand[0] + ".");
12993
ProcessBuilder processBuilder = new ProcessBuilder().directory(new File(projectRootPom)).command(buildCommand);
130-
13194
try {
13295
Process process = processBuilder.start();
13396
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
@@ -157,7 +120,6 @@ private static boolean isMavenInstalled() {
157120
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
158121
String line = reader.readLine(); // Read the first line of the output
159122
if (line != null && line.contains("Apache Maven")) {
160-
Log.info("Maven is installed: " + line);
161123
return true;
162124
}
163125
} catch (IOException e) {
@@ -179,11 +141,7 @@ private static boolean mavenBuild(String projectPath) {
179141
Log.info("Checking if Maven is installed.");
180142
return false;
181143
}
182-
String[] mavenCommand = {
183-
MAVEN_CMD, "clean", "compile", "-f", projectPath + "/pom.xml", "-B", "-V", "-e", "-Drat.skip",
184-
"-Dfindbugs.skip", "-Dcheckstyle.skip", "-Dpmd.skip=true", "-Dspotbugs.skip", "-Denforcer.skip",
185-
"-Dmaven.javadoc.skip", "-DskipTests", "-Dmaven.test.skip.exec", "-Dlicense.skip=true",
186-
"-Drat.skip=true", "-Dspotless.check.skip=true"};
144+
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"};
187145

188146
return buildWithTool(mavenCommand);
189147
}
@@ -193,8 +151,7 @@ public static boolean gradleBuild(String projectPath) {
193151
String[] gradleCommand;
194152
if (GRADLE_CMD.equals("gradlew") || GRADLE_CMD.equals("gradlew.bat")) {
195153
gradleCommand = new String[]{projectPath + File.separator + GRADLE_CMD, "clean", "compileJava", "-p", projectPath};
196-
}
197-
else {
154+
} else {
198155
gradleCommand = new String[]{GRADLE_CMD, "clean", "compileJava", "-p", projectPath};
199156
}
200157
return buildWithTool(gradleCommand);
@@ -252,31 +209,38 @@ public static boolean downloadLibraryDependencies(String projectPath, String pro
252209
}
253210
File pomFile = new File(projectRoot, "pom.xml");
254211
if (pomFile.exists()) {
212+
if (MAVEN_CMD == null || !commandExists(new File(MAVEN_CMD)).getKey()) {
213+
String msg = MAVEN_CMD == null ?
214+
"Could not find Maven or a valid Maven Wrapper" :
215+
MessageFormat.format("Could not verify that {0} exists", MAVEN_CMD);
216+
Log.error(msg);
217+
throw new IllegalStateException("Unable to execute Maven command. " +
218+
(MAVEN_CMD == null ?
219+
"Could not find Maven or a valid Maven Wrapper" :
220+
"Attempt failed with message\n" + commandExists(new File(MAVEN_CMD)).getValue()
221+
));
222+
}
255223
Log.info("Found pom.xml in the project directory. Using Maven to download dependencies.");
256-
AbstractMap.SimpleEntry<Boolean, String> mavenCheck = commandExists(MAVEN_CMD);
257-
if (!mavenCheck.getKey())
258-
throw new IllegalStateException("Unable to execute Maven command. Attempt failed with message\n" + mavenCheck.getValue());
259-
260-
String[] mavenCommand = {
261-
MAVEN_CMD, "--no-transfer-progress", "-f",
262-
Paths.get(projectRoot, "pom.xml").toString(),
263-
"dependency:copy-dependencies",
264-
"-DoutputDirectory=" + libDownloadPath.toString()
265-
};
224+
String[] mavenCommand = {MAVEN_CMD, "--no-transfer-progress", "-f", Paths.get(projectRoot, "pom.xml").toString(), "dependency:copy-dependencies", "-DoutputDirectory=" + libDownloadPath.toString()};
266225
return buildWithTool(mavenCommand);
267226
} else if (new File(projectRoot, "build.gradle").exists() || new File(projectRoot, "build.gradle.kts").exists()) {
268-
Log.info("Found build.gradle or build.gradle.kts in the project directory. Using gradle to download dependencies.");
269-
AbstractMap.SimpleEntry<Boolean, String> gradleCheck = commandExists(GRADLE_CMD);
270-
if (!gradleCheck.getKey())
271-
throw new IllegalStateException("Could not execute Gradle command. Attempt failed with message\n" + gradleCheck.getValue());
272-
273-
Log.info("Found build.gradle[.kts] in the project directory. Using Gradle to download dependencies.");
227+
if (GRADLE_CMD == null || !commandExists(new File(GRADLE_CMD)).getKey()) {
228+
String msg = GRADLE_CMD == null ?
229+
"Could not find Gradle or valid Gradle Wrapper" :
230+
MessageFormat.format("Could not verify that {0} exists", GRADLE_CMD);
231+
Log.error(msg);
232+
throw new IllegalStateException("Unable to execute Maven command. " +
233+
(GRADLE_CMD == null ?
234+
"Could not find Gradle or valid Gradle Wrapper" :
235+
"Attempt failed with message\n" + commandExists(new File(GRADLE_CMD)).getValue()
236+
));
237+
}
238+
Log.info("Found build.gradle or build.gradle.kts in the project directory. Using Gradle to download dependencies.");
274239
tempInitScript = Files.writeString(tempInitScript, GRADLE_DEPENDENCIES_TASK);
275240
String[] gradleCommand;
276241
if (GRADLE_CMD.equals("gradlew") || GRADLE_CMD.equals("gradlew.bat")) {
277242
gradleCommand = new String[]{projectRoot + File.separator + GRADLE_CMD, "--init-script", tempInitScript.toFile().getAbsolutePath(), "downloadDependencies", "-PoutputDir=" + libDownloadPath.toString()};
278-
}
279-
else {
243+
} else {
280244
gradleCommand = new String[]{GRADLE_CMD, "--init-script", tempInitScript.toFile().getAbsolutePath(), "downloadDependencies", "-PoutputDir=" + libDownloadPath.toString()};
281245
}
282246
return buildWithTool(gradleCommand);
@@ -288,10 +252,7 @@ public static void cleanLibraryDependencies() {
288252
if (libDownloadPath != null) {
289253
Log.info("Cleaning up library dependency directory: " + libDownloadPath);
290254
try {
291-
Files.walk(libDownloadPath)
292-
.filter(Files::isRegularFile)
293-
.map(Path::toFile)
294-
.forEach(File::delete);
255+
Files.walk(libDownloadPath).filter(Files::isRegularFile).map(Path::toFile).forEach(File::delete);
295256
Files.delete(libDownloadPath);
296257
} catch (IOException e) {
297258
Log.error("Error deleting library dependency directory: " + e.getMessage());

src/main/java/com/ibm/cldk/utils/ScopeUtils.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,15 @@
2121
import java.io.ByteArrayInputStream;
2222
import java.io.IOException;
2323
import java.nio.charset.StandardCharsets;
24+
import java.nio.file.FileVisitOption;
2425
import java.nio.file.Files;
2526
import java.nio.file.Path;
2627
import java.nio.file.Paths;
2728
import java.util.List;
2829
import java.util.Objects;
2930
import java.util.UUID;
3031
import java.util.jar.JarFile;
32+
import java.util.stream.Stream;
3133

3234
import org.apache.commons.io.FileUtils;
3335

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

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

135+
private static Path getJmodsPath() {
136+
try {
137+
try (Stream<Path> paths = Files.walk(Path.of(System.getenv("JAVA_HOME")), Integer.MAX_VALUE, FileVisitOption.FOLLOW_LINKS)) {
138+
return paths
139+
.filter(path -> path.getFileName().toString().equals("jmods"))
140+
.findFirst()
141+
.orElseThrow(() -> new RuntimeException("jmods directory not found in " + System.getenv("JAVA_HOME")));
142+
}
143+
} catch (IOException e) {
144+
throw new RuntimeException("Error searching for jmods directory", e);
145+
}
146+
}
147+
133148
private static AnalysisScope addDefaultExclusions(AnalysisScope scope)
134149
throws IOException {
135150
Log.info("Add exclusions to scope.");

0 commit comments

Comments
 (0)