Skip to content

Commit

Permalink
feat: Populate java compiler args (#109)
Browse files Browse the repository at this point in the history
Co-authored-by: Arthur McGibbon <arthur.mcgibbon@h3im.com>
  • Loading branch information
Arthurm1 and Arthur McGibbon authored Dec 11, 2023
1 parent 25c1141 commit b77ecfd
Show file tree
Hide file tree
Showing 16 changed files with 346 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,23 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.LinkedList;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.gradle.api.Project;
import org.gradle.api.file.Directory;
import org.gradle.api.file.FileCollection;
import org.gradle.api.file.SourceDirectorySet;
import org.gradle.api.internal.tasks.compile.DefaultJavaCompileSpec;
import org.gradle.api.internal.tasks.compile.JavaCompilerArgumentsBuilder;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.SourceSetContainer;
Expand Down Expand Up @@ -116,9 +122,11 @@ public Object buildAll(String modelName, Project rootProject) {
gradleSourceSet.setJavaHome(defaultJavaHome);
gradleSourceSet.setJavaVersion(javaVersion);
gradleSourceSet.setGradleVersion(gradleVersion);
gradleSourceSet.setSourceCompatibility(getSourceCompatibility(project, sourceSet));
gradleSourceSet.setTargetCompatibility(getTargetCompatibility(project, sourceSet));
gradleSourceSet.setCompilerArgs(getCompilerArgs(project, sourceSet));
gradleSourceSet.setSourceCompatibility(
getSourceCompatibility(gradleSourceSet.getCompilerArgs()));
gradleSourceSet.setTargetCompatibility(
getTargetCompatibility(gradleSourceSet.getCompilerArgs()));
gradleSourceSets.add(gradleSourceSet);

// tests
Expand Down Expand Up @@ -344,28 +352,75 @@ private File getSourceOutputDir(SourceSet sourceSet) {
return null;
}

private Optional<String> findCompilerArg(List<String> compilerArgs, String arg) {
int idx = compilerArgs.indexOf(arg);
if (idx >= 0 && idx < compilerArgs.size() - 1) {
return Optional.of(compilerArgs.get(idx + 1));
}
return Optional.empty();
}

private Optional<String> findFirstCompilerArgMatch(List<String> compilerArgs,
Stream<String> args) {
return args.map(arg -> findCompilerArg(compilerArgs, arg))
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst();
}

/**
* Get the source compatibility level of the source set.
*/
private String getSourceCompatibility(Project project, SourceSet sourceSet) {
JavaCompile javaCompile = getJavaCompileTask(project, sourceSet);
if (javaCompile != null) {
return javaCompile.getSourceCompatibility();
}

return "";
private String getSourceCompatibility(List<String> compilerArgs) {
return findFirstCompilerArgMatch(compilerArgs,
Stream.of("-source", "--source", "--release"))
.orElse("");
}

/**
* Get the target compatibility level of the source set.
*/
private String getTargetCompatibility(Project project, SourceSet sourceSet) {
JavaCompile javaCompile = getJavaCompileTask(project, sourceSet);
if (javaCompile != null) {
return javaCompile.getTargetCompatibility();
private String getTargetCompatibility(List<String> compilerArgs) {
return findFirstCompilerArgMatch(compilerArgs,
Stream.of("-target", "--target", "--release"))
.orElse("");
}

private DefaultJavaCompileSpec getJavaCompileSpec(JavaCompile javaCompile) {
CompileOptions options = javaCompile.getOptions();

DefaultJavaCompileSpec specs = new DefaultJavaCompileSpec();
specs.setCompileOptions(options);

// check the project hasn't already got the target or source defined in the
// compiler args so they're not overwritten below
List<String> originalArgs = options.getCompilerArgs();
String argsSourceCompatibility = getSourceCompatibility(originalArgs);
String argsTargetCompatibility = getTargetCompatibility(originalArgs);

if (!argsSourceCompatibility.isEmpty() && !argsTargetCompatibility.isEmpty()) {
return specs;
}

return "";
if (GradleVersion.current().compareTo(GradleVersion.version("6.6")) >= 0) {
if (options.getRelease().isPresent()) {
specs.setRelease(options.getRelease().get());
return specs;
}
}
if (argsSourceCompatibility.isEmpty() && specs.getSourceCompatibility() == null) {
String sourceCompatibility = javaCompile.getSourceCompatibility();
if (sourceCompatibility != null) {
specs.setSourceCompatibility(sourceCompatibility);
}
}
if (argsTargetCompatibility.isEmpty() && specs.getTargetCompatibility() == null) {
String targetCompatibility = javaCompile.getTargetCompatibility();
if (targetCompatibility != null) {
specs.setTargetCompatibility(targetCompatibility);
}
}
return specs;
}

/**
Expand All @@ -374,7 +429,29 @@ private String getTargetCompatibility(Project project, SourceSet sourceSet) {
private List<String> getCompilerArgs(Project project, SourceSet sourceSet) {
JavaCompile javaCompile = getJavaCompileTask(project, sourceSet);
if (javaCompile != null) {
return javaCompile.getOptions().getCompilerArgs();
CompileOptions options = javaCompile.getOptions();

try {
DefaultJavaCompileSpec specs = getJavaCompileSpec(javaCompile);

JavaCompilerArgumentsBuilder builder = new JavaCompilerArgumentsBuilder(specs)
.includeMainOptions(true)
.includeClasspath(false)
.includeSourceFiles(false)
.includeLauncherOptions(false);
return builder.build();
} catch (Exception e) {
// DefaultJavaCompileSpec and JavaCompilerArgumentsBuilder are internal so may not exist.
// Fallback to returning just the compiler arguments the build has specified.
// This will miss a lot of arguments derived from the CompileOptions e.g. sourceCompatibilty
// Arguments must be cast and converted to String because Groovy can use GStringImpl
// which then throws IllegalArgumentException when passed back over the tooling connection.
List<Object> compilerArgs = new LinkedList<>(options.getCompilerArgs());
return compilerArgs
.stream()
.map(Object::toString)
.collect(Collectors.toList());
}
}

return Collections.emptyList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package com.microsoft.java.bs.gradle.plugin;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

Expand Down Expand Up @@ -155,4 +156,191 @@ void testSourceInference() throws IOException {
assertEquals(4, generatedSourceDirCount);
}
}

@Test
void testJavaCompilerArgs1() throws IOException {
File projectDir = projectPath.resolve("java-compilerargs-1").toFile();
GradleConnector connector = GradleConnector.newConnector().forProjectDirectory(projectDir);
connector.useBuildDistribution();
try (ProjectConnection connect = connector.connect()) {
ModelBuilder<GradleSourceSets> modelBuilder = connect.model(GradleSourceSets.class);
File initScript = PluginHelper.getInitScript();
modelBuilder.addArguments("--init-script", initScript.getAbsolutePath());
GradleSourceSets gradleSourceSets = modelBuilder.get();
assertEquals(2, gradleSourceSets.getGradleSourceSets().size());
for (GradleSourceSet gradleSourceSet : gradleSourceSets.getGradleSourceSets()) {
String args = "|" + String.join("|", gradleSourceSet.getCompilerArgs());
assertFalse(args.contains("|--source|"), () -> "Available args: " + args);
assertFalse(args.contains("|--target|"), () -> "Available args: " + args);
assertFalse(args.contains("|--release|"), () -> "Available args: " + args);
assertTrue(args.contains("|-source|1.8"), () -> "Available args: " + args);
assertTrue(args.contains("|-target|9"), () -> "Available args: " + args);
assertTrue(args.contains("|-Xlint:all"), () -> "Available args: " + args);
assertEquals("1.8", gradleSourceSet.getSourceCompatibility(),
() -> "Available args: " + args);
assertEquals("9", gradleSourceSet.getTargetCompatibility(),
() -> "Available args: " + args);
}
}
}

@Test
void testJavaCompilerArgs2() throws IOException {
File projectDir = projectPath.resolve("java-compilerargs-2").toFile();
GradleConnector connector = GradleConnector.newConnector().forProjectDirectory(projectDir);
connector.useBuildDistribution();
try (ProjectConnection connect = connector.connect()) {
ModelBuilder<GradleSourceSets> modelBuilder = connect.model(GradleSourceSets.class);
File initScript = PluginHelper.getInitScript();
modelBuilder.addArguments("--init-script", initScript.getAbsolutePath());
GradleSourceSets gradleSourceSets = modelBuilder.get();
assertEquals(2, gradleSourceSets.getGradleSourceSets().size());
for (GradleSourceSet gradleSourceSet : gradleSourceSets.getGradleSourceSets()) {
String args = "|" + String.join("|", gradleSourceSet.getCompilerArgs());
assertFalse(args.contains("|--source|"), () -> "Available args: " + args);
assertFalse(args.contains("|--target|"), () -> "Available args: " + args);
assertFalse(args.contains("|-source|"), () -> "Available args: " + args);
assertFalse(args.contains("|-target|"), () -> "Available args: " + args);
assertTrue(args.contains("|--release|9"), () -> "Available args: " + args);
assertTrue(args.contains("|-Xlint:all"), () -> "Available args: " + args);
assertEquals("9", gradleSourceSet.getSourceCompatibility(),
() -> "Available args: " + args);
assertEquals("9", gradleSourceSet.getTargetCompatibility(),
() -> "Available args: " + args);
}
}
}

@Test
void testJavaCompilerArgs3() throws IOException {
File projectDir = projectPath.resolve("java-compilerargs-3").toFile();
GradleConnector connector = GradleConnector.newConnector().forProjectDirectory(projectDir);
connector.useBuildDistribution();
try (ProjectConnection connect = connector.connect()) {
ModelBuilder<GradleSourceSets> modelBuilder = connect.model(GradleSourceSets.class);
File initScript = PluginHelper.getInitScript();
modelBuilder.addArguments("--init-script", initScript.getAbsolutePath());
GradleSourceSets gradleSourceSets = modelBuilder.get();
assertEquals(2, gradleSourceSets.getGradleSourceSets().size());
for (GradleSourceSet gradleSourceSet : gradleSourceSets.getGradleSourceSets()) {
String args = "|" + String.join("|", gradleSourceSet.getCompilerArgs());
assertFalse(args.contains("|--source|"), () -> "Available args: " + args);
assertFalse(args.contains("|--target|"), () -> "Available args: " + args);
assertFalse(args.contains("|-source|"), () -> "Available args: " + args);
assertFalse(args.contains("|-target|"), () -> "Available args: " + args);
assertTrue(args.contains("|--release|9"), () -> "Available args: " + args);
assertTrue(args.contains("|-Xlint:all"), () -> "Available args: " + args);
assertEquals("9", gradleSourceSet.getSourceCompatibility(),
() -> "Available args: " + args);
assertEquals("9", gradleSourceSet.getTargetCompatibility(),
() -> "Available args: " + args);
}
}
}

@Test
void testJavaCompilerArgs4() throws IOException {
File projectDir = projectPath.resolve("java-compilerargs-4").toFile();
GradleConnector connector = GradleConnector.newConnector().forProjectDirectory(projectDir);
connector.useBuildDistribution();
try (ProjectConnection connect = connector.connect()) {
ModelBuilder<GradleSourceSets> modelBuilder = connect.model(GradleSourceSets.class);
File initScript = PluginHelper.getInitScript();
modelBuilder.addArguments("--init-script", initScript.getAbsolutePath());
GradleSourceSets gradleSourceSets = modelBuilder.get();
assertEquals(2, gradleSourceSets.getGradleSourceSets().size());
for (GradleSourceSet gradleSourceSet : gradleSourceSets.getGradleSourceSets()) {
String args = "|" + String.join("|", gradleSourceSet.getCompilerArgs());
assertFalse(args.contains("|--release|"), () -> "Available args: " + args);
assertFalse(args.contains("|-source|"), () -> "Available args: " + args);
assertFalse(args.contains("|-target|"), () -> "Available args: " + args);
assertTrue(args.contains("|--source|1.8"), () -> "Available args: " + args);
assertTrue(args.contains("|--target|9"), () -> "Available args: " + args);
assertTrue(args.contains("|-Xlint:all"), () -> "Available args: " + args);
assertEquals("1.8", gradleSourceSet.getSourceCompatibility(),
() -> "Available args: " + args);
assertEquals("9", gradleSourceSet.getTargetCompatibility(),
() -> "Available args: " + args);
}
}
}

@Test
void testJavaCompilerArgs5() throws IOException {
File projectDir = projectPath.resolve("java-compilerargs-5").toFile();
GradleConnector connector = GradleConnector.newConnector().forProjectDirectory(projectDir);
connector.useBuildDistribution();
try (ProjectConnection connect = connector.connect()) {
ModelBuilder<GradleSourceSets> modelBuilder = connect.model(GradleSourceSets.class);
File initScript = PluginHelper.getInitScript();
modelBuilder.addArguments("--init-script", initScript.getAbsolutePath());
GradleSourceSets gradleSourceSets = modelBuilder.get();
assertEquals(2, gradleSourceSets.getGradleSourceSets().size());
for (GradleSourceSet gradleSourceSet : gradleSourceSets.getGradleSourceSets()) {
String args = "|" + String.join("|", gradleSourceSet.getCompilerArgs());
assertFalse(args.contains("|--release|"), () -> "Available args: " + args);
assertFalse(args.contains("|--source|"), () -> "Available args: " + args);
assertFalse(args.contains("|--target|"), () -> "Available args: " + args);
assertTrue(args.contains("|-source|1.8"), () -> "Available args: " + args);
assertTrue(args.contains("|-target|9"), () -> "Available args: " + args);
assertTrue(args.contains("|-Xlint:all"), () -> "Available args: " + args);
assertEquals("1.8", gradleSourceSet.getSourceCompatibility(),
() -> "Available args: " + args);
assertEquals("9", gradleSourceSet.getTargetCompatibility(),
() -> "Available args: " + args);
}
}
}

@Test
void testJavaCompilerArgs6() throws IOException {
File projectDir = projectPath.resolve("java-compilerargs-6").toFile();
GradleConnector connector = GradleConnector.newConnector().forProjectDirectory(projectDir);
connector.useBuildDistribution();
try (ProjectConnection connect = connector.connect()) {
ModelBuilder<GradleSourceSets> modelBuilder = connect.model(GradleSourceSets.class);
File initScript = PluginHelper.getInitScript();
modelBuilder.addArguments("--init-script", initScript.getAbsolutePath());
GradleSourceSets gradleSourceSets = modelBuilder.get();
assertEquals(2, gradleSourceSets.getGradleSourceSets().size());
for (GradleSourceSet gradleSourceSet : gradleSourceSets.getGradleSourceSets()) {
String args = "|" + String.join("|", gradleSourceSet.getCompilerArgs());
assertFalse(args.contains("|--release|"), () -> "Available args: " + args);
assertFalse(args.contains("|--source|"), () -> "Available args: " + args);
assertFalse(args.contains("|--target|"), () -> "Available args: " + args);
assertTrue(args.contains("|-source|"), () -> "Available args: " + args);
assertTrue(args.contains("|-target|"), () -> "Available args: " + args);
assertFalse(gradleSourceSet.getSourceCompatibility().isEmpty(),
() -> "Available args: " + args);
assertFalse(gradleSourceSet.getTargetCompatibility().isEmpty(),
() -> "Available args: " + args);
}
}
}

@Test
void testJavaCompilerArgsToolchain() throws IOException {
File projectDir = projectPath.resolve("java-compilerargs-toolchain").toFile();
GradleConnector connector = GradleConnector.newConnector().forProjectDirectory(projectDir);
connector.useBuildDistribution();
try (ProjectConnection connect = connector.connect()) {
ModelBuilder<GradleSourceSets> modelBuilder = connect.model(GradleSourceSets.class);
File initScript = PluginHelper.getInitScript();
modelBuilder.addArguments("--init-script", initScript.getAbsolutePath());
GradleSourceSets gradleSourceSets = modelBuilder.get();
assertEquals(2, gradleSourceSets.getGradleSourceSets().size());
for (GradleSourceSet gradleSourceSet : gradleSourceSets.getGradleSourceSets()) {
String args = "|" + String.join("|", gradleSourceSet.getCompilerArgs());
assertFalse(args.contains("|--release|"), () -> "Available args: " + args);
assertFalse(args.contains("|--source|"), () -> "Available args: " + args);
assertFalse(args.contains("|--target|"), () -> "Available args: " + args);
assertTrue(args.contains("|-source|17|"), () -> "Available args: " + args);
assertTrue(args.contains("|-target|17|"), () -> "Available args: " + args);
assertFalse(gradleSourceSet.getSourceCompatibility().isEmpty(),
() -> "Available args: " + args);
assertFalse(gradleSourceSet.getTargetCompatibility().isEmpty(),
() -> "Available args: " + args);
}
}
}
}
10 changes: 10 additions & 0 deletions testProjects/java-compilerargs-1/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
plugins {
id 'java'
id 'java-gradle-plugin'
}

tasks.withType(JavaCompile) {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_9
options.compilerArgs << "-Xlint:all"
}
1 change: 1 addition & 0 deletions testProjects/java-compilerargs-1/settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = 'java-compilerargs-1'
9 changes: 9 additions & 0 deletions testProjects/java-compilerargs-2/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
plugins {
id 'java'
id 'java-gradle-plugin'
}

tasks.withType(JavaCompile) {
options.release = 9
options.compilerArgs << "-Xlint:all"
}
1 change: 1 addition & 0 deletions testProjects/java-compilerargs-2/settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = 'java-compilerargs-2'
8 changes: 8 additions & 0 deletions testProjects/java-compilerargs-3/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
plugins {
id 'java'
id 'java-gradle-plugin'
}

tasks.withType(JavaCompile) {
options.compilerArgs << "-Xlint:all" << "--release" << "9"
}
1 change: 1 addition & 0 deletions testProjects/java-compilerargs-3/settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = 'java-compilerargs-3'
Loading

0 comments on commit b77ecfd

Please sign in to comment.