Skip to content

Commit

Permalink
Handle java source/target defined at extension level
Browse files Browse the repository at this point in the history
  • Loading branch information
Arthur McGibbon committed Aug 27, 2024
1 parent 395b977 commit 755fdef
Show file tree
Hide file tree
Showing 9 changed files with 175 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public class DefaultGradleSourceSet implements GradleSourceSet {
public DefaultGradleSourceSet() {}

/**
* Copy constructor.
* Copy constructor. To create a POJO from the proxy that is returned from Gradle tooling API
*
* @param gradleSourceSet the source set to copy from.
*/
Expand Down Expand Up @@ -99,12 +99,13 @@ public DefaultGradleSourceSet(GradleSourceSet gradleSourceSet) {
}

private LanguageExtension convertLanguageExtension(LanguageExtension object) {
// use copy constructors to create a POJO from proxy returned by Gradle tooling
if (object.isJavaExtension()) {
return object.getAsJavaExtension();
return new DefaultJavaExtension(object.getAsJavaExtension());
}

if (object.isScalaExtension()) {
return object.getAsScalaExtension();
return new DefaultScalaExtension(object.getAsScalaExtension());
}

throw new IllegalArgumentException("No conversion methods defined for object: " + object);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,25 @@ public class DefaultJavaExtension implements JavaExtension {

private File classesDir;

/**
* copy constructor. To create a POJO from the proxy that is returned from Gradle tooling API
*
* @param javaExtension extension to copy
*/
public DefaultJavaExtension(JavaExtension javaExtension) {
this.javaHome = javaExtension.getJavaHome();
this.javaVersion = javaExtension.getJavaVersion();
this.sourceCompatibility = javaExtension.getSourceCompatibility();
this.targetCompatibility = javaExtension.getTargetCompatibility();
this.compilerArgs = javaExtension.getCompilerArgs();
this.sourceDirs = javaExtension.getSourceDirs();
this.generatedSourceDirs = javaExtension.getGeneratedSourceDirs();
this.compileTaskName = javaExtension.getCompileTaskName();
this.classesDir = javaExtension.getClassesDir();
}

public DefaultJavaExtension() {}

@Override
public File getJavaHome() {
return javaHome;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,25 @@ public class DefaultScalaExtension implements ScalaExtension {

private File classesDir;

/**
* copy constructor. To create a POJO from the proxy that is returned from Gradle tooling API
*
* @param scalaExtension extension to copy
*/
public DefaultScalaExtension(ScalaExtension scalaExtension) {
this.scalaCompilerArgs = scalaExtension.getScalaCompilerArgs();
this.scalaOrganization = scalaExtension.getScalaOrganization();
this.scalaVersion = scalaExtension.getScalaVersion();
this.scalaBinaryVersion = scalaExtension.getScalaBinaryVersion();
this.scalaJars = scalaExtension.getScalaJars();
this.sourceDirs = scalaExtension.getSourceDirs();
this.generatedSourceDirs = scalaExtension.getGeneratedSourceDirs();
this.compileTaskName = scalaExtension.getCompileTaskName();
this.classesDir = scalaExtension.getClassesDir();
}

public DefaultScalaExtension() {}

@Override
public List<String> getScalaCompilerArgs() {
return scalaCompilerArgs;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
Expand All @@ -20,21 +22,23 @@
import com.microsoft.java.bs.gradle.model.GradleModuleDependency;
import com.microsoft.java.bs.gradle.model.JavaExtension;
import com.microsoft.java.bs.gradle.model.SupportedLanguage;
import com.microsoft.java.bs.gradle.model.SupportedLanguages;
import com.microsoft.java.bs.gradle.model.impl.DefaultJavaExtension;
import com.microsoft.java.bs.gradle.plugin.utils.Utils;

import org.gradle.api.JavaVersion;
import org.gradle.api.Project;
import org.gradle.api.file.Directory;
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.compile.AbstractCompile;
import org.gradle.api.tasks.compile.CompileOptions;
import org.gradle.api.tasks.compile.JavaCompile;
import org.gradle.plugins.ide.internal.tooling.java.DefaultInstalledJdk;
import org.gradle.util.GradleVersion;

import com.microsoft.java.bs.gradle.model.SupportedLanguages;
import com.microsoft.java.bs.gradle.model.impl.DefaultJavaExtension;

/**
* The language model builder for Java language.
*/
Expand Down Expand Up @@ -69,6 +73,37 @@ public DefaultJavaExtension getExtensionFor(Project project, SourceSet sourceSet
extension.setCompilerArgs(compilerArgs);
extension.setSourceCompatibility(getSourceCompatibility(compilerArgs));
extension.setTargetCompatibility(getTargetCompatibility(compilerArgs));

// it's possible to set source/target compatibility in different places.
// Java plugin setting should override javacoptions
JavaVersion sourceVersion = null;
JavaVersion targetVersion = null;
if (GradleVersion.current().compareTo(GradleVersion.version("7.1")) >= 0) {
Object plugin = project.getExtensions().findByName("java");
// must get `raw` versions or Gradle will substitute with toolchain versions
sourceVersion = Utils.invokeMethodIgnoreFail(plugin, "getRawSourceCompatibility");
targetVersion = Utils.invokeMethodIgnoreFail(plugin, "getRawTargetCompatibility");
} else {
// use deprecated `convention`
try {
Object convention = Utils.invokeMethod(project, "getConvention");
Object plugins = Utils.invokeMethod(convention, "getPlugins");
Method getGet = plugins.getClass().getMethod("get", Object.class);
Object plugin = getGet.invoke(plugins, "java");
// must get `raw` versions or Gradle will substitute with toolchain versions
sourceVersion = Utils.invokeMethodIgnoreFail(plugin, "getSourceCompatibility");
targetVersion = Utils.invokeMethodIgnoreFail(plugin, "getTargetCompatibility");
} catch (NoSuchMethodException | SecurityException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException e) {
// ignore
}
}
if (sourceVersion != null) {
extension.setSourceCompatibility(sourceVersion.toString());
}
if (targetVersion != null) {
extension.setTargetCompatibility(targetVersion.toString());
}
return extension;
}
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import com.microsoft.java.bs.gradle.model.impl.DefaultGradleSourceSet;
import com.microsoft.java.bs.gradle.model.impl.DefaultGradleSourceSets;
import com.microsoft.java.bs.gradle.plugin.dependency.DependencyCollector;
import com.microsoft.java.bs.gradle.plugin.utils.Utils;

/**
* The model builder for Gradle source sets.
Expand Down Expand Up @@ -148,9 +149,8 @@ private DefaultGradleSourceSet getSourceSet(Project project, SourceSet sourceSet
break;
}
} else {
try {
Method getTestClassesDir = testTask.getClass().getMethod("getTestClassesDir");
Object testClassesDir = getTestClassesDir.invoke(testTask);
Object testClassesDir = Utils.invokeMethodIgnoreFail(testTask, "getTestClassesDir");
if (testClassesDir != null) {
for (File sourceOutputDir : sourceOutputDirs) {
if (sourceOutputDir.equals(testClassesDir)) {
gradleSourceSet.setHasTests(true);
Expand All @@ -160,9 +160,6 @@ private DefaultGradleSourceSet getSourceSet(Project project, SourceSet sourceSet
if (gradleSourceSet.hasTests()) {
break;
}
} catch (NoSuchMethodException | SecurityException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException e) {
// ignore
}
}
}
Expand Down Expand Up @@ -276,15 +273,12 @@ private Collection<SourceSet> getSourceSetContainer(Project project) {
// query the java plugin. This limits support to Java only if other
// languages add their own sourcesets
// use reflection because `getConvention` will be removed in Gradle 9.0
Method getConvention = project.getClass().getMethod("getConvention");
Object convention = getConvention.invoke(project);
Method getPlugins = convention.getClass().getMethod("getPlugins");
Object plugins = getPlugins.invoke(convention);
Object convention = Utils.invokeMethod(project, "getConvention");
Object plugins = Utils.invokeMethod(convention, "getPlugins");
Method getGet = plugins.getClass().getMethod("get", Object.class);
Object pluginConvention = getGet.invoke(plugins, "java");
if (pluginConvention != null) {
Method getSourceSetsMethod = pluginConvention.getClass().getMethod("getSourceSets");
return (SourceSetContainer) getSourceSetsMethod.invoke(pluginConvention);
return Utils.invokeMethod(pluginConvention, "getSourceSets");
}
} catch (NoSuchMethodException | SecurityException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException e) {
Expand Down Expand Up @@ -329,19 +323,13 @@ private Set<Object> getArchiveSourcePaths(CopySpec copySpec) {
sourcePaths.addAll(getArchiveSourcePaths(child));
}
} else {
try {
Method getChildren = defaultCopySpec.getClass().getMethod("getChildren");
Object children = getChildren.invoke(defaultCopySpec);
if (children instanceof Iterable) {
for (Object child : (Iterable<?>) children) {
if (child instanceof CopySpec) {
sourcePaths.addAll(getArchiveSourcePaths((CopySpec) child));
}
Object children = Utils.invokeMethodIgnoreFail(defaultCopySpec, "getChildren");
if (children instanceof Iterable) {
for (Object child : (Iterable<?>) children) {
if (child instanceof CopySpec) {
sourcePaths.addAll(getArchiveSourcePaths((CopySpec) child));
}
}
} catch (NoSuchMethodException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException e) {
// cannot get archive information
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

package com.microsoft.java.bs.gradle.plugin.utils;

import java.lang.reflect.InvocationTargetException;

/**
* Helper functions.
*/
public class Utils {

/**
* Invoke a method by reflection and pass back any exceptions thrown.
* Method takes no parameters.
*
* @param <A> expected return type
* @param object instance to invoke method on.
* @param methodName name of method to invoke.
* @return result of method called.
*/
@SuppressWarnings("unchecked")
public static <A> A invokeMethod(Object object, String methodName)
throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
return (A) object.getClass().getMethod(methodName).invoke(object);
}

/**
* Invoke a method by reflection and return null if method cannot be called.
* Method takes no parameters.
*
* @param <A> expected return type
* @param object instance to invoke method on.
* @param methodName name of method to invoke.
* @return result of method called.
*/
public static <A> A invokeMethodIgnoreFail(Object object, String methodName) {
try {
return invokeMethod(object, methodName);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Stream;
import java.util.ArrayList;

import com.microsoft.java.bs.gradle.model.GradleSourceSet;
import com.microsoft.java.bs.gradle.model.GradleSourceSets;
Expand Down Expand Up @@ -57,8 +56,8 @@ private GradleSourceSets getGradleSourceSets(ProjectConnection connect) throws I
.addArguments("-Dorg.gradle.logging.level=quiet")
.addJvmArguments("-Dbsp.gradle.supportedLanguages="
+ String.join(",", SupportedLanguages.allBspNames));
GradleSourceSets sourceSets = action.run();
return new DefaultGradleSourceSets(new ArrayList<>(sourceSets.getGradleSourceSets()));

return new DefaultGradleSourceSets(action.run());
}

private interface ConnectionConsumer {
Expand Down Expand Up @@ -465,6 +464,31 @@ void testJavaCompilerArgsToolchain(GradleVersion gradleVersion) throws IOExcepti
});
}

@ParameterizedTest(name = "testJavaSourceTarget {0}")
@MethodSource("versionProvider")
void testJavaSourceTarget(GradleVersion gradleVersion) throws IOException {
// `java` cannot be used before 5.0
assumeTrue(gradleVersion.compareTo(GradleVersion.version("5.0")) >= 0);
withSourceSets("java-source-target", gradleVersion, gradleSourceSets -> {
assertEquals(2, gradleSourceSets.getGradleSourceSets().size());
for (GradleSourceSet gradleSourceSet : gradleSourceSets.getGradleSourceSets()) {
JavaExtension javaExtension = SupportedLanguages.JAVA.getExtension(gradleSourceSet);
assertNotNull(javaExtension);
String args = "|" + String.join("|", javaExtension.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|8"), () -> "Available args: " + args);
String version9 = gradleVersion.compareTo(GradleVersion.version("8.0")) >= 0 ? "9" : "1.9";
assertEquals(version9, javaExtension.getSourceCompatibility(),
() -> "Available args: " + args);
assertEquals("1.8", javaExtension.getTargetCompatibility(),
() -> "Available args: " + args);
}
});
}

@ParameterizedTest(name = "testScala2ModelBuilder {0}")
@MethodSource("versionProvider")
void testScala2ModelBuilder(GradleVersion gradleVersion) throws IOException {
Expand Down
12 changes: 12 additions & 0 deletions testProjects/java-source-target/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
plugins {
id 'java'
}

java {
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_1_9
}

tasks.withType(JavaCompile) {
options.compilerArgs.addAll(['--release', '8'])
}
1 change: 1 addition & 0 deletions testProjects/java-source-target/settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = 'java-source-target'

0 comments on commit 755fdef

Please sign in to comment.