Skip to content

Commit

Permalink
Simplify xtextTooling dependency setup
Browse files Browse the repository at this point in the history
Create the xtextTooling Configuration for each SourceSet right away
rather than lazily. This makes it visible in the output of the `dependencies`
task and allows users to easily customize it in their build scripts, e.g.
when there is another issue like #187

Use lazy callback APIs like `eachDependency` (to align versions for Xtext itself)
and `withDependencies` (to add the BOM when that Xtext version has one) instead of
using the Gradle-interna LazilyInitializedFileCollection.

Load all dependencies, including the Xtext languages into the xtextTooling Configuration
so that all version numbers are aligned. Previously it was possible for the xtextLanguages
Configuration to bring in incompatible versions of libraries like eclipse.core.resources,
which was especially visible when trying to use Xcore with this plugin. Now everything
is aligned with the Xtext BOM and/or our dependency resolution rules.

Remove the complicated "classpathInferrer" logic in favor of this new approach.
This also means that the Xtend plugin is now much simpler, since it only needs to add
the xtend.core dependency to the xtextTooling Configurations and that version will automatically
be aligned by our resolution rules.

This raises the minimum Gradle version to 4.7, which seems very reasonable given that the
latest is 7.2.
  • Loading branch information
oehme committed Aug 31, 2021
1 parent 8a56d10 commit ec4358b
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 108 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
minimumXtextVersion = 2.9.0
latestXtextVersion = 2.25.0
minimumGradleVersion = 4.3
minimumGradleVersion = 4.7
latestGradleVersion = 7.2
systemProp.http.connectionTimeout=120000
systemProp.http.socketTimeout=120000
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
package org.xtext.gradle

import org.gradle.api.GradleException
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.file.FileCollection
import org.gradle.api.internal.plugins.DslObject
import org.gradle.api.plugins.JavaBasePlugin
import org.gradle.api.plugins.JavaPluginConvention
import org.xtext.gradle.protocol.GradleInstallDebugInfoRequest.SourceInstaller
import org.xtext.gradle.tasks.XtextClasspathInferrer
import org.xtext.gradle.tasks.XtextExtension
import org.xtext.gradle.tasks.XtextSourceDirectorySet
import org.xtext.gradle.tasks.internal.XtendSourceSet

class XtendLanguageBasePlugin implements Plugin<Project> {
Expand All @@ -25,6 +21,9 @@ class XtendLanguageBasePlugin implements Plugin<Project> {
plugin(XtextBuilderPlugin)
]
xtext = project.extensions.getByType(XtextExtension)
xtext.sourceSets.all [
project.dependencies.add(qualifyConfigurationName('xtextTooling'), 'org.eclipse.xtend:org.eclipse.xtend.core')
]
val xtend = xtext.languages.create("xtend") [
setup = "org.eclipse.xtend.core.XtendStandaloneSetup"
generator.outlet => [
Expand All @@ -34,7 +33,6 @@ class XtendLanguageBasePlugin implements Plugin<Project> {
sourceInstaller = SourceInstaller.SMAP
]
]
automaticallyInferXtendCompilerClasspath
project.extensions.add("xtend", xtend)
val java = project.convention.getPlugin(JavaPluginConvention)
java.sourceSets.all [ sourceSet |
Expand All @@ -45,20 +43,4 @@ class XtendLanguageBasePlugin implements Plugin<Project> {
new DslObject(sourceSet).convention.plugins.put("xtend", xtendSourceSet)
]
}

private def void automaticallyInferXtendCompilerClasspath() {
xtext.classpathInferrers += new XtextClasspathInferrer() {
override inferXtextClasspath(XtextSourceDirectorySet sourceSet, FileCollection xtextClasspath, FileCollection classpath) {
val version = xtext.getXtextVersion(classpath) ?: xtext.getXtextVersion(xtextClasspath)
if (version === null) {
throw new GradleException('''Could not infer Xtext classpath, because xtext.version was not set and no xtext libraries were found on the «classpath» classpath''')
}
val xtendTooling = project.configurations.create(sourceSet.qualifyConfigurationName("xtendTooling"))
xtendTooling.dependencies += project.dependencies.create("org.eclipse.xtend:org.eclipse.xtend.core:" + version)
xtext.makeXtextCompatible(xtendTooling)
xtext.forceXtextVersion(xtendTooling, version)
xtendTooling.plus(xtextClasspath)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,14 @@ import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.Configuration
import org.gradle.api.file.FileCollection
import org.gradle.api.internal.file.collections.LazilyInitializedFileCollection
import org.gradle.api.internal.plugins.DslObject
import org.gradle.api.internal.tasks.TaskDependencyResolveContext
import org.gradle.api.plugins.JavaBasePlugin
import org.gradle.api.plugins.JavaPluginConvention
import org.gradle.api.tasks.Delete
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.plugins.ide.eclipse.EclipsePlugin
import org.gradle.plugins.ide.eclipse.model.EclipseModel
import org.xtext.gradle.tasks.Outlet
import org.xtext.gradle.tasks.XtextClasspathInferrer
import org.xtext.gradle.tasks.XtextEclipseSettings
import org.xtext.gradle.tasks.XtextExtension
import org.xtext.gradle.tasks.XtextGenerate
Expand All @@ -46,8 +42,6 @@ class XtextBuilderPlugin implements Plugin<Project> {

xtext = project.extensions.create("xtext", XtextExtension, project);
xtextLanguages = project.configurations.create("xtextLanguages")
xtext.makeXtextCompatible(xtextLanguages)
automaticallyInferXtextCoreClasspath
createGeneratorTasks
configureOutletDefaults
integrateWithJavaPlugin
Expand All @@ -56,27 +50,12 @@ class XtextBuilderPlugin implements Plugin<Project> {

private def createGeneratorTasks() {
xtext.sourceSets.all [ sourceSet |
project.tasks.create(sourceSet.generatorTaskName, XtextGenerate) [
val generatorTask = project.tasks.create(sourceSet.generatorTaskName, XtextGenerate) [
sources = sourceSet
sourceSetOutputs = sourceSet.output
languages = xtext.languages
val XtextGenerate generate = it
xtextClasspath = new LazilyInitializedFileCollection() {
override getDisplayName() {
"Xtext classpath"
}

override createDelegate() {
inferXtextClasspath(sourceSet, generate.classpath)
}

override visitDependencies(TaskDependencyResolveContext context) {
context.add(generate.classpath)
context.add(xtextLanguages)
}

}
]
setupXtextClasspath(sourceSet, generatorTask)
project.tasks.create('clean' + sourceSet.generatorTaskName.toFirstUpper, Delete) [
delete([
xtext.languages.map[generator.outlets].flatten.filter[cleanAutomatically].map [
Expand All @@ -87,33 +66,44 @@ class XtextBuilderPlugin implements Plugin<Project> {
]
}

private def inferXtextClasspath(XtextSourceDirectorySet sourceSet, FileCollection classpath) {
xtext.classpathInferrers.fold(xtextLanguages as FileCollection) [ newXextClasspath, inferrer |
inferrer.inferXtextClasspath(sourceSet, newXextClasspath, classpath)
private def setupXtextClasspath(XtextSourceDirectorySet sourceSet, XtextGenerate generatorTask) {
val xtextTooling = project.configurations.create(sourceSet.qualifyConfigurationName("xtextTooling"))
generatorTask.xtextClasspath = xtextTooling
xtextTooling.extendsFrom(xtextLanguages)
xtextTooling.exclude(#{"group" -> "asm"})
#[
'org.eclipse.xtext:org.eclipse.xtext',
'org.eclipse.xtext:org.eclipse.xtext.smap',
'org.eclipse.xtext:org.eclipse.xtext.xbase',
'org.eclipse.xtext:org.eclipse.xtext.java'
].forEach[project.dependencies.add(xtextTooling.name, it)]
val xtextVersion = new LazyXtextVersion(xtext, xtextLanguages, generatorTask)
xtextTooling.withDependencies [
val version = xtextVersion.getVersion
if (version === null) {
return
}
if (project.supportsJvmEcoSystemplugin && new ComparableVersion(version) >= new ComparableVersion("2.17.1")) {
add(project.dependencies.enforcedPlatform('''org.eclipse.xtext:xtext-dev-bom'''))
}
]
}

private def automaticallyInferXtextCoreClasspath() {
xtext.classpathInferrers += new XtextClasspathInferrer() {
override inferXtextClasspath(XtextSourceDirectorySet sourceSet, FileCollection xtextClasspath, FileCollection classpath) {
val version = xtext.getXtextVersion(classpath) ?: xtext.getXtextVersion(xtextClasspath)
if (version === null) {
throw new GradleException('''Could not infer Xtext classpath, because xtext.version was not set and no xtext libraries were found on the «classpath» classpath''')
}
val xtextTooling = project.configurations.create(sourceSet.qualifyConfigurationName("xtextTooling"))
xtextTooling.dependencies += #[
'org.eclipse.xtext:org.eclipse.xtext',
'org.eclipse.xtext:org.eclipse.xtext.smap',
'org.eclipse.xtext:org.eclipse.xtext.xbase',
'org.eclipse.xtext:org.eclipse.xtext.java',
'org.eclipse.jdt:org.eclipse.jdt.core:3.10.0'
]
.map[project.dependencies.create(it)]
xtext.makeXtextCompatible(xtextTooling)
xtext.forceXtextVersion(xtextTooling, version)
xtextTooling.plus(xtextClasspath)
xtextTooling.resolutionStrategy.eachDependency [
val version = xtextVersion.getVersion
if (version === null) {
return
}
}
if (requested.group == "org.eclipse.xtext" || requested.group == "org.eclipse.xtend")
useVersion(version)

if (!project.supportsJvmEcoSystemplugin || new ComparableVersion(version) < new ComparableVersion("2.17.1")) {
if (requested.group == "com.google.inject" && requested.name == "guice")
useVersion("5.0.1")
if (requested.name == "org.eclipse.equinox.common")
useTarget("org.eclipse.platform:org.eclipse.equinox.common:3.13.0")
if (requested.name == "org.eclipse.core.runtime")
useTarget("org.eclipse.platform:org.eclipse.core.runtime:3.19.0")
}
]
}

private def configureOutletDefaults() {
Expand Down Expand Up @@ -179,4 +169,27 @@ class XtextBuilderPlugin implements Plugin<Project> {
}
]
}

private static class LazyXtextVersion {
val XtextExtension xtext
val Configuration languages
val XtextGenerate task
var String version

new (XtextExtension xtext, Configuration languages, XtextGenerate task) {
this.xtext = xtext
this.languages = languages
this.task = task
}

def String getVersion() {
if (version === null) {
version = xtext.getXtextVersion(task.classpath) ?: xtext.getXtextVersion(languages)
if (version === null && !task.mainSources.empty) {
throw new GradleException('''Could not infer Xtext classpath for «task», because xtext.version was not set and no xtext libraries were found in «task.classpath» or «languages»''')
}
}
version
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
package org.xtext.gradle.tasks;

import com.google.common.base.CaseFormat
import com.google.common.collect.Lists
import java.io.File
import java.util.List
import java.util.Map
import java.util.Set
import java.util.regex.Pattern
import org.apache.maven.artifact.versioning.ComparableVersion
import org.eclipse.xtend.lib.annotations.Accessors
import org.gradle.api.Action
import org.gradle.api.Named
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Internal
Expand All @@ -29,15 +25,13 @@ class XtextExtension {
@Accessors String version
@Accessors val NamedDomainObjectContainer<XtextSourceDirectorySet> sourceSets
@Accessors val NamedDomainObjectContainer<Language> languages;
@Accessors val List<XtextClasspathInferrer> classpathInferrers;

Project project

new(Project project) {
this.project = project
sourceSets = project.container(XtextSourceDirectorySet)[name|project.instantiate(DefaultXtextSourceDirectorySet, name, project, this)]
languages = project.container(Language)[name|project.instantiate(Language, name, project)]
classpathInferrers = Lists.newArrayList
}

def sourceSets(Action<? super NamedDomainObjectContainer<XtextSourceDirectorySet>> configureAction) {
Expand Down Expand Up @@ -68,30 +62,6 @@ class XtextExtension {
return matcher.group(2)
}
}

def void forceXtextVersion(Configuration dependencies, String xtextVersion) {
dependencies.resolutionStrategy.eachDependency [
if (requested.group == "org.eclipse.xtext" || requested.group == "org.eclipse.xtend")
useVersion(xtextVersion)
]

if (project.supportsJvmEcoSystemplugin && new ComparableVersion(xtextVersion)>= new ComparableVersion("2.17.1")) {
dependencies.dependencies += project.dependencies.enforcedPlatform('''org.eclipse.xtext:xtext-dev-bom:«xtextVersion»''')
} else {
dependencies.resolutionStrategy.eachDependency [
if (requested.group == "com.google.inject" && requested.name == "guice")
useVersion("5.0.1")
if (requested.name == "org.eclipse.equinox.common")
useTarget("org.eclipse.platform:org.eclipse.equinox.common:3.13.0")
if (requested.name == "org.eclipse.core.runtime")
useTarget("org.eclipse.platform:org.eclipse.core.runtime:3.19.0")
]
}
}

def void makeXtextCompatible(Configuration dependencies) {
dependencies.exclude(#{'group' -> 'asm'})
}
}

@Accessors
Expand Down

0 comments on commit ec4358b

Please sign in to comment.