From 49aab296a5a890c21c6b6602f460cb489943fb15 Mon Sep 17 00:00:00 2001 From: Arthur McGibbon Date: Thu, 11 Jul 2024 11:54:11 +0100 Subject: [PATCH 1/2] Parallelize source set retrieval. --- .../java/bs/gradle/model/GradleSourceSet.java | 9 +- .../model/GradleSourceSetsMetadata.java | 45 -- .../java/bs/gradle/model/JavaExtension.java | 5 + .../bs/gradle/model/LanguageExtension.java | 30 ++ .../java/bs/gradle/model/ScalaExtension.java | 5 + .../model/actions/GetSourceSetsAction.java | 195 ++++++--- .../model/impl/DefaultGradleSourceSet.java | 29 +- .../impl/DefaultGradleSourceSetsMetadata.java | 62 --- .../model/impl/DefaultJavaExtension.java | 56 ++- .../model/impl/DefaultScalaExtension.java | 55 ++- .../plugin/GradleBuildServerPlugin.java | 22 - .../plugin/JavaLanguageModelBuilder.java | 171 ++++---- .../gradle/plugin/LanguageModelBuilder.java | 14 +- .../plugin/ScalaLanguageModelBuilder.java | 46 +- .../java/bs/gradle/plugin/SourceSetCache.java | 56 --- .../gradle/plugin/SourceSetsModelBuilder.java | 409 +++++++++--------- .../dependency/DependencyCollector.java | 146 ++++--- .../plugin/GradleBuildServerPluginTest.java | 83 +++- .../internal/managers/BuildTargetManager.java | 22 +- .../internal/services/BuildTargetService.java | 22 +- .../gradle/GradleApiConnectorTest.java | 10 +- .../managers/BuildTargetManagerTest.java | 11 +- .../BuildTargetServerIntegrationTest.java | 4 +- .../services/BuildTargetServiceTest.java | 4 +- testProjects/missing-repository/build.gradle | 14 + .../missing-repository/settings.gradle | 1 + testProjects/spock/build.gradle | 2 +- 27 files changed, 805 insertions(+), 723 deletions(-) delete mode 100644 model/src/main/java/com/microsoft/java/bs/gradle/model/GradleSourceSetsMetadata.java delete mode 100644 model/src/main/java/com/microsoft/java/bs/gradle/model/impl/DefaultGradleSourceSetsMetadata.java delete mode 100644 plugin/src/main/java/com/microsoft/java/bs/gradle/plugin/SourceSetCache.java create mode 100644 testProjects/missing-repository/build.gradle create mode 100644 testProjects/missing-repository/settings.gradle diff --git a/model/src/main/java/com/microsoft/java/bs/gradle/model/GradleSourceSet.java b/model/src/main/java/com/microsoft/java/bs/gradle/model/GradleSourceSet.java index d685437f..465b2783 100644 --- a/model/src/main/java/com/microsoft/java/bs/gradle/model/GradleSourceSet.java +++ b/model/src/main/java/com/microsoft/java/bs/gradle/model/GradleSourceSet.java @@ -81,15 +81,20 @@ public interface GradleSourceSet extends Serializable { public Set getResourceDirs(); /** - * The output directory of this source set. + * The output directories of this source set. */ - public File getSourceOutputDir(); + Set getSourceOutputDirs(); /** * The resource output directory of this source set. */ public File getResourceOutputDir(); + /** + * Any archive files created from the output of this source set to the output dirs. + */ + Map> getArchiveOutputFiles(); + /** * The compile classpath for this source set. */ diff --git a/model/src/main/java/com/microsoft/java/bs/gradle/model/GradleSourceSetsMetadata.java b/model/src/main/java/com/microsoft/java/bs/gradle/model/GradleSourceSetsMetadata.java deleted file mode 100644 index 00435b27..00000000 --- a/model/src/main/java/com/microsoft/java/bs/gradle/model/GradleSourceSetsMetadata.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.microsoft.java.bs.gradle.model; - -import java.io.File; -import java.io.Serializable; -import java.util.List; -import java.util.Map; - -/** - * Provides necessary information about Gradle source sets, - * enabling mapping of dependencies between them. - */ -public interface GradleSourceSetsMetadata extends Serializable { - - /** - * Returns a map that associates each Gradle source set with its corresponding - * classpath files in a gradle project. This typically includes any libraries - * or dependencies required for compilation within that source set. - * - *

- * The keys of the map represent instances of the {@link GradleSourceSet} class, - * identifying all the source sets within the project. - *

- *

- * The values of the map are lists of {@link File} objects, representing the - * classpath files associated with the corresponding source set. - *

- */ - Map> getGradleSourceSetsToClasspath(); - - /** - * Returns a map that associates output files with the Gradle source sets that - * generated them. This is useful for understanding the origin of generated artifacts. - * - *

- * The keys of the map are {@link File} objects, representing individual - * output files produced during the build process. - *

- *

- * The values of the map are instances of the {@link GradleSourceSet} class, - * indicating the source set that generated the corresponding output file. - *

- */ - Map getOutputsToSourceSet(); - -} diff --git a/model/src/main/java/com/microsoft/java/bs/gradle/model/JavaExtension.java b/model/src/main/java/com/microsoft/java/bs/gradle/model/JavaExtension.java index dc8220a0..515f18ea 100644 --- a/model/src/main/java/com/microsoft/java/bs/gradle/model/JavaExtension.java +++ b/model/src/main/java/com/microsoft/java/bs/gradle/model/JavaExtension.java @@ -35,4 +35,9 @@ public interface JavaExtension extends LanguageExtension { * The list of compiler arguments. */ public List getCompilerArgs(); + + /** + * The classes directory. + */ + File getClassesDir(); } diff --git a/model/src/main/java/com/microsoft/java/bs/gradle/model/LanguageExtension.java b/model/src/main/java/com/microsoft/java/bs/gradle/model/LanguageExtension.java index 49f6c0cc..475cc4fb 100644 --- a/model/src/main/java/com/microsoft/java/bs/gradle/model/LanguageExtension.java +++ b/model/src/main/java/com/microsoft/java/bs/gradle/model/LanguageExtension.java @@ -3,7 +3,9 @@ package com.microsoft.java.bs.gradle.model; +import java.io.File; import java.io.Serializable; +import java.util.Set; /** * Interface representing a language extension. @@ -13,6 +15,34 @@ */ public interface LanguageExtension extends Serializable { + /** + * returns all the source directories for this language. + * + * @return set of source directories + */ + Set getSourceDirs(); + + /** + * returns all the generated source directories for this language. + * + * @return set of generated source directories + */ + Set getGeneratedSourceDirs(); + + /** + * returns the output directory for this language. + * + * @return directory containing class files + */ + File getClassesDir(); + + /** + * returns the name of the Gradle compile task for this language. + * + * @return name of Gradle compile task + */ + String getCompileTaskName(); + /** * Checks if the implementing class is a {@link JavaExtension}. * diff --git a/model/src/main/java/com/microsoft/java/bs/gradle/model/ScalaExtension.java b/model/src/main/java/com/microsoft/java/bs/gradle/model/ScalaExtension.java index 0120f086..6c62f0c1 100644 --- a/model/src/main/java/com/microsoft/java/bs/gradle/model/ScalaExtension.java +++ b/model/src/main/java/com/microsoft/java/bs/gradle/model/ScalaExtension.java @@ -36,4 +36,9 @@ public interface ScalaExtension extends LanguageExtension { * E.g. scala-library, scala-compiler and scala-reflect. */ List getScalaJars(); + + /** + * The classes directory. + */ + File getClassesDir(); } diff --git a/model/src/main/java/com/microsoft/java/bs/gradle/model/actions/GetSourceSetsAction.java b/model/src/main/java/com/microsoft/java/bs/gradle/model/actions/GetSourceSetsAction.java index 0bd8c36b..a6d7e308 100644 --- a/model/src/main/java/com/microsoft/java/bs/gradle/model/actions/GetSourceSetsAction.java +++ b/model/src/main/java/com/microsoft/java/bs/gradle/model/actions/GetSourceSetsAction.java @@ -6,23 +6,23 @@ import com.microsoft.java.bs.gradle.model.BuildTargetDependency; import com.microsoft.java.bs.gradle.model.GradleSourceSet; import com.microsoft.java.bs.gradle.model.GradleSourceSets; -import com.microsoft.java.bs.gradle.model.GradleSourceSetsMetadata; import com.microsoft.java.bs.gradle.model.impl.DefaultBuildTargetDependency; import com.microsoft.java.bs.gradle.model.impl.DefaultGradleSourceSet; import com.microsoft.java.bs.gradle.model.impl.DefaultGradleSourceSets; import org.gradle.tooling.BuildAction; import org.gradle.tooling.BuildController; +import org.gradle.tooling.model.gradle.BasicGradleProject; import org.gradle.tooling.model.gradle.GradleBuild; import java.io.File; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; -import java.util.Map.Entry; +import java.util.stream.Collectors; /** * {@link BuildAction} that retrieves {@link DefaultGradleSourceSet} from a Gradle build, @@ -37,79 +37,144 @@ public class GetSourceSetsAction implements BuildAction { */ @Override public GradleSourceSets execute(BuildController buildController) { - Set traversedProjects = new HashSet<>(); - Map> sourceSetToClasspath = new HashMap<>(); - Map outputsToSourceSet = new HashMap<>(); + Collection builds = fetchIncludedBuilds(buildController); + List sourceSets = fetchModels(buildController, builds); + return new DefaultGradleSourceSets(sourceSets); + } - GradleBuild buildModel = buildController.getBuildModel(); - String rootProjectName = buildModel.getRootProject().getName(); - fetchModels(buildController, - buildModel, - traversedProjects, - sourceSetToClasspath, - outputsToSourceSet, - rootProjectName); - - // Add dependencies - List sourceSets = new ArrayList<>(); - for (Entry> entry : sourceSetToClasspath.entrySet()) { - Set dependencies = new HashSet<>(); - for (File file : entry.getValue()) { - GradleSourceSet otherSourceSet = outputsToSourceSet.get(file); - if (otherSourceSet != null && !Objects.equals(entry.getKey(), otherSourceSet)) { - dependencies.add(new DefaultBuildTargetDependency(otherSourceSet)); - } + private Collection fetchIncludedBuilds(BuildController buildController) { + Map builds = new HashMap<>(); + GradleBuild build = buildController.getBuildModel(); + String rootProjectName = build.getRootProject().getName(); + fetchIncludedBuilds(buildController, build, builds, rootProjectName); + return builds.values(); + } + + private void fetchIncludedBuilds(BuildController buildController, GradleBuild build, + Map builds, String rootProjectName) { + if (builds.containsKey(rootProjectName)) { + return; + } + builds.put(rootProjectName, build); + // Cannot use GradleVersion.current() in BuildAction as that will return the Tooling API version + // Cannot use BuildEnvironment to get GradleVersion as that doesn't work pre-3.0 even though + // documentation has it added in version 1. + // So just handle exceptions + Set moreBuilds; + try { + // added in 4.10 + moreBuilds = build.getEditableBuilds(); + } catch (Exception e1) { + try { + // added in 3.3 + moreBuilds = build.getIncludedBuilds(); + } catch (Exception e2) { + moreBuilds = null; } + } + if (moreBuilds != null) { + for (GradleBuild includedBuild : moreBuilds) { + String includedBuildName = includedBuild.getRootProject().getName(); + fetchIncludedBuilds(buildController, includedBuild, builds, includedBuildName); + } + } + } - DefaultGradleSourceSet sourceSet = new DefaultGradleSourceSet(entry.getKey()); - sourceSet.setBuildTargetDependencies(dependencies); - sourceSets.add(sourceSet); + /** + * Fetches source sets from the provided Gradle build model. + * + * @param buildController The Gradle build controller used to interact with the build. + * @param builds The Gradle build models representing the build and included builds. + */ + private List fetchModels(BuildController buildController, + Collection builds) { + List projectActions = new ArrayList(); + for (GradleBuild build : builds) { + for (BasicGradleProject project : build.getProjects()) { + projectActions.add(new GetSourceSetAction(project)); + } } - return new DefaultGradleSourceSets(sourceSets); + // since the model returned from Gradle TAPI is a wrapped object, here we re-construct it + // via a copy constructorso we can treat as a DefaultGradleSourceSet and + // populate source set dependencies. + List sourceSets = buildController.run(projectActions).stream() + .flatMap(ss -> ss.getGradleSourceSets().stream()) + .map(ss -> new DefaultGradleSourceSet(ss)) + .collect(Collectors.toList()); + + populateInterProjectInfo(sourceSets); + + return sourceSets; } /** - * Fetches source sets from the provided Gradle build model and - * stores them in a map categorized by project name. - * - * @param buildController The Gradle build controller used to interact with the build. - * @param build The Gradle build model representing the current build. - * @param traversedProjects A set of traversed project names to avoid cyclic dependencies. - * @param sourceSetToClasspath A map that associates GradleSourceSet objects with their - * corresponding classpath files. - * @param outputsToSourceSet A map that associates output files with the GradleSourceSet - * they belong to. - * @param buildName The name of the root project in the build. + * {@link BuildAction} that retrieves {@link GradleSourceSets} for a single project. + * This allows project models to be retrieved in parallel. */ - private void fetchModels( - BuildController buildController, - GradleBuild build, - Set traversedProjects, - Map> sourceSetToClasspath, - Map outputsToSourceSet, - String buildName - ) { - if (traversedProjects.contains(buildName)) { - return; + static class GetSourceSetAction implements BuildAction { + private final BasicGradleProject project; + + public GetSourceSetAction(BasicGradleProject project) { + this.project = project; } - GradleSourceSetsMetadata sourceSets = buildController - .findModel(build.getRootProject(), GradleSourceSetsMetadata.class); - - traversedProjects.add(buildName); - sourceSetToClasspath.putAll(sourceSets.getGradleSourceSetsToClasspath()); - outputsToSourceSet.putAll(sourceSets.getOutputsToSourceSet()); - - for (GradleBuild includedBuild : build.getIncludedBuilds()) { - String includedBuildName = includedBuild.getRootProject().getName(); - fetchModels(buildController, - includedBuild, - traversedProjects, - sourceSetToClasspath, - outputsToSourceSet, - includedBuildName); + + @Override + public GradleSourceSets execute(BuildController controller) { + return controller.getModel(project, GradleSourceSets.class); } } -} + // Inter-sourceset dependencies must be built up after retrieval of all sourcesets + // because they are not available before when using included builds. + // Classpaths that reference other projects using jars are to be replaced with + // source paths. + private void populateInterProjectInfo(List sourceSets) { + // map all output dirs to their source sets + Map> archivesToSourceOutput = new HashMap<>(); + Map outputsToSourceSet = new HashMap<>(); + for (GradleSourceSet sourceSet : sourceSets) { + if (sourceSet.getSourceOutputDirs() != null) { + for (File file : sourceSet.getSourceOutputDirs()) { + outputsToSourceSet.put(file, sourceSet); + } + } + if (sourceSet.getResourceOutputDir() != null) { + outputsToSourceSet.put(sourceSet.getResourceOutputDir(), sourceSet); + } + if (sourceSet.getArchiveOutputFiles() != null) { + for (Map.Entry> archive : sourceSet.getArchiveOutputFiles().entrySet()) { + outputsToSourceSet.put(archive.getKey(), sourceSet); + archivesToSourceOutput.computeIfAbsent(archive.getKey(), f -> new ArrayList<>()) + .addAll(archive.getValue()); + } + } + } + + // match any classpath entries to other project's output dirs/jars to create dependencies. + // replace classpath entries that reference jars with classes dirs. + for (GradleSourceSet sourceSet : sourceSets) { + Set dependencies = new HashSet<>(); + List classpath = new ArrayList<>(); + for (File file : sourceSet.getCompileClasspath()) { + // add project dependency + GradleSourceSet otherSourceSet = outputsToSourceSet.get(file); + if (otherSourceSet != null) { + dependencies.add(new DefaultBuildTargetDependency(otherSourceSet)); + } + // replace jar on classpath with source output on classpath + List sourceOutputDir = archivesToSourceOutput.get(file); + if (sourceOutputDir == null) { + classpath.add(file); + } else { + classpath.addAll(sourceOutputDir); + } + } + if (sourceSet instanceof DefaultGradleSourceSet) { + ((DefaultGradleSourceSet) sourceSet).setBuildTargetDependencies(dependencies); + ((DefaultGradleSourceSet) sourceSet).setCompileClasspath(classpath); + } + } + } +} \ No newline at end of file diff --git a/model/src/main/java/com/microsoft/java/bs/gradle/model/impl/DefaultGradleSourceSet.java b/model/src/main/java/com/microsoft/java/bs/gradle/model/impl/DefaultGradleSourceSet.java index bbf7ea25..97151aae 100644 --- a/model/src/main/java/com/microsoft/java/bs/gradle/model/impl/DefaultGradleSourceSet.java +++ b/model/src/main/java/com/microsoft/java/bs/gradle/model/impl/DefaultGradleSourceSet.java @@ -45,12 +45,14 @@ public class DefaultGradleSourceSet implements GradleSourceSet { private Set generatedSourceDirs; - private File sourceOutputDir; + private Set sourceOutputDirs; private Set resourceDirs; private File resourceOutputDir; + private Map> archiveOutputFiles; + private List compileClasspath; private Set moduleDependencies; @@ -81,9 +83,10 @@ public DefaultGradleSourceSet(GradleSourceSet gradleSourceSet) { this.taskNames = gradleSourceSet.getTaskNames(); this.sourceDirs = gradleSourceSet.getSourceDirs(); this.generatedSourceDirs = gradleSourceSet.getGeneratedSourceDirs(); - this.sourceOutputDir = gradleSourceSet.getSourceOutputDir(); + this.sourceOutputDirs = gradleSourceSet.getSourceOutputDirs(); this.resourceDirs = gradleSourceSet.getResourceDirs(); this.resourceOutputDir = gradleSourceSet.getResourceOutputDir(); + this.archiveOutputFiles = gradleSourceSet.getArchiveOutputFiles(); this.compileClasspath = gradleSourceSet.getCompileClasspath(); this.moduleDependencies = gradleSourceSet.getModuleDependencies().stream() .map(DefaultGradleModuleDependency::new).collect(Collectors.toSet()); @@ -213,12 +216,12 @@ public void setGeneratedSourceDirs(Set generatedSourceDirs) { } @Override - public File getSourceOutputDir() { - return sourceOutputDir; + public Set getSourceOutputDirs() { + return sourceOutputDirs; } - public void setSourceOutputDir(File sourceOutputDir) { - this.sourceOutputDir = sourceOutputDir; + public void setSourceOutputDirs(Set sourceOutputDirs) { + this.sourceOutputDirs = sourceOutputDirs; } @Override @@ -239,6 +242,15 @@ public void setResourceOutputDir(File resourceOutputDir) { this.resourceOutputDir = resourceOutputDir; } + @Override + public Map> getArchiveOutputFiles() { + return archiveOutputFiles; + } + + public void setArchiveOutputFiles(Map> archiveOutputFiles) { + this.archiveOutputFiles = archiveOutputFiles; + } + @Override public List getCompileClasspath() { return compileClasspath; @@ -288,7 +300,7 @@ public void setExtensions(Map extensions) { public int hashCode() { return Objects.hash(gradleVersion, displayName, projectName, projectPath, projectDir, rootDir, sourceSetName, classesTaskName, cleanTaskName, taskNames, sourceDirs, - generatedSourceDirs, sourceOutputDir, resourceDirs, resourceOutputDir, + generatedSourceDirs, sourceOutputDirs, resourceDirs, resourceOutputDir, archiveOutputFiles, compileClasspath, moduleDependencies, buildTargetDependencies, hasTests, extensions); } @@ -317,9 +329,10 @@ public boolean equals(Object obj) { && Objects.equals(taskNames, other.taskNames) && Objects.equals(sourceDirs, other.sourceDirs) && Objects.equals(generatedSourceDirs, other.generatedSourceDirs) - && Objects.equals(sourceOutputDir, other.sourceOutputDir) + && Objects.equals(sourceOutputDirs, other.sourceOutputDirs) && Objects.equals(resourceDirs, other.resourceDirs) && Objects.equals(resourceOutputDir, other.resourceOutputDir) + && Objects.equals(archiveOutputFiles, other.archiveOutputFiles) && Objects.equals(compileClasspath, other.compileClasspath) && Objects.equals(moduleDependencies, other.moduleDependencies) && Objects.equals(buildTargetDependencies, other.buildTargetDependencies) diff --git a/model/src/main/java/com/microsoft/java/bs/gradle/model/impl/DefaultGradleSourceSetsMetadata.java b/model/src/main/java/com/microsoft/java/bs/gradle/model/impl/DefaultGradleSourceSetsMetadata.java deleted file mode 100644 index 21459e6f..00000000 --- a/model/src/main/java/com/microsoft/java/bs/gradle/model/impl/DefaultGradleSourceSetsMetadata.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.microsoft.java.bs.gradle.model.impl; - -import com.microsoft.java.bs.gradle.model.GradleSourceSet; -import com.microsoft.java.bs.gradle.model.GradleSourceSetsMetadata; - -import java.io.File; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -/** - * Default implementation of {@link DefaultGradleSourceSetsMetadata}. - */ -public class DefaultGradleSourceSetsMetadata implements GradleSourceSetsMetadata { - - private Map> sourceSetsToClasspath; - private Map outputsToSourceSet; - - public DefaultGradleSourceSetsMetadata( - Map> sourceSetsToClasspath, - Map outputsToSourceSet - ) { - this.sourceSetsToClasspath = sourceSetsToClasspath; - this.outputsToSourceSet = outputsToSourceSet; - } - - @Override - public Map> getGradleSourceSetsToClasspath() { - return sourceSetsToClasspath; - } - - public void setSourceSetsToClasspath(Map> sourceSetsToClasspath) { - this.sourceSetsToClasspath = sourceSetsToClasspath; - } - - @Override - public Map getOutputsToSourceSet() { - return outputsToSourceSet; - } - - public void setOutputsToSourceSet(Map outputsToSourceSet) { - this.outputsToSourceSet = outputsToSourceSet; - } - - @Override - public int hashCode() { - return Objects.hash(sourceSetsToClasspath, outputsToSourceSet); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - DefaultGradleSourceSetsMetadata that = (DefaultGradleSourceSetsMetadata) obj; - return Objects.equals(sourceSetsToClasspath, that.sourceSetsToClasspath) - && Objects.equals(outputsToSourceSet, that.outputsToSourceSet); - } -} diff --git a/model/src/main/java/com/microsoft/java/bs/gradle/model/impl/DefaultJavaExtension.java b/model/src/main/java/com/microsoft/java/bs/gradle/model/impl/DefaultJavaExtension.java index cc4558a5..ad8ddcb9 100644 --- a/model/src/main/java/com/microsoft/java/bs/gradle/model/impl/DefaultJavaExtension.java +++ b/model/src/main/java/com/microsoft/java/bs/gradle/model/impl/DefaultJavaExtension.java @@ -9,6 +9,7 @@ import java.io.File; import java.util.List; import java.util.Objects; +import java.util.Set; /** * Default implementation of {@link JavaExtension}. @@ -25,6 +26,14 @@ public class DefaultJavaExtension implements JavaExtension { private String targetCompatibility; private List compilerArgs; + + private Set sourceDirs; + + private Set generatedSourceDirs; + + private String compileTaskName; + + private File classesDir; @Override public File getJavaHome() { @@ -71,11 +80,46 @@ public void setCompilerArgs(List compilerArgs) { this.compilerArgs = compilerArgs; } + @Override + public Set getSourceDirs() { + return sourceDirs; + } + + public void setSourceDirs(Set sourceDirs) { + this.sourceDirs = sourceDirs; + } + + @Override + public Set getGeneratedSourceDirs() { + return generatedSourceDirs; + } + + public void setGeneratedSourceDirs(Set generatedSourceDirs) { + this.generatedSourceDirs = generatedSourceDirs; + } + + @Override + public String getCompileTaskName() { + return compileTaskName; + } + + public void setCompileTaskName(String compileTaskName) { + this.compileTaskName = compileTaskName; + } + + @Override + public File getClassesDir() { + return classesDir; + } + + public void setClassesDir(File classesDir) { + this.classesDir = classesDir; + } + @Override public int hashCode() { - return Objects.hash(javaHome, javaVersion, - sourceCompatibility, targetCompatibility, compilerArgs - ); + return Objects.hash(javaHome, javaVersion, sourceCompatibility, targetCompatibility, + compilerArgs, sourceDirs, generatedSourceDirs, compileTaskName, classesDir); } @Override @@ -94,7 +138,11 @@ public boolean equals(Object obj) { && Objects.equals(javaVersion, other.javaVersion) && Objects.equals(sourceCompatibility, other.sourceCompatibility) && Objects.equals(targetCompatibility, other.targetCompatibility) - && Objects.equals(compilerArgs, other.compilerArgs); + && Objects.equals(compilerArgs, other.compilerArgs) + && Objects.equals(sourceDirs, other.sourceDirs) + && Objects.equals(generatedSourceDirs, other.generatedSourceDirs) + && Objects.equals(compileTaskName, other.compileTaskName) + && Objects.equals(classesDir, other.classesDir); } @Override diff --git a/model/src/main/java/com/microsoft/java/bs/gradle/model/impl/DefaultScalaExtension.java b/model/src/main/java/com/microsoft/java/bs/gradle/model/impl/DefaultScalaExtension.java index 4cef62b3..b340d149 100644 --- a/model/src/main/java/com/microsoft/java/bs/gradle/model/impl/DefaultScalaExtension.java +++ b/model/src/main/java/com/microsoft/java/bs/gradle/model/impl/DefaultScalaExtension.java @@ -9,6 +9,7 @@ import java.io.File; import java.util.List; import java.util.Objects; +import java.util.Set; /** * Default implementation of {@link ScalaExtension}. @@ -25,6 +26,14 @@ public class DefaultScalaExtension implements ScalaExtension { private String scalaBinaryVersion; private List scalaJars; + + private Set sourceDirs; + + private Set generatedSourceDirs; + + private String compileTaskName; + + private File classesDir; @Override public List getScalaCompilerArgs() { @@ -71,10 +80,46 @@ public void setScalaJars(List scalaJars) { this.scalaJars = scalaJars; } + @Override + public Set getSourceDirs() { + return sourceDirs; + } + + public void setSourceDirs(Set sourceDirs) { + this.sourceDirs = sourceDirs; + } + + @Override + public Set getGeneratedSourceDirs() { + return generatedSourceDirs; + } + + public void setGeneratedSourceDirs(Set generatedSourceDirs) { + this.generatedSourceDirs = generatedSourceDirs; + } + + @Override + public String getCompileTaskName() { + return compileTaskName; + } + + public void setCompileTaskName(String compileTaskName) { + this.compileTaskName = compileTaskName; + } + + @Override + public File getClassesDir() { + return classesDir; + } + + public void setClassesDir(File classesDir) { + this.classesDir = classesDir; + } + @Override public int hashCode() { - return Objects.hash(scalaCompilerArgs, scalaOrganization, - scalaVersion, scalaBinaryVersion, scalaJars); + return Objects.hash(scalaCompilerArgs, scalaOrganization, scalaVersion, scalaBinaryVersion, + scalaJars, sourceDirs, generatedSourceDirs, compileTaskName, classesDir); } @Override @@ -93,7 +138,11 @@ public boolean equals(Object obj) { && Objects.equals(scalaOrganization, other.scalaOrganization) && Objects.equals(scalaVersion, other.scalaVersion) && Objects.equals(scalaBinaryVersion, other.scalaBinaryVersion) - && Objects.equals(scalaJars, other.scalaJars); + && Objects.equals(scalaJars, other.scalaJars) + && Objects.equals(sourceDirs, other.sourceDirs) + && Objects.equals(generatedSourceDirs, other.generatedSourceDirs) + && Objects.equals(compileTaskName, other.compileTaskName) + && Objects.equals(classesDir, other.classesDir); } @Override diff --git a/plugin/src/main/java/com/microsoft/java/bs/gradle/plugin/GradleBuildServerPlugin.java b/plugin/src/main/java/com/microsoft/java/bs/gradle/plugin/GradleBuildServerPlugin.java index dd9cf94d..8d665087 100644 --- a/plugin/src/main/java/com/microsoft/java/bs/gradle/plugin/GradleBuildServerPlugin.java +++ b/plugin/src/main/java/com/microsoft/java/bs/gradle/plugin/GradleBuildServerPlugin.java @@ -3,24 +3,17 @@ package com.microsoft.java.bs.gradle.plugin; -import java.util.LinkedList; -import java.util.List; - import javax.inject.Inject; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry; -import com.microsoft.java.bs.gradle.model.SupportedLanguages; - /** * The customized Gradle plugin to get the project structure information. */ public class GradleBuildServerPlugin implements Plugin { - public static final List SUPPORTED_LANGUAGE_BUILDERS = new LinkedList<>(); - private final ToolingModelBuilderRegistry registry; /** @@ -28,7 +21,6 @@ public class GradleBuildServerPlugin implements Plugin { */ @Inject public GradleBuildServerPlugin(ToolingModelBuilderRegistry registry) { - registerSupportedLanguages(); this.registry = registry; } @@ -36,18 +28,4 @@ public GradleBuildServerPlugin(ToolingModelBuilderRegistry registry) { public void apply(Project project) { registry.register(new SourceSetsModelBuilder()); } - - private void registerSupportedLanguages() { - String supportedLanguagesProps = System.getProperty("bsp.gradle.supportedLanguages"); - if (supportedLanguagesProps != null) { - String[] supportedLanguages = supportedLanguagesProps.split(","); - for (String language : supportedLanguages) { - if (language.equalsIgnoreCase(SupportedLanguages.JAVA.getBspName())) { - SUPPORTED_LANGUAGE_BUILDERS.add(new JavaLanguageModelBuilder()); - } else if (language.equalsIgnoreCase(SupportedLanguages.SCALA.getBspName())) { - SUPPORTED_LANGUAGE_BUILDERS.add(new ScalaLanguageModelBuilder()); - } - } - } - } } diff --git a/plugin/src/main/java/com/microsoft/java/bs/gradle/plugin/JavaLanguageModelBuilder.java b/plugin/src/main/java/com/microsoft/java/bs/gradle/plugin/JavaLanguageModelBuilder.java index 79b0254d..c658eefe 100644 --- a/plugin/src/main/java/com/microsoft/java/bs/gradle/plugin/JavaLanguageModelBuilder.java +++ b/plugin/src/main/java/com/microsoft/java/bs/gradle/plugin/JavaLanguageModelBuilder.java @@ -7,8 +7,6 @@ import java.io.File; import java.io.FileReader; import java.io.IOException; -import java.util.Collection; -import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; import java.util.List; @@ -28,6 +26,7 @@ import org.gradle.api.internal.tasks.compile.DefaultJavaCompileSpec; import org.gradle.api.internal.tasks.compile.JavaCompilerArgumentsBuilder; 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; @@ -41,85 +40,72 @@ */ public class JavaLanguageModelBuilder extends LanguageModelBuilder { - @Override - public boolean appliesFor(Project project, SourceSet sourceSet) { - return getJavaCompileTask(project, sourceSet) != null; - } - @Override public SupportedLanguage getLanguage() { return SupportedLanguages.JAVA; } @Override - public Collection getSourceFoldersFor(Project project, SourceSet sourceSet) { - return sourceSet.getJava().getSrcDirs(); - } - - @Override - public Collection getGeneratedSourceFoldersFor(Project project, SourceSet sourceSet) { - Set generatedSrcDirs = new HashSet<>(); - addAnnotationProcessingDir(project, sourceSet, generatedSrcDirs); - addGeneratedSourceDirs(project, sourceSet, sourceSet.getJava().getSrcDirs(), generatedSrcDirs); - return generatedSrcDirs; - } - - @Override - public DefaultJavaExtension getExtensionsFor(Project project, SourceSet sourceSet, + public DefaultJavaExtension getExtensionFor(Project project, SourceSet sourceSet, Set moduleDependencies) { - DefaultJavaExtension extension = new DefaultJavaExtension(); - - // jdk - extension.setJavaHome(DefaultInstalledJdk.current().getJavaHome()); - extension.setJavaVersion(DefaultInstalledJdk.current().getJavaVersion().getMajorVersion()); - List compilerArgs = getCompilerArgs(project, sourceSet); - extension.setCompilerArgs(compilerArgs); - extension.setSourceCompatibility( - getSourceCompatibility(compilerArgs)); - extension.setTargetCompatibility( - getTargetCompatibility(compilerArgs)); - return extension; + JavaCompile javaCompile = getJavaCompileTask(project, sourceSet); + if (javaCompile != null) { + DefaultJavaExtension extension = new DefaultJavaExtension(); + + // jdk + extension.setJavaHome(DefaultInstalledJdk.current().getJavaHome()); + extension.setJavaVersion(DefaultInstalledJdk.current().getJavaVersion().getMajorVersion()); + + extension.setCompileTaskName(javaCompile.getName()); + + extension.setSourceDirs(sourceSet.getJava().getSrcDirs()); + Set generatedSrcDirs = new HashSet<>(); + addAnnotationProcessingDir(javaCompile, generatedSrcDirs); + addGeneratedSourceDirs(javaCompile, extension.getSourceDirs(), generatedSrcDirs); + extension.setGeneratedSourceDirs(generatedSrcDirs); + extension.setClassesDir(getClassesDir(javaCompile)); + + List compilerArgs = getCompilerArgs(javaCompile); + extension.setCompilerArgs(compilerArgs); + extension.setSourceCompatibility(getSourceCompatibility(compilerArgs)); + extension.setTargetCompatibility(getTargetCompatibility(compilerArgs)); + return extension; + } + return null; } private JavaCompile getJavaCompileTask(Project project, SourceSet sourceSet) { return (JavaCompile) getLanguageCompileTask(project, sourceSet); } - private void addAnnotationProcessingDir(Project project, SourceSet sourceSet, - Set generatedSrcDirs) { - JavaCompile javaCompile = getJavaCompileTask(project, sourceSet); - if (javaCompile != null) { - CompileOptions options = javaCompile.getOptions(); - if (GradleVersion.current().compareTo(GradleVersion.version("6.3")) >= 0) { - Directory generatedDir = options.getGeneratedSourceOutputDirectory().getOrNull(); - if (generatedDir != null) { - generatedSrcDirs.add(generatedDir.getAsFile()); - } - } else if (GradleVersion.current().compareTo(GradleVersion.version("4.3")) >= 0) { - File generatedDir = options.getAnnotationProcessorGeneratedSourcesDirectory(); - if (generatedDir != null) { - generatedSrcDirs.add(generatedDir); - } + private void addAnnotationProcessingDir(JavaCompile javaCompile, Set generatedSrcDirs) { + CompileOptions options = javaCompile.getOptions(); + if (GradleVersion.current().compareTo(GradleVersion.version("6.3")) >= 0) { + Directory generatedDir = options.getGeneratedSourceOutputDirectory().getOrNull(); + if (generatedDir != null) { + generatedSrcDirs.add(generatedDir.getAsFile()); + } + } else if (GradleVersion.current().compareTo(GradleVersion.version("4.3")) >= 0) { + File generatedDir = options.getAnnotationProcessorGeneratedSourcesDirectory(); + if (generatedDir != null) { + generatedSrcDirs.add(generatedDir); } } } - private void addGeneratedSourceDirs(Project project, SourceSet sourceSet, - Set srcDirs, Set generatedSrcDirs) { - JavaCompile javaCompile = getJavaCompileTask(project, sourceSet); - if (javaCompile != null) { - Set filesToCompile = javaCompile.getSource().getFiles(); - for (File file : filesToCompile) { - if (canSkipInferSourceRoot(file, srcDirs, generatedSrcDirs)) { - continue; - } + private void addGeneratedSourceDirs(JavaCompile javaCompile, Set srcDirs, + Set generatedSrcDirs) { + Set filesToCompile = javaCompile.getSource().getFiles(); + for (File file : filesToCompile) { + if (canSkipInferSourceRoot(file, srcDirs, generatedSrcDirs)) { + continue; + } - // the file is not in the source directories, so it must be a generated file. - // we need to find the source directory for the generated file. - File srcDir = findSourceDirForGeneratedFile(file); - if (srcDir != null) { - generatedSrcDirs.add(srcDir); - } + // the file is not in the source directories, so it must be a generated file. + // we need to find the source directory for the generated file. + File srcDir = findSourceDirForGeneratedFile(file); + if (srcDir != null) { + generatedSrcDirs.add(srcDir); } } } @@ -218,35 +204,30 @@ private DefaultJavaCompileSpec getJavaCompileSpec(JavaCompile javaCompile) { /** * Get the compilation arguments of the source set. */ - private List getCompilerArgs(Project project, SourceSet sourceSet) { - JavaCompile javaCompile = getJavaCompileTask(project, sourceSet); - if (javaCompile != null) { - 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 compilerArgs = new LinkedList<>(options.getCompilerArgs()); - return compilerArgs - .stream() - .map(Object::toString) - .collect(Collectors.toList()); - } - } + private List getCompilerArgs(JavaCompile javaCompile) { + CompileOptions options = javaCompile.getOptions(); - return Collections.emptyList(); + 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 compilerArgs = new LinkedList<>(options.getCompilerArgs()); + return compilerArgs + .stream() + .map(Object::toString) + .collect(Collectors.toList()); + } } /** @@ -282,4 +263,12 @@ private Optional findFirstCompilerArgMatch(List compilerArgs, .map(Optional::get) .findFirst(); } + + private File getClassesDir(AbstractCompile compile) { + if (GradleVersion.current().compareTo(GradleVersion.version("6.1")) >= 0) { + return compile.getDestinationDirectory().get().getAsFile(); + } else { + return compile.getDestinationDir(); + } + } } \ No newline at end of file diff --git a/plugin/src/main/java/com/microsoft/java/bs/gradle/plugin/LanguageModelBuilder.java b/plugin/src/main/java/com/microsoft/java/bs/gradle/plugin/LanguageModelBuilder.java index 6389fd79..cc2ced82 100644 --- a/plugin/src/main/java/com/microsoft/java/bs/gradle/plugin/LanguageModelBuilder.java +++ b/plugin/src/main/java/com/microsoft/java/bs/gradle/plugin/LanguageModelBuilder.java @@ -3,8 +3,6 @@ package com.microsoft.java.bs.gradle.plugin; -import java.io.File; -import java.util.Collection; import java.util.Set; import com.microsoft.java.bs.gradle.model.GradleModuleDependency; @@ -19,23 +17,17 @@ * The language model builder for different languages. */ public abstract class LanguageModelBuilder { - public abstract boolean appliesFor(Project project, SourceSet sourceSet); public abstract SupportedLanguage getLanguage(); - public String getLanguageId() { + public final String getLanguageId() { return getLanguage().getBspName(); } - public abstract Collection getSourceFoldersFor(Project project, SourceSet sourceSet); - - public abstract Collection getGeneratedSourceFoldersFor(Project project, - SourceSet sourceSet); - - public abstract LanguageExtension getExtensionsFor(Project project, SourceSet sourceSet, + public abstract LanguageExtension getExtensionFor(Project project, SourceSet sourceSet, Set moduleDependencies); - protected Task getLanguageCompileTask(Project project, SourceSet sourceSet) { + protected final Task getLanguageCompileTask(Project project, SourceSet sourceSet) { String taskName = sourceSet.getCompileTaskName(getLanguage().getGradleName()); try { return project.getTasks().getByName(taskName); diff --git a/plugin/src/main/java/com/microsoft/java/bs/gradle/plugin/ScalaLanguageModelBuilder.java b/plugin/src/main/java/com/microsoft/java/bs/gradle/plugin/ScalaLanguageModelBuilder.java index 9866fbdd..5d9a2c08 100644 --- a/plugin/src/main/java/com/microsoft/java/bs/gradle/plugin/ScalaLanguageModelBuilder.java +++ b/plugin/src/main/java/com/microsoft/java/bs/gradle/plugin/ScalaLanguageModelBuilder.java @@ -6,7 +6,6 @@ import java.io.File; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -23,6 +22,7 @@ import org.gradle.api.file.SourceDirectorySet; import org.gradle.api.tasks.ScalaSourceDirectorySet; import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.compile.AbstractCompile; import org.gradle.api.tasks.scala.ScalaCompile; import org.gradle.api.tasks.scala.ScalaCompileOptions; import org.gradle.util.GradleVersion; @@ -34,25 +34,19 @@ */ public class ScalaLanguageModelBuilder extends LanguageModelBuilder { - @Override - public boolean appliesFor(Project project, SourceSet sourceSet) { - return getScalaCompileTask(project, sourceSet) != null; - } - @Override public SupportedLanguage getLanguage() { return SupportedLanguages.SCALA; } - @Override - public Collection getSourceFoldersFor(Project project, SourceSet sourceSet) { + private Set getSourceFolders(SourceSet sourceSet) { if (GradleVersion.current().compareTo(GradleVersion.version("7.1")) >= 0) { SourceDirectorySet sourceDirectorySet = sourceSet.getExtensions() .findByType(ScalaSourceDirectorySet.class); return sourceDirectorySet == null ? Collections.emptySet() : sourceDirectorySet.getSrcDirs(); } else { - // there is no way pre-Gradle 7.1 to get the scala source dirs separately from other - // languages. Luckily source dirs from all languages are jumbled together in BSP, + // there appears to be no way pre-Gradle 7.1 to get the scala source dirs separately + // from other languages. Source dirs from all languages are jumbled together in BSP, // so we can just reply with all. // resource dirs must be removed. Set allSource = sourceSet.getAllSource().getSrcDirs(); @@ -62,30 +56,32 @@ public Collection getSourceFoldersFor(Project project, SourceSet sourceSet } } - @Override - public Collection getGeneratedSourceFoldersFor(Project project, SourceSet sourceSet) { - return Collections.emptySet(); - } - private ScalaCompile getScalaCompileTask(Project project, SourceSet sourceSet) { return (ScalaCompile) getLanguageCompileTask(project, sourceSet); } @Override - public DefaultScalaExtension getExtensionsFor(Project project, SourceSet sourceSet, + public DefaultScalaExtension getExtensionFor(Project project, SourceSet sourceSet, Set moduleDependencies) { + ScalaCompile scalaCompile = getScalaCompileTask(project, sourceSet); GradleModuleDependency scalaLibraryDependency = getScalaLibraryDependency(moduleDependencies); - if (scalaLibraryDependency != null) { - ScalaCompile scalaCompile = getScalaCompileTask(project, sourceSet); + if (scalaCompile != null && scalaLibraryDependency != null) { DefaultScalaExtension extension = new DefaultScalaExtension(); + + extension.setCompileTaskName(scalaCompile.getName()); + + extension.setSourceDirs(getSourceFolders(sourceSet)); + extension.setGeneratedSourceDirs(Collections.emptySet()); + extension.setClassesDir(getClassesDir(scalaCompile)); + extension.setScalaOrganization(getScalaOrganization(scalaLibraryDependency)); extension.setScalaVersion(getScalaVersion(scalaLibraryDependency)); extension.setScalaBinaryVersion(getScalaBinaryVersion(scalaLibraryDependency)); extension.setScalaCompilerArgs(getScalaCompilerArgs(scalaCompile)); extension.setScalaJars(getScalaJars(scalaCompile)); + extension.setClassesDir(getClassesDir(scalaCompile)); return extension; } - // Scala plugin found but Scala library not found - should not be possible return null; } @@ -178,4 +174,16 @@ private List getScalaCompilerArgs(ScalaCompile scalaCompile) { return args; } + + private File getClassesDir(AbstractCompile compile) { + if (compile != null) { + if (GradleVersion.current().compareTo(GradleVersion.version("6.1")) >= 0) { + return compile.getDestinationDirectory().get().getAsFile(); + } else { + return compile.getDestinationDir(); + } + } + + return null; + } } diff --git a/plugin/src/main/java/com/microsoft/java/bs/gradle/plugin/SourceSetCache.java b/plugin/src/main/java/com/microsoft/java/bs/gradle/plugin/SourceSetCache.java deleted file mode 100644 index 9bba868d..00000000 --- a/plugin/src/main/java/com/microsoft/java/bs/gradle/plugin/SourceSetCache.java +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -package com.microsoft.java.bs.gradle.plugin; - -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; - -import org.gradle.api.Project; -import org.gradle.api.tasks.SourceSet; - -import com.microsoft.java.bs.gradle.model.impl.DefaultGradleSourceSet; - -/** - * The cache for SourceSet (from Gradle) and the customized - * DefaultGradleSourceSet/Project (from Gradle) mapping. - */ -public class SourceSetCache { - private Map gradleSourceSetMapping; - private Map projectMapping; - - public SourceSetCache() { - this.gradleSourceSetMapping = new HashMap<>(); - this.projectMapping = new HashMap<>(); - } - - public void addGradleSourceSet(SourceSet sourceSet, DefaultGradleSourceSet gradleSourceSet) { - this.gradleSourceSetMapping.put(sourceSet, gradleSourceSet); - } - - public void addProject(SourceSet sourceSet, Project project) { - this.projectMapping.put(sourceSet, project); - } - - public DefaultGradleSourceSet getGradleSourceSet(SourceSet sourceSet) { - return this.gradleSourceSetMapping.get(sourceSet); - } - - public Project getProject(SourceSet sourceSet) { - return this.projectMapping.get(sourceSet); - } - - public Collection getAllGradleSourceSets() { - return this.gradleSourceSetMapping.values(); - } - - public Collection getAllSourceSets() { - return this.gradleSourceSetMapping.keySet(); - } - - public Collection getAllProjects() { - return new HashSet<>(this.projectMapping.values()); - } -} diff --git a/plugin/src/main/java/com/microsoft/java/bs/gradle/plugin/SourceSetsModelBuilder.java b/plugin/src/main/java/com/microsoft/java/bs/gradle/plugin/SourceSetsModelBuilder.java index 5f3484c3..5b20f596 100644 --- a/plugin/src/main/java/com/microsoft/java/bs/gradle/plugin/SourceSetsModelBuilder.java +++ b/plugin/src/main/java/com/microsoft/java/bs/gradle/plugin/SourceSetsModelBuilder.java @@ -6,36 +6,36 @@ import java.io.File; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.net.URI; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; - -import com.microsoft.java.bs.gradle.model.GradleSourceSet; -import com.microsoft.java.bs.gradle.model.GradleSourceSetsMetadata; +import java.util.stream.Collectors; import org.gradle.api.GradleException; import org.gradle.api.Project; import org.gradle.api.Task; import org.gradle.api.file.CopySpec; -import org.gradle.api.file.Directory; import org.gradle.api.file.FileCollection; -import org.gradle.api.file.SourceDirectorySet; import org.gradle.api.internal.file.copy.DefaultCopySpec; import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.SourceSetContainer; -import org.gradle.api.tasks.SourceSetOutput; -import org.gradle.api.tasks.TaskCollection; import org.gradle.api.tasks.bundling.AbstractArchiveTask; import org.gradle.api.tasks.testing.Test; import org.gradle.tooling.provider.model.ToolingModelBuilder; import org.gradle.util.GradleVersion; -import com.microsoft.java.bs.gradle.model.impl.DefaultGradleSourceSet; -import com.microsoft.java.bs.gradle.model.impl.DefaultGradleSourceSetsMetadata; +import com.microsoft.java.bs.gradle.model.GradleModuleDependency; +import com.microsoft.java.bs.gradle.model.GradleSourceSet; +import com.microsoft.java.bs.gradle.model.GradleSourceSets; import com.microsoft.java.bs.gradle.model.LanguageExtension; +import com.microsoft.java.bs.gradle.model.SupportedLanguages; +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; /** @@ -44,179 +44,227 @@ public class SourceSetsModelBuilder implements ToolingModelBuilder { @Override public boolean canBuild(String modelName) { - return modelName.equals(GradleSourceSetsMetadata.class.getName()); + return modelName.equals(GradleSourceSets.class.getName()); } @Override - public Object buildAll(String modelName, Project rootProject) { - - Map> sourceSetsToClasspath = new HashMap<>(); - Map outputsToSourceSet = new HashMap<>(); - - Set allProject = rootProject.getAllprojects(); - SourceSetCache cache = new SourceSetCache(); - // this set is used to eliminate the source, resource and output - // directories from the module dependencies. - Set exclusionFromDependencies = new HashSet<>(); + public Object buildAll(String modelName, Project project) { // mapping Gradle source set to our customized model. - for (Project project : allProject) { - SourceSetContainer sourceSets = getSourceSetContainer(project); - if (sourceSets == null || sourceSets.isEmpty()) { - continue; - } - sourceSets.forEach(sourceSet -> { - DefaultGradleSourceSet gradleSourceSet = new DefaultGradleSourceSet(); - cache.addGradleSourceSet(sourceSet, gradleSourceSet); - cache.addProject(sourceSet, project); - gradleSourceSet.setBuildTargetDependencies(new HashSet<>()); - gradleSourceSet.setGradleVersion(project.getGradle().getGradleVersion()); - gradleSourceSet.setProjectName(project.getName()); - String projectPath = project.getPath(); - gradleSourceSet.setProjectPath(projectPath); - gradleSourceSet.setProjectDir(project.getProjectDir()); - gradleSourceSet.setRootDir(project.getRootDir()); - gradleSourceSet.setSourceSetName(sourceSet.getName()); - String classesTaskName = getFullTaskName(projectPath, sourceSet.getClassesTaskName()); - gradleSourceSet.setClassesTaskName(classesTaskName); - String cleanTaskName = getFullTaskName(projectPath, "clean"); - gradleSourceSet.setCleanTaskName(cleanTaskName); - Set taskNames = new HashSet<>(); - gradleSourceSet.setTaskNames(taskNames); - String projectName = stripPathPrefix(projectPath); - if (projectName == null || projectName.length() == 0) { - projectName = gradleSourceSet.getProjectName(); - } - String displayName = projectName + " [" + gradleSourceSet.getSourceSetName() + ']'; - gradleSourceSet.setDisplayName(displayName); + List sourceSets = getSourceSetContainer(project).stream() + .map(ss -> getSourceSet(project, ss)).collect(Collectors.toList()); - // source - Set srcDirs = new HashSet<>(); - Set generatedSrcDirs = new HashSet<>(); - for (LanguageModelBuilder languageModelBuilder : - GradleBuildServerPlugin.SUPPORTED_LANGUAGE_BUILDERS) { - if (languageModelBuilder.appliesFor(project, sourceSet)) { - Task compileTask = languageModelBuilder.getLanguageCompileTask(project, sourceSet); - if (compileTask != null) { - String compileTaskName = getFullTaskName(projectPath, compileTask.getName()); - taskNames.add(compileTaskName); - } - srcDirs.addAll(languageModelBuilder.getSourceFoldersFor(project, sourceSet)); - generatedSrcDirs.addAll( - languageModelBuilder.getGeneratedSourceFoldersFor(project, sourceSet)); - } - } - gradleSourceSet.setSourceDirs(srcDirs); - exclusionFromDependencies.addAll(srcDirs); - gradleSourceSet.setGeneratedSourceDirs(generatedSrcDirs); - exclusionFromDependencies.addAll(generatedSrcDirs); + excludeSourceDirsFromModules(sourceSets); - // classpath - List compileClasspath = new LinkedList<>(); - try { - compileClasspath.addAll(sourceSet.getCompileClasspath().getFiles()); - } catch (GradleException e) { - // ignore - } - gradleSourceSet.setCompileClasspath(compileClasspath); - sourceSetsToClasspath.put(gradleSourceSet, compileClasspath); + return new DefaultGradleSourceSets(sourceSets); + } + + private DefaultGradleSourceSet getSourceSet(Project project, SourceSet sourceSet) { + DefaultGradleSourceSet gradleSourceSet = new DefaultGradleSourceSet(); + // dependencies are populated by the GradleSourceSetsAction. Make sure not null. + gradleSourceSet.setBuildTargetDependencies(new HashSet<>()); + gradleSourceSet.setGradleVersion(project.getGradle().getGradleVersion()); + gradleSourceSet.setProjectName(project.getName()); + String projectPath = project.getPath(); + gradleSourceSet.setProjectPath(projectPath); + gradleSourceSet.setProjectDir(project.getProjectDir()); + gradleSourceSet.setRootDir(project.getRootDir()); + gradleSourceSet.setSourceSetName(sourceSet.getName()); + String classesTaskName = getFullTaskName(projectPath, sourceSet.getClassesTaskName()); + gradleSourceSet.setClassesTaskName(classesTaskName); + String cleanTaskName = getFullTaskName(projectPath, "clean"); + gradleSourceSet.setCleanTaskName(cleanTaskName); + Set taskNames = new HashSet<>(); + gradleSourceSet.setTaskNames(taskNames); + String projectName = stripPathPrefix(projectPath); + if (projectName.isEmpty()) { + projectName = project.getName(); + } + String displayName = projectName + " [" + gradleSourceSet.getSourceSetName() + ']'; + gradleSourceSet.setDisplayName(displayName); + + // setup module dependencies before language support check. + gradleSourceSet.setModuleDependencies(getModuleDependencies(project, sourceSet)); + + // specific languages + Map extensions = new HashMap<>(); + Set srcDirs = new HashSet<>(); + Set generatedSrcDirs = new HashSet<>(); + Set sourceOutputDirs = new HashSet<>(); + for (LanguageModelBuilder languageModelBuilder : getSupportedLanguages()) { + LanguageExtension extension = languageModelBuilder.getExtensionFor(project, sourceSet, + gradleSourceSet.getModuleDependencies()); + if (extension != null) { + String compileTaskName = getFullTaskName(projectPath, extension.getCompileTaskName()); + taskNames.add(compileTaskName); + + srcDirs.addAll(extension.getSourceDirs()); + generatedSrcDirs.addAll(extension.getGeneratedSourceDirs()); + sourceOutputDirs.add(extension.getClassesDir()); + + extensions.put(languageModelBuilder.getLanguageId(), extension); + } + } + gradleSourceSet.setSourceDirs(srcDirs); + gradleSourceSet.setGeneratedSourceDirs(generatedSrcDirs); + gradleSourceSet.setExtensions(extensions); + gradleSourceSet.setSourceOutputDirs(sourceOutputDirs); - // source output dir - File sourceOutputDir = getSourceOutputDir(sourceSet); - if (sourceOutputDir != null) { - gradleSourceSet.setSourceOutputDir(sourceOutputDir); - outputsToSourceSet.put(sourceOutputDir, gradleSourceSet); - exclusionFromDependencies.add(sourceOutputDir); - } + // classpath + List compileClasspath = new LinkedList<>(); + try { + compileClasspath.addAll(sourceSet.getCompileClasspath().getFiles()); + } catch (GradleException e) { + // ignore + } + gradleSourceSet.setCompileClasspath(compileClasspath); - // resource - Set resourceDirs = sourceSet.getResources().getSrcDirs(); - gradleSourceSet.setResourceDirs(resourceDirs); - exclusionFromDependencies.addAll(resourceDirs); + // resource + Set resourceDirs = sourceSet.getResources().getSrcDirs(); + gradleSourceSet.setResourceDirs(resourceDirs); - // resource output dir - File resourceOutputDir = sourceSet.getOutput().getResourcesDir(); - if (resourceOutputDir != null) { - gradleSourceSet.setResourceOutputDir(resourceOutputDir); - outputsToSourceSet.put(resourceOutputDir, gradleSourceSet); - exclusionFromDependencies.add(resourceOutputDir); - } + // resource output dir + File resourceOutputDir = sourceSet.getOutput().getResourcesDir(); + if (resourceOutputDir != null) { + gradleSourceSet.setResourceOutputDir(resourceOutputDir); + } - // tests - if (sourceOutputDir != null) { - TaskCollection testTasks = project.getTasks().withType(Test.class); - for (Test testTask : testTasks) { - if (GradleVersion.current().compareTo(GradleVersion.version("4.0")) >= 0) { - FileCollection files = testTask.getTestClassesDirs(); - if (files.contains(sourceOutputDir)) { + // archive output dirs + Map> archiveOutputFiles = getArchiveOutputFiles(project, sourceSet); + gradleSourceSet.setArchiveOutputFiles(archiveOutputFiles); + + // tests + if (sourceOutputDirs != null) { + Set testTasks = tasksWithType(project, Test.class); + for (Test testTask : testTasks) { + if (GradleVersion.current().compareTo(GradleVersion.version("4.0")) >= 0) { + FileCollection files = testTask.getTestClassesDirs(); + for (File sourceOutputDir : sourceOutputDirs) { + if (files.contains(sourceOutputDir)) { + gradleSourceSet.setHasTests(true); + break; + } + } + if (gradleSourceSet.hasTests()) { + break; + } + } else { + try { + Method getTestClassesDir = testTask.getClass().getMethod("getTestClassesDir"); + Object testClassesDir = getTestClassesDir.invoke(testTask); + for (File sourceOutputDir : sourceOutputDirs) { + if (sourceOutputDir.equals(testClassesDir)) { gradleSourceSet.setHasTests(true); break; } - } else { - try { - Method getTestClassesDir = testTask.getClass().getMethod("getTestClassesDir"); - Object testClassesDir = getTestClassesDir.invoke(testTask); - if (sourceOutputDir.equals(testClassesDir)) { - gradleSourceSet.setHasTests(true); - break; - } - } catch (NoSuchMethodException | SecurityException | IllegalAccessException - | IllegalArgumentException | InvocationTargetException e) { - // ignore - } } + if (gradleSourceSet.hasTests()) { + break; + } + } catch (NoSuchMethodException | SecurityException | IllegalAccessException + | IllegalArgumentException | InvocationTargetException e) { + // ignore } } - }); - - if (!sourceSets.isEmpty()) { - gatherArchiveTasks(outputsToSourceSet, cache, project, sourceSets); } } - setModuleDependencies(cache, exclusionFromDependencies); + return gradleSourceSet; + } - for (SourceSet sourceSet : cache.getAllSourceSets()) { - DefaultGradleSourceSet gradleSourceSet = cache.getGradleSourceSet(sourceSet); - if (gradleSourceSet == null) { - continue; + private List getSupportedLanguages() { + List results = new LinkedList<>(); + String supportedLanguagesProps = System.getProperty("bsp.gradle.supportedLanguages"); + if (supportedLanguagesProps != null) { + String[] supportedLanguages = supportedLanguagesProps.split(","); + for (String language : supportedLanguages) { + if (language.equalsIgnoreCase(SupportedLanguages.JAVA.getBspName())) { + results.add(new JavaLanguageModelBuilder()); + } else if (language.equalsIgnoreCase(SupportedLanguages.SCALA.getBspName())) { + results.add(new ScalaLanguageModelBuilder()); + } } + } + return results; + } - Project project = cache.getProject(sourceSet); - if (project == null) { - continue; - } + private Set tasksWithType(Project project, Class clazz) { + // Gradle gives concurrentmodification exceptions if multiple threads resolve + // the tasks concurrently, which happens on multi-project builds + synchronized (project.getRootProject()) { + return new HashSet<>(project.getTasks().withType(clazz)); + } + } - Map extensions = new HashMap<>(); - for (LanguageModelBuilder languageModelBuilder : - GradleBuildServerPlugin.SUPPORTED_LANGUAGE_BUILDERS) { - if (languageModelBuilder.appliesFor(project, sourceSet)) { - LanguageExtension extension = languageModelBuilder.getExtensionsFor(project, sourceSet, - gradleSourceSet.getModuleDependencies()); - if (extension != null) { - extensions.put(languageModelBuilder.getLanguageId(), extension); + /** + * get all archive tasks for this project and maintain the archive file + * to source set mapping. + */ + private Map> getArchiveOutputFiles(Project project, SourceSet sourceSet) { + // get all archive tasks for this project and find the dirs that are included in the archive + Set archiveTasks = tasksWithType(project, AbstractArchiveTask.class); + Map> archiveOutputFiles = new HashMap<>(); + for (AbstractArchiveTask archiveTask : archiveTasks) { + Set archiveSourcePaths = getArchiveSourcePaths(archiveTask.getRootSpec()); + for (Object sourcePath : archiveSourcePaths) { + if (sourceSet.getOutput().equals(sourcePath)) { + File archiveFile; + if (GradleVersion.current().compareTo(GradleVersion.version("5.1")) >= 0) { + archiveFile = archiveTask.getArchiveFile().get().getAsFile(); + } else { + archiveFile = archiveTask.getArchivePath(); } + List sourceSetOutputs = new LinkedList<>(); + sourceSetOutputs.addAll(sourceSet.getOutput().getFiles()); + archiveOutputFiles.put(archiveFile, sourceSetOutputs); } } - gradleSourceSet.setExtensions(extensions); } + return archiveOutputFiles; + } - return new DefaultGradleSourceSetsMetadata(sourceSetsToClasspath, outputsToSourceSet); + private Set getModuleDependencies(Project project, SourceSet sourceSet) { + Set configurationNames = getClasspathConfigurationNames(sourceSet); + return DependencyCollector.getModuleDependencies(project, configurationNames); } - private void setModuleDependencies(SourceSetCache cache, Set exclusionFromDependencies) { - for (SourceSet sourceSet : cache.getAllSourceSets()) { - DefaultGradleSourceSet gradleSourceSet = cache.getGradleSourceSet(sourceSet); - if (gradleSourceSet == null) { - continue; + // remove source set dirs from modules + private void excludeSourceDirsFromModules(List sourceSets) { + Set exclusions = new HashSet<>(); + for (GradleSourceSet sourceSet : sourceSets) { + if (sourceSet.getSourceDirs() != null) { + exclusions.addAll(sourceSet.getSourceDirs()); + } + if (sourceSet.getGeneratedSourceDirs() != null) { + exclusions.addAll(sourceSet.getGeneratedSourceDirs()); + } + if (sourceSet.getResourceDirs() != null) { + exclusions.addAll(sourceSet.getResourceDirs()); + } + if (sourceSet.getSourceOutputDirs() != null) { + exclusions.addAll(sourceSet.getSourceOutputDirs()); + } + if (sourceSet.getResourceOutputDir() != null) { + exclusions.add(sourceSet.getResourceOutputDir()); + } + if (sourceSet.getArchiveOutputFiles() != null) { + exclusions.addAll(sourceSet.getArchiveOutputFiles().keySet()); + } + } + + Set exclusionUris = exclusions.stream().map(File::toURI).collect(Collectors.toSet()); + + for (GradleSourceSet sourceSet : sourceSets) { + Set filteredModuleDependencies = sourceSet.getModuleDependencies() + .stream().filter(mod -> mod.getArtifacts() + .stream().anyMatch(art -> !exclusionUris.contains(art.getUri()))) + .collect(Collectors.toSet()); + if (sourceSet instanceof DefaultGradleSourceSet) { + ((DefaultGradleSourceSet) sourceSet).setModuleDependencies(filteredModuleDependencies); } - DependencyCollector collector = new DependencyCollector(cache.getProject(sourceSet), - exclusionFromDependencies); - collector.collectByConfigurationNames(getClasspathConfigurationNames(sourceSet)); - gradleSourceSet.setModuleDependencies(collector.getModuleDependencies()); } } - private SourceSetContainer getSourceSetContainer(Project project) { + private Collection getSourceSetContainer(Project project) { if (GradleVersion.current().compareTo(GradleVersion.version("5.0")) >= 0) { SourceSetContainer sourceSetContainer = project.getExtensions() .findByType(SourceSetContainer.class); @@ -242,7 +290,7 @@ private SourceSetContainer getSourceSetContainer(Project project) { | IllegalArgumentException | InvocationTargetException e) { // ignore } - return null; + return new LinkedList<>(); } /** @@ -270,67 +318,6 @@ private String stripPathPrefix(String projectPath) { return projectPath; } - private File getSourceOutputDir(SourceSet sourceSet) { - if (GradleVersion.current().compareTo(GradleVersion.version("6.1")) >= 0) { - Directory sourceOutputDir = sourceSet.getJava().getClassesDirectory().getOrNull(); - if (sourceOutputDir != null) { - return sourceOutputDir.getAsFile(); - } - return null; - } else if (GradleVersion.current().compareTo(GradleVersion.version("4.0")) >= 0) { - try { - // https://docs.gradle.org/4.0/javadoc/org/gradle/api/file/SourceDirectorySet.html#getOutputDir() - Method getOutputDirMethod = SourceDirectorySet.class.getMethod("getOutputDir"); - return (File) getOutputDirMethod.invoke(sourceSet.getJava()); - } catch (NoSuchMethodException | SecurityException | IllegalAccessException - | IllegalArgumentException | InvocationTargetException e) { - // ignore - } - } else { - // get all output dirs and filter out resources output - SourceSetOutput output = sourceSet.getOutput(); - Set allOutputDirs = output.getFiles(); - File resourceOutputDir = output.getResourcesDir(); - allOutputDirs.remove(resourceOutputDir); - if (!allOutputDirs.isEmpty()) { - return allOutputDirs.iterator().next(); - } - } - - return null; - } - - /** - * get all archive tasks for this project and maintain the archive file - * to source set mapping. - */ - private void gatherArchiveTasks(Map outputsToSourceSet, - SourceSetCache cache, Project project, SourceSetContainer sourceSets) { - TaskCollection archiveTasks = - project.getTasks().withType(AbstractArchiveTask.class); - for (AbstractArchiveTask archiveTask : archiveTasks) { - Set archiveSourcePaths = getArchiveSourcePaths(archiveTask.getRootSpec()); - for (Object sourcePath : archiveSourcePaths) { - sourceSets.forEach(sourceSet -> { - DefaultGradleSourceSet gradleSourceSet = cache.getGradleSourceSet(sourceSet); - if (gradleSourceSet == null) { - return; - } - - if (sourceSet.getOutput().equals(sourcePath)) { - File archiveFile; - if (GradleVersion.current().compareTo(GradleVersion.version("5.1")) >= 0) { - archiveFile = archiveTask.getArchiveFile().get().getAsFile(); - } else { - archiveFile = archiveTask.getArchivePath(); - } - outputsToSourceSet.put(archiveFile, gradleSourceSet); - } - }); - } - } - } - private Set getArchiveSourcePaths(CopySpec copySpec) { Set sourcePaths = new HashSet<>(); if (copySpec instanceof DefaultCopySpec) { @@ -353,7 +340,7 @@ private Set getArchiveSourcePaths(CopySpec copySpec) { } } } catch (NoSuchMethodException | IllegalAccessException - | IllegalArgumentException | InvocationTargetException e) { + | IllegalArgumentException | InvocationTargetException e) { // cannot get archive information } } diff --git a/plugin/src/main/java/com/microsoft/java/bs/gradle/plugin/dependency/DependencyCollector.java b/plugin/src/main/java/com/microsoft/java/bs/gradle/plugin/dependency/DependencyCollector.java index b46cd684..b952831a 100644 --- a/plugin/src/main/java/com/microsoft/java/bs/gradle/plugin/dependency/DependencyCollector.java +++ b/plugin/src/main/java/com/microsoft/java/bs/gradle/plugin/dependency/DependencyCollector.java @@ -4,12 +4,15 @@ package com.microsoft.java.bs.gradle.plugin.dependency; import java.io.File; -import java.util.LinkedHashSet; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.gradle.api.GradleException; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.ResolvedArtifact; @@ -42,70 +45,72 @@ public class DependencyCollector { private static final String UNKNOWN = "unknown"; - private final Project project; - private final Set exclusionFromDependencies; - private final Set moduleDependencies; - - /** - * Instantiates a new dependency collector. - */ - public DependencyCollector(Project project, Set exclusionFromDependencies) { - this.project = project; - this.exclusionFromDependencies = exclusionFromDependencies; - this.moduleDependencies = new LinkedHashSet<>(); - } - - public Set getModuleDependencies() { - return moduleDependencies; - } - /** * Resolve and collect dependencies from a {@link SourceSet}. */ - public void collectByConfigurationNames(Set configurationNames) { - if (GradleVersion.current().compareTo(GradleVersion.version("3.3")) < 0) { - List configs = project.getConfigurations().stream() - .filter(configuration -> configurationNames.contains(configuration.getName())) + public static Set getModuleDependencies(Project project, + Set configurationNames) { + if (GradleVersion.current().compareTo(GradleVersion.version("4.0")) < 0) { + try { + List configs = project.getConfigurations().stream() + .filter(configuration -> configurationNames.contains(configuration.getName())) .map(Configuration::getResolvedConfiguration) - .collect(Collectors.toList()); - configs.stream().flatMap(config -> config.getResolvedArtifacts().stream()) - .forEach(this::resolveArtifact); - - // add as individual files for direct dependencies on jars - configs.stream().flatMap(config -> config.getFiles(Specs.satisfyAll()).stream()) - .forEach(this::resolveFileDependency); + .collect(Collectors.toList()); + Stream dependencies = configs.stream() + .flatMap(config -> config.getResolvedArtifacts().stream()) + .map(artifact -> getArtifact(project, artifact)); + + // add as individual files for direct dependencies on jars + Stream directDependencies = configs.stream() + .flatMap(config -> config.getFiles(Specs.satisfyAll()).stream()) + .map(DependencyCollector::getFileDependency); + return Stream.concat(dependencies, directDependencies) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + } catch (GradleException ex) { + // handle build with unresolvable dependencies e.g. missing repository + return new HashSet<>(); + } } else { - project.getConfigurations() - .stream() - .filter(configuration -> configurationNames.contains(configuration.getName())) - .filter(Configuration::isCanBeResolved) - .flatMap(configuration -> getConfigurationArtifacts(configuration).stream()) - .filter(artifact -> !exclusionFromDependencies.contains(artifact.getFile())) - .forEach(this::resolveArtifact); + return project.getConfigurations() + .stream() + .filter(configuration -> configurationNames.contains(configuration.getName())) + .filter(Configuration::isCanBeResolved) + .flatMap(configuration -> getConfigurationArtifacts(configuration).stream()) + .map(artifactResult -> getArtifact(project, artifactResult)) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); } } - private void resolveArtifact(ResolvedArtifactResult artifactResult) { + private static DefaultGradleModuleDependency getArtifact(Project project, + ResolvedArtifactResult artifactResult) { ComponentArtifactIdentifier id = artifactResult.getId(); - resolveArtifact(id, artifactResult.getFile()); + return getArtifact(project, id, artifactResult.getFile()); } - private void resolveArtifact(ResolvedArtifact resolvedArtifact) { + private static DefaultGradleModuleDependency getArtifact(Project project, + ResolvedArtifact resolvedArtifact) { ComponentArtifactIdentifier id = resolvedArtifact.getId(); - resolveArtifact(id, resolvedArtifact.getFile()); + return getArtifact(project, id, resolvedArtifact.getFile()); } - private void resolveArtifact(ComponentArtifactIdentifier id, File artifactFile) { + private static DefaultGradleModuleDependency getArtifact(Project project, + ComponentArtifactIdentifier id, File artifactFile) { if (id instanceof ModuleComponentArtifactIdentifier) { - resolveModuleArtifactDependency((ModuleComponentArtifactIdentifier) id, artifactFile); - } else if (id instanceof OpaqueComponentArtifactIdentifier) { - resolveFileArtifactDependency((OpaqueComponentArtifactIdentifier) id, artifactFile); - } else if (id instanceof ComponentFileArtifactIdentifier) { - resolveFileArtifactDependency((ComponentFileArtifactIdentifier) id, artifactFile); + return getModuleArtifactDependency(project, (ModuleComponentArtifactIdentifier) id, + artifactFile); + } + if (id instanceof OpaqueComponentArtifactIdentifier) { + return getFileArtifactDependency((OpaqueComponentArtifactIdentifier) id, artifactFile); + } + if (id instanceof ComponentFileArtifactIdentifier) { + return getFileArtifactDependency((ComponentFileArtifactIdentifier) id, artifactFile); } + return null; } - private List getConfigurationArtifacts(Configuration config) { + private static List getConfigurationArtifacts(Configuration config) { return config.getIncoming() .artifactView(viewConfiguration -> { viewConfiguration.lenient(true); @@ -117,8 +122,9 @@ private List getConfigurationArtifacts(Configuration con .collect(Collectors.toList()); } - private void resolveModuleArtifactDependency(ModuleComponentArtifactIdentifier artifactIdentifier, - File resolvedArtifactFile) { + private static DefaultGradleModuleDependency getModuleArtifactDependency(Project project, + ModuleComponentArtifactIdentifier artifactIdentifier, File resolvedArtifactFile) { + @SuppressWarnings("unchecked") ArtifactResolutionResult resolutionResult = project.getDependencies() .createArtifactResolutionQuery() .forComponents(artifactIdentifier.getComponentIdentifier()) @@ -134,25 +140,25 @@ private void resolveModuleArtifactDependency(ModuleComponentArtifactIdentifier a } Set resolvedComponents = resolutionResult.getResolvedComponents(); - File sourceJar = getArtifact(resolvedComponents, SourcesArtifact.class); + File sourceJar = getNonClassesArtifact(resolvedComponents, SourcesArtifact.class); if (sourceJar != null) { artifacts.add(new DefaultArtifact(sourceJar.toURI(), "sources")); } - File javaDocJar = getArtifact(resolvedComponents, JavadocArtifact.class); + File javaDocJar = getNonClassesArtifact(resolvedComponents, JavadocArtifact.class); if (javaDocJar != null) { artifacts.add(new DefaultArtifact(javaDocJar.toURI(), "javadoc")); } - moduleDependencies.add(new DefaultGradleModuleDependency( + return new DefaultGradleModuleDependency( artifactIdentifier.getComponentIdentifier().getGroup(), artifactIdentifier.getComponentIdentifier().getModule(), artifactIdentifier.getComponentIdentifier().getVersion(), artifacts - )); + ); } - private File getArtifact(Set resolvedComponents, + private static File getNonClassesArtifact(Set resolvedComponents, Class artifactClass) { for (ComponentArtifactsResult component : resolvedComponents) { Set artifacts = component.getArtifacts(artifactClass); @@ -166,30 +172,30 @@ private File getArtifact(Set resolvedComponents, return null; } - private void resolveFileArtifactDependency(ComponentFileArtifactIdentifier artifactIdentifier, - File resolvedArtifactFile) { - moduleDependencies.add(getFileArtifactDependency( - artifactIdentifier.getCapitalizedDisplayName(), - resolvedArtifactFile - )); + private static DefaultGradleModuleDependency getFileDependency(File resolvedArtifactFile) { + return getFileArtifactDependency( + resolvedArtifactFile.getName(), + resolvedArtifactFile + ); } - private void resolveFileArtifactDependency(OpaqueComponentArtifactIdentifier artifactIdentifier, - File resolvedArtifactFile) { - moduleDependencies.add(getFileArtifactDependency( + private static DefaultGradleModuleDependency getFileArtifactDependency( + ComponentFileArtifactIdentifier artifactIdentifier, File resolvedArtifactFile) { + return getFileArtifactDependency( artifactIdentifier.getCapitalizedDisplayName(), resolvedArtifactFile - )); + ); } - private void resolveFileDependency(File resolvedArtifactFile) { - moduleDependencies.add(getFileArtifactDependency( - resolvedArtifactFile.getName(), - resolvedArtifactFile - )); + private static DefaultGradleModuleDependency getFileArtifactDependency( + OpaqueComponentArtifactIdentifier artifactIdentifier, File resolvedArtifactFile) { + return getFileArtifactDependency( + artifactIdentifier.getCapitalizedDisplayName(), + resolvedArtifactFile + ); } - private GradleModuleDependency getFileArtifactDependency(String displayName, + private static DefaultGradleModuleDependency getFileArtifactDependency(String displayName, File resolvedArtifactFile) { List artifacts = new LinkedList<>(); if (resolvedArtifactFile != null) { diff --git a/plugin/src/test/java/com/microsoft/java/bs/gradle/plugin/GradleBuildServerPluginTest.java b/plugin/src/test/java/com/microsoft/java/bs/gradle/plugin/GradleBuildServerPluginTest.java index 171063c1..7c75c4ce 100644 --- a/plugin/src/test/java/com/microsoft/java/bs/gradle/plugin/GradleBuildServerPluginTest.java +++ b/plugin/src/test/java/com/microsoft/java/bs/gradle/plugin/GradleBuildServerPluginTest.java @@ -20,13 +20,14 @@ import com.microsoft.java.bs.gradle.model.GradleSourceSet; import com.microsoft.java.bs.gradle.model.GradleSourceSets; -import com.microsoft.java.bs.gradle.model.GradleSourceSetsMetadata; import com.microsoft.java.bs.gradle.model.JavaExtension; import com.microsoft.java.bs.gradle.model.ScalaExtension; import com.microsoft.java.bs.gradle.model.SupportedLanguages; +import com.microsoft.java.bs.gradle.model.actions.GetSourceSetsAction; import com.microsoft.java.bs.gradle.model.impl.DefaultGradleSourceSets; + +import org.gradle.tooling.BuildActionExecuter; import org.gradle.tooling.GradleConnector; -import org.gradle.tooling.ModelBuilder; import org.gradle.tooling.ProjectConnection; import org.gradle.util.GradleVersion; import org.junit.jupiter.api.BeforeAll; @@ -47,25 +48,23 @@ static void beforeClass() { } private GradleSourceSets getGradleSourceSets(ProjectConnection connect) throws IOException { - ModelBuilder modelBuilder = - connect.model(GradleSourceSetsMetadata.class); + BuildActionExecuter action = connect.action(new GetSourceSetsAction()); File initScript = PluginHelper.getInitScript(); - modelBuilder + action .addArguments("--init-script", initScript.getAbsolutePath()) .addArguments("-Dorg.gradle.daemon.idletimeout=10") .addArguments("-Dorg.gradle.vfs.watch=false") .addArguments("-Dorg.gradle.logging.level=quiet") .addJvmArguments("-Dbsp.gradle.supportedLanguages=" + String.join(",", SupportedLanguages.allBspNames)); - GradleSourceSetsMetadata sourceSetsMetadata = modelBuilder.get(); - return new DefaultGradleSourceSets( - new ArrayList<>(sourceSetsMetadata.getGradleSourceSetsToClasspath().keySet())); + GradleSourceSets sourceSets = action.run(); + return new DefaultGradleSourceSets(new ArrayList<>(sourceSets.getGradleSourceSets())); } private interface ConnectionConsumer { void accept(ProjectConnection connection) throws IOException; } - + private void withConnection(File projectDir, GradleVersion gradleVersion, ConnectionConsumer consumer) throws IOException { GradleConnector connector = GradleConnector.newConnector() @@ -133,7 +132,7 @@ static Stream versionProvider() { // java source/target options specified in 2.14 // tooling api jar name changed from gradle-tooling-api to gradle-api in 3.0 new GradleJreVersion("3.0", 8), - // artifacts view added in 3.3 + // artifacts view added in 4.0 // RuntimeClasspathConfigurationName added to sourceset in 3.4 // Test#getTestClassesDir -> Test#getTestClassesDirs in 4.0 // sourceSet#getJava#getOutputDir added in 4.0 @@ -185,7 +184,7 @@ void testModelBuilder(GradleVersion gradleVersion) throws IOException { assertEquals(1, gradleSourceSet.getGeneratedSourceDirs().size()); } assertEquals(1, gradleSourceSet.getResourceDirs().size()); - assertNotNull(gradleSourceSet.getSourceOutputDir()); + assertNotNull(gradleSourceSet.getSourceOutputDirs()); assertNotNull(gradleSourceSet.getResourceOutputDir()); assertNotNull(gradleSourceSet.getBuildTargetDependencies()); @@ -211,10 +210,33 @@ void testModelBuilder(GradleVersion gradleVersion) throws IOException { assertNotNull(javaExtension.getSourceCompatibility()); assertNotNull(javaExtension.getTargetCompatibility()); assertNotNull(javaExtension.getCompilerArgs()); + + // dirs not split by language before 4.0 + if (gradleVersion.compareTo(GradleVersion.version("4.0")) >= 0) { + assertTrue(gradleSourceSet.getSourceOutputDirs().stream() + .anyMatch(file -> file.toPath().endsWith(Paths.get("classes", "java", + gradleSourceSet.getSourceSetName())))); + assertTrue(javaExtension.getClassesDir().toPath().endsWith(Paths.get("classes", "java", + gradleSourceSet.getSourceSetName()))); + } else { + assertTrue(gradleSourceSet.getSourceOutputDirs().stream() + .anyMatch(file -> file.toPath().endsWith(Paths.get("classes", + gradleSourceSet.getSourceSetName())))); + assertTrue(javaExtension.getClassesDir().toPath().endsWith(Paths.get("classes", + gradleSourceSet.getSourceSetName()))); + } } }); } + @ParameterizedTest(name = "testGetSourceContainerFromOldGradle {0}") + @MethodSource("versionProvider") + void testMissingRepository(GradleVersion gradleVersion) throws IOException { + withSourceSets("missing-repository", gradleVersion, gradleSourceSets -> { + assertEquals(2, gradleSourceSets.getGradleSourceSets().size()); + }); + } + @ParameterizedTest(name = "testGetSourceContainerFromOldGradle {0}") @MethodSource("versionProvider") void testGetSourceContainerFromOldGradle(GradleVersion gradleVersion) throws IOException { @@ -449,7 +471,6 @@ void testScala2ModelBuilder(GradleVersion gradleVersion) throws IOException { withSourceSets("scala-2", gradleVersion, gradleSourceSets -> { assertEquals(2, gradleSourceSets.getGradleSourceSets().size()); for (GradleSourceSet gradleSourceSet : gradleSourceSets.getGradleSourceSets()) { - assertEquals("scala-2", gradleSourceSet.getProjectName()); assertEquals(":", gradleSourceSet.getProjectPath()); assertTrue(gradleSourceSet.getSourceSetName().equals("main") || gradleSourceSet.getSourceSetName().equals("test")); @@ -464,7 +485,7 @@ void testScala2ModelBuilder(GradleVersion gradleVersion) throws IOException { assertEquals(1, gradleSourceSet.getResourceDirs().size()); assertNotNull(gradleSourceSet.getBuildTargetDependencies()); assertNotNull(gradleSourceSet.getModuleDependencies()); - assertNotNull(gradleSourceSet.getSourceOutputDir()); + assertNotNull(gradleSourceSet.getSourceOutputDirs()); assertNotNull(gradleSourceSet.getResourceOutputDir()); JavaExtension javaExtension = SupportedLanguages.JAVA.getExtension(gradleSourceSet); @@ -505,6 +526,28 @@ void testScala2ModelBuilder(GradleVersion gradleVersion) throws IOException { assertFalse(scalaExtension.getScalaCompilerArgs().isEmpty()); assertTrue(scalaExtension.getScalaCompilerArgs().stream() .anyMatch(arg -> arg.equals("-deprecation"))); + + // dirs not split by language before 4.0 + if (gradleVersion.compareTo(GradleVersion.version("4.0")) >= 0) { + assertTrue(gradleSourceSet.getSourceOutputDirs().stream() + .anyMatch(file -> file.toPath().endsWith(Paths.get("classes", "java", + gradleSourceSet.getSourceSetName())))); + assertTrue(gradleSourceSet.getSourceOutputDirs().stream() + .anyMatch(file -> file.toPath().endsWith(Paths.get("classes", "scala", + gradleSourceSet.getSourceSetName())))); + assertTrue(javaExtension.getClassesDir().toPath().endsWith(Paths.get("classes", "java", + gradleSourceSet.getSourceSetName()))); + assertTrue(scalaExtension.getClassesDir().toPath().endsWith(Paths.get("classes", "scala", + gradleSourceSet.getSourceSetName()))); + } else { + assertTrue(gradleSourceSet.getSourceOutputDirs().stream() + .anyMatch(file -> file.toPath().endsWith(Paths.get("classes", + gradleSourceSet.getSourceSetName())))); + assertTrue(scalaExtension.getClassesDir().toPath().endsWith(Paths.get("classes", + gradleSourceSet.getSourceSetName()))); + assertTrue(javaExtension.getClassesDir().toPath().endsWith(Paths.get("classes", + gradleSourceSet.getSourceSetName()))); + } } }); } @@ -517,7 +560,6 @@ void testScala3ModelBuilder(GradleVersion gradleVersion) throws IOException { withSourceSets("scala-3", gradleVersion, gradleSourceSets -> { assertEquals(2, gradleSourceSets.getGradleSourceSets().size()); for (GradleSourceSet gradleSourceSet : gradleSourceSets.getGradleSourceSets()) { - assertEquals("scala-3", gradleSourceSet.getProjectName()); assertEquals(":", gradleSourceSet.getProjectPath()); assertTrue(gradleSourceSet.getSourceSetName().equals("main") || gradleSourceSet.getSourceSetName().equals("test")); @@ -530,7 +572,7 @@ void testScala3ModelBuilder(GradleVersion gradleVersion) throws IOException { assertEquals(1, gradleSourceSet.getGeneratedSourceDirs().size()); } assertEquals(1, gradleSourceSet.getResourceDirs().size()); - assertNotNull(gradleSourceSet.getSourceOutputDir()); + assertNotNull(gradleSourceSet.getSourceOutputDirs()); assertNotNull(gradleSourceSet.getResourceOutputDir()); assertNotNull(gradleSourceSet.getBuildTargetDependencies()); assertNotNull(gradleSourceSet.getModuleDependencies()); @@ -568,6 +610,17 @@ void testScala3ModelBuilder(GradleVersion gradleVersion) throws IOException { assertFalse(scalaExtension.getScalaCompilerArgs().isEmpty()); assertTrue(scalaExtension.getScalaCompilerArgs().stream() .anyMatch(arg -> arg.equals("-deprecation"))); + + assertTrue(gradleSourceSet.getSourceOutputDirs().stream() + .anyMatch(file -> file.toPath().endsWith(Paths.get("classes", "java", + gradleSourceSet.getSourceSetName())))); + assertTrue(gradleSourceSet.getSourceOutputDirs().stream() + .anyMatch(file -> file.toPath().endsWith(Paths.get("classes", "scala", + gradleSourceSet.getSourceSetName())))); + assertTrue(javaExtension.getClassesDir().toPath().endsWith(Paths.get("classes", "java", + gradleSourceSet.getSourceSetName()))); + assertTrue(scalaExtension.getClassesDir().toPath().endsWith(Paths.get("classes", "scala", + gradleSourceSet.getSourceSetName()))); } }); } diff --git a/server/src/main/java/com/microsoft/java/bs/core/internal/managers/BuildTargetManager.java b/server/src/main/java/com/microsoft/java/bs/core/internal/managers/BuildTargetManager.java index 91ae70dc..badf83b6 100644 --- a/server/src/main/java/com/microsoft/java/bs/core/internal/managers/BuildTargetManager.java +++ b/server/src/main/java/com/microsoft/java/bs/core/internal/managers/BuildTargetManager.java @@ -25,6 +25,7 @@ import com.microsoft.java.bs.gradle.model.JavaExtension; import com.microsoft.java.bs.gradle.model.ScalaExtension; import com.microsoft.java.bs.gradle.model.SupportedLanguages; +import com.microsoft.java.bs.gradle.model.impl.DefaultBuildTargetDependency; import ch.epfl.scala.bsp4j.BuildTarget; import ch.epfl.scala.bsp4j.BuildTargetCapabilities; @@ -50,7 +51,7 @@ public BuildTargetManager() { */ public List store(GradleSourceSets gradleSourceSets) { Map newCache = new HashMap<>(); - Map projectPathToBuildTargetId = new HashMap<>(); + Map dependencyToBuildTargetId = new HashMap<>(); List changedTargets = new LinkedList<>(); for (GradleSourceSet sourceSet : gradleSourceSets.getGradleSourceSets()) { String sourceSetName = sourceSet.getSourceSetName(); @@ -82,13 +83,11 @@ public List store(GradleSourceSets gradleSourceSets) { changedTargets.add(btId); } newCache.put(btId, buildTarget); - // Store the relationship between the project dir and the build target id. - // 'test' and other source sets are ignored. - if ("main".equals(sourceSet.getSourceSetName())) { - projectPathToBuildTargetId.put(sourceSet.getProjectDir().getAbsolutePath(), btId); - } + // Store the relationship between the project/sourceset and the build target id. + BuildTargetDependency dependency = new DefaultBuildTargetDependency(sourceSet); + dependencyToBuildTargetId.put(dependency, btId); } - updateBuildTargetDependencies(newCache.values(), projectPathToBuildTargetId); + updateBuildTargetDependencies(newCache.values(), dependencyToBuildTargetId); this.cache = newCache; return changedTargets; } @@ -169,21 +168,18 @@ private void setScalaBuildTarget(GradleSourceSet sourceSet, ScalaExtension scala /** * Iterate all the gradle build targets, and update their dependencies with - * the help of 'project path to id' mapping. + * the help of 'build target to id' mapping. */ private void updateBuildTargetDependencies( Collection gradleBuildTargets, - Map projectPathToBuildTargetId + Map dependencyToBuildTargetId ) { for (GradleBuildTarget gradleBuildTarget : gradleBuildTargets) { Set buildTargetDependencies = gradleBuildTarget.getSourceSet().getBuildTargetDependencies(); if (buildTargetDependencies != null) { List btDependencies = buildTargetDependencies.stream() - .map(btDependency -> { - String path = btDependency.getProjectDir(); - return projectPathToBuildTargetId.get(path); - }) + .map(dependencyToBuildTargetId::get) .filter(Objects::nonNull) .distinct() .collect(Collectors.toList()); diff --git a/server/src/main/java/com/microsoft/java/bs/core/internal/services/BuildTargetService.java b/server/src/main/java/com/microsoft/java/bs/core/internal/services/BuildTargetService.java index b8e1e2d4..50f2c39a 100644 --- a/server/src/main/java/com/microsoft/java/bs/core/internal/services/BuildTargetService.java +++ b/server/src/main/java/com/microsoft/java/bs/core/internal/services/BuildTargetService.java @@ -243,12 +243,14 @@ public OutputPathsResult getBuildTargetOutputPaths(OutputPathsParams params) { // output path. // TODO: file a BSP spec issue to support additional flags for each output path. - File sourceOutputDir = sourceSet.getSourceOutputDir(); - if (sourceOutputDir != null) { - outputPaths.add(new OutputPathItem( - sourceOutputDir.toURI().toString() + "?kind=source", - OutputPathItemKind.DIRECTORY - )); + Set sourceOutputDirs = sourceSet.getSourceOutputDirs(); + if (sourceOutputDirs != null) { + for (File sourceOutputDir : sourceOutputDirs) { + outputPaths.add(new OutputPathItem( + sourceOutputDir.toURI().toString() + "?kind=source", + OutputPathItemKind.DIRECTORY + )); + } } File resourceOutputDir = sourceSet.getResourceOutputDir(); @@ -425,8 +427,8 @@ public JavacOptionsResult getBuildTargetJavacOptions(JavacOptionsParams params) .map(file -> file.toURI().toString()) .collect(Collectors.toList()); String classesDir; - if (sourceSet.getSourceOutputDir() != null) { - classesDir = sourceSet.getSourceOutputDir().toURI().toString(); + if (javaExtension.getClassesDir() != null) { + classesDir = javaExtension.getClassesDir().toURI().toString(); } else { classesDir = ""; } @@ -464,8 +466,8 @@ public ScalacOptionsResult getBuildTargetScalacOptions(ScalacOptionsParams param .map(file -> file.toURI().toString()) .collect(Collectors.toList()); String classesDir; - if (sourceSet.getSourceOutputDir() != null) { - classesDir = sourceSet.getSourceOutputDir().toURI().toString(); + if (scalaExtension.getClassesDir() != null) { + classesDir = scalaExtension.getClassesDir().toURI().toString(); } else { classesDir = ""; } diff --git a/server/src/test/java/com/microsoft/java/bs/core/internal/gradle/GradleApiConnectorTest.java b/server/src/test/java/com/microsoft/java/bs/core/internal/gradle/GradleApiConnectorTest.java index 2ee6f41e..f3c3a202 100644 --- a/server/src/test/java/com/microsoft/java/bs/core/internal/gradle/GradleApiConnectorTest.java +++ b/server/src/test/java/com/microsoft/java/bs/core/internal/gradle/GradleApiConnectorTest.java @@ -140,10 +140,7 @@ void testGetGradleHasTests() { @Test void testCompositeBuild1() { File projectDir = projectPath.resolve("composite-build-1").toFile(); - PreferenceManager preferenceManager = new PreferenceManager(); - preferenceManager.setPreferences(new Preferences()); - GradleApiConnector connector = new GradleApiConnector(preferenceManager); - GradleSourceSets gradleSourceSets = connector.getGradleSourceSets(projectDir.toURI(), null); + GradleSourceSets gradleSourceSets = getGradleSourceSets(projectDir); assertEquals(4, gradleSourceSets.getGradleSourceSets().size()); findSourceSet(gradleSourceSets, "projectA [main]"); findSourceSet(gradleSourceSets, "projectA [test]"); @@ -154,10 +151,7 @@ void testCompositeBuild1() { @Test void testCompositeBuild2() { File projectDir = projectPath.resolve("composite-build-2").toFile(); - PreferenceManager preferenceManager = new PreferenceManager(); - preferenceManager.setPreferences(new Preferences()); - GradleApiConnector connector = new GradleApiConnector(preferenceManager); - GradleSourceSets gradleSourceSets = connector.getGradleSourceSets(projectDir.toURI(), null); + GradleSourceSets gradleSourceSets = getGradleSourceSets(projectDir); assertEquals(6, gradleSourceSets.getGradleSourceSets().size()); findSourceSet(gradleSourceSets, "app [test]"); findSourceSet(gradleSourceSets, "string-utils [test]"); diff --git a/server/src/test/java/com/microsoft/java/bs/core/internal/managers/BuildTargetManagerTest.java b/server/src/test/java/com/microsoft/java/bs/core/internal/managers/BuildTargetManagerTest.java index 77a856c6..94b92d2e 100644 --- a/server/src/test/java/com/microsoft/java/bs/core/internal/managers/BuildTargetManagerTest.java +++ b/server/src/test/java/com/microsoft/java/bs/core/internal/managers/BuildTargetManagerTest.java @@ -26,6 +26,7 @@ import com.microsoft.java.bs.gradle.model.JavaExtension; import com.microsoft.java.bs.gradle.model.LanguageExtension; import com.microsoft.java.bs.gradle.model.SupportedLanguages; +import com.microsoft.java.bs.gradle.model.impl.DefaultBuildTargetDependency; import ch.epfl.scala.bsp4j.BuildTarget; import ch.epfl.scala.bsp4j.JvmBuildTarget; @@ -90,13 +91,15 @@ void testJvmExtensionEx() { @Test void testBuildTargetDependency() { + File fooProjectDir = new File("foo"); + String fooSourceSetName = "main"; GradleSourceSet gradleSourceSetFoo = getMockedTestGradleSourceSet(); when(gradleSourceSetFoo.getProjectPath()).thenReturn(":foo"); - when(gradleSourceSetFoo.getProjectDir()).thenReturn(new File("foo")); + when(gradleSourceSetFoo.getProjectDir()).thenReturn(fooProjectDir); + when(gradleSourceSetFoo.getSourceSetName()).thenReturn(fooSourceSetName); - - BuildTargetDependency buildTargetDependency = mock(BuildTargetDependency.class); - when(buildTargetDependency.getProjectDir()).thenReturn(new File("foo").getAbsolutePath()); + BuildTargetDependency buildTargetDependency = new DefaultBuildTargetDependency( + fooProjectDir.getAbsolutePath(), fooSourceSetName); Set dependencies = new HashSet<>(); dependencies.add(buildTargetDependency); GradleSourceSet gradleSourceSetBar = getMockedTestGradleSourceSet(); diff --git a/server/src/test/java/com/microsoft/java/bs/core/internal/server/BuildTargetServerIntegrationTest.java b/server/src/test/java/com/microsoft/java/bs/core/internal/server/BuildTargetServerIntegrationTest.java index 63501f36..b1ed6a8a 100644 --- a/server/src/test/java/com/microsoft/java/bs/core/internal/server/BuildTargetServerIntegrationTest.java +++ b/server/src/test/java/com/microsoft/java/bs/core/internal/server/BuildTargetServerIntegrationTest.java @@ -70,13 +70,11 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable; import com.microsoft.java.bs.core.Launcher; import com.microsoft.java.bs.core.internal.gradle.GradleApiConnector; import com.microsoft.java.bs.core.internal.managers.BuildTargetManager; import com.microsoft.java.bs.core.internal.managers.PreferenceManager; -import com.microsoft.java.bs.core.internal.model.Preferences; import com.microsoft.java.bs.core.internal.services.BuildTargetService; import com.microsoft.java.bs.core.internal.services.LifecycleService; import com.microsoft.java.bs.core.internal.utils.JsonUtils; @@ -1796,6 +1794,8 @@ void testSpock() { passingTestParams.setDataKind(TestParamsDataKind.SCALA_TEST); passingTestParams.setData(passingScalaTestParams); TestResult passingTestResult = gradleBuildServer.buildTargetTest(passingTestParams).join(); + // Caveat - this may fail if using too high JDK version for SPOCK. + // Dependency `org.spockframework:spock-core:2.3-groovy-4.0` may need upgrading. assertEquals(StatusCode.OK, passingTestResult.getStatusCode()); assertEquals("originId", passingTestResult.getOriginId()); client.waitOnStartReports(4); diff --git a/server/src/test/java/com/microsoft/java/bs/core/internal/services/BuildTargetServiceTest.java b/server/src/test/java/com/microsoft/java/bs/core/internal/services/BuildTargetServiceTest.java index 8eb2150c..dbf5a735 100644 --- a/server/src/test/java/com/microsoft/java/bs/core/internal/services/BuildTargetServiceTest.java +++ b/server/src/test/java/com/microsoft/java/bs/core/internal/services/BuildTargetServiceTest.java @@ -186,7 +186,9 @@ void testGetBuildTargetOutputPaths() { when(gradleBuildTarget.getSourceSet()).thenReturn(gradleSourceSet); File sourceOutputDir = new File(("sourceOutputDir")); - when(gradleSourceSet.getSourceOutputDir()).thenReturn(sourceOutputDir); + Set sourceOutputDirs = new HashSet<>(); + sourceOutputDirs.add(sourceOutputDir); + when(gradleSourceSet.getSourceOutputDirs()).thenReturn(sourceOutputDirs); File resourceOutputDir = new File(("resourceOutputDir")); when(gradleSourceSet.getResourceOutputDir()).thenReturn(resourceOutputDir); diff --git a/testProjects/missing-repository/build.gradle b/testProjects/missing-repository/build.gradle new file mode 100644 index 00000000..d3cfc1dc --- /dev/null +++ b/testProjects/missing-repository/build.gradle @@ -0,0 +1,14 @@ +plugins { + id 'java' +} +// repo list deliberately NOT included to check plugin doesn't throw exception + +if (org.gradle.util.GradleVersion.current().compareTo(org.gradle.util.GradleVersion.version("4.6")) >= 0) { + dependencies { + testImplementation('org.testng:testng:7.9.0') + } +} else { + dependencies { + testCompile('org.testng:testng:7.9.0') + } +} diff --git a/testProjects/missing-repository/settings.gradle b/testProjects/missing-repository/settings.gradle new file mode 100644 index 00000000..d03b401a --- /dev/null +++ b/testProjects/missing-repository/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'missing-repository' \ No newline at end of file diff --git a/testProjects/spock/build.gradle b/testProjects/spock/build.gradle index 05fdcf68..61cf481d 100644 --- a/testProjects/spock/build.gradle +++ b/testProjects/spock/build.gradle @@ -8,7 +8,7 @@ repositories { } dependencies { - testImplementation 'org.spockframework:spock-core:2.3-groovy-4.0' + testImplementation 'org.spockframework:spock-core:2.4-M4-groovy-4.0' testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } From 714063ffd9afb16ed9e83b4f840794c182ee2e96 Mon Sep 17 00:00:00 2001 From: Arthur McGibbon Date: Wed, 24 Jul 2024 11:37:58 +0100 Subject: [PATCH 2/2] Code tidy --- .../com/microsoft/java/bs/gradle/model/JavaExtension.java | 5 ----- .../microsoft/java/bs/gradle/model/LanguageExtension.java | 1 - .../com/microsoft/java/bs/gradle/model/ScalaExtension.java | 5 ----- .../java/bs/gradle/model/actions/GetSourceSetsAction.java | 2 +- 4 files changed, 1 insertion(+), 12 deletions(-) diff --git a/model/src/main/java/com/microsoft/java/bs/gradle/model/JavaExtension.java b/model/src/main/java/com/microsoft/java/bs/gradle/model/JavaExtension.java index 515f18ea..dc8220a0 100644 --- a/model/src/main/java/com/microsoft/java/bs/gradle/model/JavaExtension.java +++ b/model/src/main/java/com/microsoft/java/bs/gradle/model/JavaExtension.java @@ -35,9 +35,4 @@ public interface JavaExtension extends LanguageExtension { * The list of compiler arguments. */ public List getCompilerArgs(); - - /** - * The classes directory. - */ - File getClassesDir(); } diff --git a/model/src/main/java/com/microsoft/java/bs/gradle/model/LanguageExtension.java b/model/src/main/java/com/microsoft/java/bs/gradle/model/LanguageExtension.java index 475cc4fb..34c2fc85 100644 --- a/model/src/main/java/com/microsoft/java/bs/gradle/model/LanguageExtension.java +++ b/model/src/main/java/com/microsoft/java/bs/gradle/model/LanguageExtension.java @@ -80,5 +80,4 @@ public interface LanguageExtension extends Serializable { * or null if the cast fails. */ ScalaExtension getAsScalaExtension(); - } diff --git a/model/src/main/java/com/microsoft/java/bs/gradle/model/ScalaExtension.java b/model/src/main/java/com/microsoft/java/bs/gradle/model/ScalaExtension.java index 6c62f0c1..0120f086 100644 --- a/model/src/main/java/com/microsoft/java/bs/gradle/model/ScalaExtension.java +++ b/model/src/main/java/com/microsoft/java/bs/gradle/model/ScalaExtension.java @@ -36,9 +36,4 @@ public interface ScalaExtension extends LanguageExtension { * E.g. scala-library, scala-compiler and scala-reflect. */ List getScalaJars(); - - /** - * The classes directory. - */ - File getClassesDir(); } diff --git a/model/src/main/java/com/microsoft/java/bs/gradle/model/actions/GetSourceSetsAction.java b/model/src/main/java/com/microsoft/java/bs/gradle/model/actions/GetSourceSetsAction.java index a6d7e308..e655ef09 100644 --- a/model/src/main/java/com/microsoft/java/bs/gradle/model/actions/GetSourceSetsAction.java +++ b/model/src/main/java/com/microsoft/java/bs/gradle/model/actions/GetSourceSetsAction.java @@ -89,7 +89,7 @@ private void fetchIncludedBuilds(BuildController buildController, GradleBuild bu private List fetchModels(BuildController buildController, Collection builds) { - List projectActions = new ArrayList(); + List projectActions = new ArrayList<>(); for (GradleBuild build : builds) { for (BasicGradleProject project : build.getProjects()) { projectActions.add(new GetSourceSetAction(project));