diff --git a/platforms/documentation/docs/src/docs/release/notes.md b/platforms/documentation/docs/src/docs/release/notes.md index 137c43d9a0f5c..d74d358ff4c31 100644 --- a/platforms/documentation/docs/src/docs/release/notes.md +++ b/platforms/documentation/docs/src/docs/release/notes.md @@ -166,6 +166,47 @@ try (GroupTestEventReporter outer = root.reportTestGroup("OuterNestingSuite")) { Nested events are reflected in the HTML test reports, providing clear traceability. +#### Scala version can be declared explicitly + +Starting in this version of Gradle, when applying the [scala-base or scala](userguide/scala_plugin.html) plugins, you can now explicitly declare the Scala version on the `scala` extension. +This allows Gradle to automatically resolve the required Scala toolchain dependencies, eliminating the need for the user to declare them manually. +It also removes the need to infer the Scala version from the production runtime classpath, which was error-prone. + +Previously, you had to declare a `scala-library` dependency, like this: + +```kotlin +plugins { + id("scala") +} + +repositories { + mavenCentral() +} + +dependencies { + implementation("org.scala-lang:scala-library:2.13.12") + // OR + implementation("org.scala-lang:scala3-library_3:3.6.3") +} +``` + +Now, you can explicitly set the Scala version in the `scala` extension, and the `scala-library` dependency is no longer required: +```kotlin +plugins { + id("scala") +} + +repositories { + mavenCentral() +} + +scala { + scalaVersion = "2.13.12" + // OR + scalaVersion = "3.6.3" +} +``` + #### Distribution base plugin introduced Gradle now includes a `distribution-base` plugin, which mirrors the functionality of the [Distribution Plugin](userguide/distribution_plugin.html) but does not add a default distribution. diff --git a/platforms/documentation/docs/src/docs/userguide/platforms/jvm/scala_plugin.adoc b/platforms/documentation/docs/src/docs/userguide/platforms/jvm/scala_plugin.adoc index fda766ff173ac..8577f60d7f4cd 100644 --- a/platforms/documentation/docs/src/docs/userguide/platforms/jvm/scala_plugin.adoc +++ b/platforms/documentation/docs/src/docs/userguide/platforms/jvm/scala_plugin.adoc @@ -115,25 +115,50 @@ include::sample[dir="snippets/scala/customizedLayout/kotlin",files="build.gradle include::sample[dir="snippets/scala/customizedLayout/groovy",files="build.gradle[tags=custom-source-locations]"] ==== -[[sec:scala_dependency_management]] -== Dependency management +== Scala version -Scala projects need to declare a `scala-library` dependency. This dependency will then be used on compile and runtime class paths. It will also be used to get hold of the Scala compiler and Scaladoc tool, respectively.footnote:[See <<#sec:configure_scala_classpath,Automatic configuration of Scala classpath>>.] +The Scala version can be declared directly on the `scala` extension. +Dependencies are automatically added to the `scalaToolchain` configuration based on the declared Scala version. +The `scalaToolchainRuntimeClasspath` configuration resolves the dependencies declared on the `scalaToolchain` configuration and contains files required to run the Scala compiler. -If Scala is used for production code, the `scala-library` dependency should be added to the `implementation` configuration: +NOTE: Direct dependencies on the Scala SDK do not need to be declared when the `scalaVersion` property is set. + +.Declaring a Scala 2 version +==== +include::sample[dir="snippets/scala/quickstart/kotlin",files="build.gradle.kts[tags=scala-version]"] +include::sample[dir="snippets/scala/quickstart/groovy",files="build.gradle[tags=scala-version]"] +==== + +Configuring Gradle to use Scala 3 is no different from Scala 2. + +.Declaring a Scala 3 version +==== +include::sample[dir="snippets/scala/scala3/kotlin",files="build.gradle.kts[tags=scala-version]"] +include::sample[dir="snippets/scala/scala3/groovy",files="build.gradle[tags=scala-version]"] +==== + +NOTE: The `scalaVersion` property is incubating. + +=== Declaring Scala dependencies + +When not using the `scalaVersion` property, the Scala SDK dependency must be declared manually on the `implementation` configuration. +This pattern is not preferred, as it relies on <<#sec:configure_scala_classpath,inferring the Scala classpath>> from the production runtime classpath. +Omitting the Scala version from the `scala` extension will be deprecated in a future Gradle version. + +Scala 2 projects need to declare a `scala-library` dependency. .Declaring a Scala dependency for production code ==== -include::sample[dir="snippets/scala/quickstart/kotlin",files="build.gradle.kts[tags=scala-dependency]"] -include::sample[dir="snippets/scala/quickstart/groovy",files="build.gradle[tags=scala-dependency]"] +include::sample[dir="snippets/scala/quickstart/kotlin/inferred-version",files="build.gradle.kts[tags=scala-dependency]"] +include::sample[dir="snippets/scala/quickstart/groovy/inferred-version",files="build.gradle[tags=scala-dependency]"] ==== -If you want to use Scala 3 instead of the `scala-library` dependency you should add the `scala3-library_3` dependency: +Scala 3 projects need to declare a `scala3-library_3` dependency instead: .Declaring a Scala 3 dependency for production code ==== -include::sample[dir="snippets/scala/scala3/kotlin",files="build.gradle.kts"] -include::sample[dir="snippets/scala/scala3/groovy",files="build.gradle"] +include::sample[dir="snippets/scala/scala3/kotlin/inferred-version",files="build.gradle.kts[tags=scala-dependency]"] +include::sample[dir="snippets/scala/scala3/groovy/inferred-version",files="build.gradle[tags=scala-dependency]"] ==== If Scala is only used for test code, the `scala-library` dependency should be added to the `testImplementation` configuration: @@ -147,9 +172,11 @@ include::sample[dir="snippets/scala/scalaDependency/groovy",files="build.gradle[ [[sec:configure_scala_classpath]] == Automatic configuration of scalaClasspath -The `ScalaCompile` and `ScalaDoc` tasks consume Scala code in two ways: on their `classpath`, and on their `scalaClasspath`. The former is used to locate classes referenced by the source code, and will typically contain `scala-library` along with other libraries. The latter is used to load and execute the Scala compiler and Scaladoc tool, respectively, and should only contain the `scala-compiler` library and its dependencies. +The `ScalaCompile` and `ScalaDoc` tasks consume Scala code in two ways: on their `classpath`, and on their `scalaClasspath`. +The former is used to locate classes referenced by the source code, and will typically contain `scala-library` along with other libraries. +The latter is used to load and execute the Scala compiler and Scaladoc tool, respectively, and should only contain the `scala-compiler` library and its dependencies. -Unless a task's `scalaClasspath` is configured explicitly, the Scala (base) plugin will try to infer it from the task's `classpath`. This is done as follows: +If the `scala` extension's `scalaVersion` property is not set and the task's `scalaClasspath` is not configured explicitly, the Scala (base) plugin will try to infer the classpath from the task's `classpath`. This is done as follows: * If a `scala-library` jar is found on `classpath`, and the project has at least one repository declared, a corresponding `scala-compiler` repository dependency will be added to `scalaClasspath`. * Otherwise, execution of the task will fail with a message saying that `scalaClasspath` could not be inferred. diff --git a/platforms/documentation/docs/src/snippets/scala/compilerPlugin/groovy/build.gradle b/platforms/documentation/docs/src/snippets/scala/compilerPlugin/groovy/build.gradle index 029b5436afb19..466e55052c7eb 100644 --- a/platforms/documentation/docs/src/snippets/scala/compilerPlugin/groovy/build.gradle +++ b/platforms/documentation/docs/src/snippets/scala/compilerPlugin/groovy/build.gradle @@ -1,14 +1,17 @@ plugins { - id 'scala' + id("scala") } repositories { mavenCentral() } +scala { + scalaVersion = "2.13.12" +} + // tag::compiler-plugin[] dependencies { - implementation "org.scala-lang:scala-library:2.13.12" - scalaCompilerPlugins "org.typelevel:kind-projector_2.13.12:0.13.2" + scalaCompilerPlugins("org.typelevel:kind-projector_2.13.12:0.13.2") } // end::compiler-plugin[] diff --git a/platforms/documentation/docs/src/snippets/scala/compilerPlugin/kotlin/build.gradle.kts b/platforms/documentation/docs/src/snippets/scala/compilerPlugin/kotlin/build.gradle.kts index c069dac5e911c..466e55052c7eb 100644 --- a/platforms/documentation/docs/src/snippets/scala/compilerPlugin/kotlin/build.gradle.kts +++ b/platforms/documentation/docs/src/snippets/scala/compilerPlugin/kotlin/build.gradle.kts @@ -1,14 +1,17 @@ plugins { - scala + id("scala") } repositories { mavenCentral() } +scala { + scalaVersion = "2.13.12" +} + // tag::compiler-plugin[] dependencies { - implementation("org.scala-lang:scala-library:2.13.12") scalaCompilerPlugins("org.typelevel:kind-projector_2.13.12:0.13.2") } // end::compiler-plugin[] diff --git a/platforms/documentation/docs/src/snippets/scala/customizedLayout/groovy/build.gradle b/platforms/documentation/docs/src/snippets/scala/customizedLayout/groovy/build.gradle index ddaa4743d2836..364f6975c86bb 100644 --- a/platforms/documentation/docs/src/snippets/scala/customizedLayout/groovy/build.gradle +++ b/platforms/documentation/docs/src/snippets/scala/customizedLayout/groovy/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'scala' + id("scala") } version = '1.0' @@ -8,9 +8,12 @@ repositories { mavenCentral() } +scala { + scalaVersion = "2.13.12" +} + dependencies { - implementation 'org.scala-lang:scala-library:2.13.12' - testImplementation 'junit:junit:4.13' + testImplementation("junit:junit:4.13") } // tag::custom-source-locations[] diff --git a/platforms/documentation/docs/src/snippets/scala/customizedLayout/kotlin/build.gradle.kts b/platforms/documentation/docs/src/snippets/scala/customizedLayout/kotlin/build.gradle.kts index 4b238007dcd20..94d12016429f6 100644 --- a/platforms/documentation/docs/src/snippets/scala/customizedLayout/kotlin/build.gradle.kts +++ b/platforms/documentation/docs/src/snippets/scala/customizedLayout/kotlin/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - scala + id("scala") } version = "1.0" @@ -8,8 +8,11 @@ repositories { mavenCentral() } +scala { + scalaVersion = "2.13.12" +} + dependencies { - implementation("org.scala-lang:scala-library:2.13.12") testImplementation("junit:junit:4.13") } diff --git a/platforms/documentation/docs/src/snippets/scala/force/groovy/build.gradle b/platforms/documentation/docs/src/snippets/scala/force/groovy/build.gradle index fafcb7124ab87..2de50b3d7023d 100644 --- a/platforms/documentation/docs/src/snippets/scala/force/groovy/build.gradle +++ b/platforms/documentation/docs/src/snippets/scala/force/groovy/build.gradle @@ -1,13 +1,13 @@ plugins { - id 'scala' + id("scala") } repositories { mavenCentral() } -dependencies { - implementation 'org.scala-lang:scala-library:2.13.12' +scala { + scalaVersion = "2.13.12" } dependencies { diff --git a/platforms/documentation/docs/src/snippets/scala/force/kotlin/build.gradle.kts b/platforms/documentation/docs/src/snippets/scala/force/kotlin/build.gradle.kts index b9bfbd5018287..724e760d5d572 100644 --- a/platforms/documentation/docs/src/snippets/scala/force/kotlin/build.gradle.kts +++ b/platforms/documentation/docs/src/snippets/scala/force/kotlin/build.gradle.kts @@ -1,13 +1,13 @@ plugins { - scala + id("scala") } repositories { mavenCentral() } -dependencies { - implementation("org.scala-lang:scala-library:2.13.12") +scala { + scalaVersion = "2.13.12" } dependencies { diff --git a/platforms/documentation/docs/src/snippets/scala/ideaTargetVersion/groovy/build.gradle b/platforms/documentation/docs/src/snippets/scala/ideaTargetVersion/groovy/build.gradle index dabcd4b62bde0..1215cb985ffc7 100644 --- a/platforms/documentation/docs/src/snippets/scala/ideaTargetVersion/groovy/build.gradle +++ b/platforms/documentation/docs/src/snippets/scala/ideaTargetVersion/groovy/build.gradle @@ -1,6 +1,6 @@ plugins { - id 'scala' - id 'idea' + id("scala") + id("idea") } // tag::scala-idea-target-version[] diff --git a/platforms/documentation/docs/src/snippets/scala/ideaTargetVersion/kotlin/build.gradle.kts b/platforms/documentation/docs/src/snippets/scala/ideaTargetVersion/kotlin/build.gradle.kts index 77ca243e27855..c8ce28db5e842 100644 --- a/platforms/documentation/docs/src/snippets/scala/ideaTargetVersion/kotlin/build.gradle.kts +++ b/platforms/documentation/docs/src/snippets/scala/ideaTargetVersion/kotlin/build.gradle.kts @@ -1,6 +1,6 @@ plugins { - scala - idea + id("scala") + id("idea") } // tag::scala-idea-target-version[] diff --git a/platforms/documentation/docs/src/snippets/scala/mixedJavaAndScala/groovy/build.gradle b/platforms/documentation/docs/src/snippets/scala/mixedJavaAndScala/groovy/build.gradle index eefc0e0196531..9714cb575d145 100644 --- a/platforms/documentation/docs/src/snippets/scala/mixedJavaAndScala/groovy/build.gradle +++ b/platforms/documentation/docs/src/snippets/scala/mixedJavaAndScala/groovy/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'scala' + id("scala") } version = 1.0 @@ -8,7 +8,10 @@ repositories { mavenCentral() } +scala { + scalaVersion = "2.13.12" +} + dependencies { - implementation 'org.scala-lang:scala-library:2.13.12' testImplementation 'junit:junit:4.13' } diff --git a/platforms/documentation/docs/src/snippets/scala/quickstart/groovy/build.gradle b/platforms/documentation/docs/src/snippets/scala/quickstart/groovy/build.gradle index ceacb56168949..ac30c162a19a4 100644 --- a/platforms/documentation/docs/src/snippets/scala/quickstart/groovy/build.gradle +++ b/platforms/documentation/docs/src/snippets/scala/quickstart/groovy/build.gradle @@ -1,23 +1,23 @@ // tag::use-plugin[] plugins { // end::use-plugin[] - id 'eclipse' + id("eclipse") // tag::use-plugin[] - id 'scala' + id("scala") } // end::use-plugin[] -// tag::scala-dependency[] +// tag::scala-version[] repositories { mavenCentral() } -dependencies { - implementation 'org.scala-lang:scala-library:2.13.12' - testImplementation 'junit:junit:4.13' +scala { + scalaVersion = "2.13.12" } -// end::scala-dependency[] +// end::scala-version[] dependencies { implementation 'commons-collections:commons-collections:3.2.2' + testImplementation 'junit:junit:4.13' } diff --git a/platforms/documentation/docs/src/snippets/scala/quickstart/groovy/inferred-version/build.gradle b/platforms/documentation/docs/src/snippets/scala/quickstart/groovy/inferred-version/build.gradle new file mode 100644 index 0000000000000..841dd19b5391f --- /dev/null +++ b/platforms/documentation/docs/src/snippets/scala/quickstart/groovy/inferred-version/build.gradle @@ -0,0 +1,13 @@ +plugins { + id("scala") +} + +// tag::scala-dependency[] +repositories { + mavenCentral() +} + +dependencies { + implementation("org.scala-lang:scala-library:2.13.12") +} +// end::scala-dependency[] diff --git a/platforms/documentation/docs/src/snippets/scala/quickstart/groovy/settings.gradle b/platforms/documentation/docs/src/snippets/scala/quickstart/groovy/settings.gradle index 996d5458b2da6..7fd07c1247e92 100644 --- a/platforms/documentation/docs/src/snippets/scala/quickstart/groovy/settings.gradle +++ b/platforms/documentation/docs/src/snippets/scala/quickstart/groovy/settings.gradle @@ -1 +1,3 @@ rootProject.name = 'quickstart' + +include("inferred-version") diff --git a/platforms/documentation/docs/src/snippets/scala/quickstart/kotlin/build.gradle.kts b/platforms/documentation/docs/src/snippets/scala/quickstart/kotlin/build.gradle.kts index 7efd479a01400..a6ee7415ddbdc 100644 --- a/platforms/documentation/docs/src/snippets/scala/quickstart/kotlin/build.gradle.kts +++ b/platforms/documentation/docs/src/snippets/scala/quickstart/kotlin/build.gradle.kts @@ -1,23 +1,23 @@ // tag::use-plugin[] plugins { // end::use-plugin[] - eclipse + id("eclipse") // tag::use-plugin[] - scala + id("scala") } // end::use-plugin[] -// tag::scala-dependency[] +// tag::scala-version[] repositories { mavenCentral() } -dependencies { - implementation("org.scala-lang:scala-library:2.13.12") - testImplementation("junit:junit:4.13") +scala { + scalaVersion = "2.13.12" } -// end::scala-dependency[] +// end::scala-version[] dependencies { implementation("commons-collections:commons-collections:3.2.2") + testImplementation("junit:junit:4.13") } diff --git a/platforms/documentation/docs/src/snippets/scala/quickstart/kotlin/inferred-version/build.gradle.kts b/platforms/documentation/docs/src/snippets/scala/quickstart/kotlin/inferred-version/build.gradle.kts new file mode 100644 index 0000000000000..841dd19b5391f --- /dev/null +++ b/platforms/documentation/docs/src/snippets/scala/quickstart/kotlin/inferred-version/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + id("scala") +} + +// tag::scala-dependency[] +repositories { + mavenCentral() +} + +dependencies { + implementation("org.scala-lang:scala-library:2.13.12") +} +// end::scala-dependency[] diff --git a/platforms/documentation/docs/src/snippets/scala/quickstart/kotlin/settings.gradle.kts b/platforms/documentation/docs/src/snippets/scala/quickstart/kotlin/settings.gradle.kts index fd64dfc055c25..6975b41b401f2 100644 --- a/platforms/documentation/docs/src/snippets/scala/quickstart/kotlin/settings.gradle.kts +++ b/platforms/documentation/docs/src/snippets/scala/quickstart/kotlin/settings.gradle.kts @@ -1 +1,3 @@ rootProject.name = "quickstart" + +include("inferred-version") diff --git a/platforms/documentation/docs/src/snippets/scala/scala3/groovy/build.gradle b/platforms/documentation/docs/src/snippets/scala/scala3/groovy/build.gradle index 3d3651c8bbd76..f42c0bddf7458 100644 --- a/platforms/documentation/docs/src/snippets/scala/scala3/groovy/build.gradle +++ b/platforms/documentation/docs/src/snippets/scala/scala3/groovy/build.gradle @@ -1,14 +1,18 @@ - plugins { - id 'scala' + id("scala") } +// tag::scala-version[] repositories { mavenCentral() } +scala { + scalaVersion = "3.6.3" +} +// end::scala-version[] + dependencies { - implementation 'org.scala-lang:scala3-library_3:3.0.1' implementation 'commons-collections:commons-collections:3.2.2' testImplementation 'org.scalatest:scalatest_3:3.2.9' testImplementation 'junit:junit:4.13' diff --git a/platforms/documentation/docs/src/snippets/scala/scala3/groovy/inferred-version/build.gradle b/platforms/documentation/docs/src/snippets/scala/scala3/groovy/inferred-version/build.gradle new file mode 100644 index 0000000000000..0d798d12bf379 --- /dev/null +++ b/platforms/documentation/docs/src/snippets/scala/scala3/groovy/inferred-version/build.gradle @@ -0,0 +1,13 @@ +plugins { + id("scala") +} + +// tag::scala-dependency[] +repositories { + mavenCentral() +} + +dependencies { + implementation("org.scala-lang:scala3-library_3:3.6.3") +} +// end::scala-dependency[] diff --git a/platforms/documentation/docs/src/snippets/scala/scala3/groovy/settings.gradle b/platforms/documentation/docs/src/snippets/scala/scala3/groovy/settings.gradle index e4c76d0fbccdc..d7c764f1406a2 100644 --- a/platforms/documentation/docs/src/snippets/scala/scala3/groovy/settings.gradle +++ b/platforms/documentation/docs/src/snippets/scala/scala3/groovy/settings.gradle @@ -1 +1,3 @@ rootProject.name = 'scala3' + +include("inferred-version") diff --git a/platforms/documentation/docs/src/snippets/scala/scala3/kotlin/build.gradle.kts b/platforms/documentation/docs/src/snippets/scala/scala3/kotlin/build.gradle.kts index 06b43a446b494..f8725ac16dbdf 100644 --- a/platforms/documentation/docs/src/snippets/scala/scala3/kotlin/build.gradle.kts +++ b/platforms/documentation/docs/src/snippets/scala/scala3/kotlin/build.gradle.kts @@ -1,18 +1,20 @@ - plugins { - scala + id("scala") } +// tag::scala-version[] repositories { mavenCentral() } +scala { + scalaVersion = "3.6.3" +} +// end::scala-version[] + dependencies { implementation("org.scala-lang:scala3-library_3:3.0.1") testImplementation("org.scalatest:scalatest_3:3.2.9") testImplementation("junit:junit:4.13") -} - -dependencies { implementation("commons-collections:commons-collections:3.2.2") } diff --git a/platforms/documentation/docs/src/snippets/scala/scala3/kotlin/inferred-version/build.gradle.kts b/platforms/documentation/docs/src/snippets/scala/scala3/kotlin/inferred-version/build.gradle.kts new file mode 100644 index 0000000000000..0d798d12bf379 --- /dev/null +++ b/platforms/documentation/docs/src/snippets/scala/scala3/kotlin/inferred-version/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + id("scala") +} + +// tag::scala-dependency[] +repositories { + mavenCentral() +} + +dependencies { + implementation("org.scala-lang:scala3-library_3:3.6.3") +} +// end::scala-dependency[] diff --git a/platforms/documentation/docs/src/snippets/scala/scala3/kotlin/settings.gradle.kts b/platforms/documentation/docs/src/snippets/scala/scala3/kotlin/settings.gradle.kts index e3efc7b03e1cd..41239e592716d 100644 --- a/platforms/documentation/docs/src/snippets/scala/scala3/kotlin/settings.gradle.kts +++ b/platforms/documentation/docs/src/snippets/scala/scala3/kotlin/settings.gradle.kts @@ -1 +1,3 @@ rootProject.name = "scala3" + +include("inferred-version") diff --git a/platforms/documentation/docs/src/snippets/scala/scalaDependency/groovy/build.gradle b/platforms/documentation/docs/src/snippets/scala/scalaDependency/groovy/build.gradle index 3a2bb465727b7..5cfb2ea8b5022 100644 --- a/platforms/documentation/docs/src/snippets/scala/scalaDependency/groovy/build.gradle +++ b/platforms/documentation/docs/src/snippets/scala/scalaDependency/groovy/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'scala' + id("scala") } repositories { @@ -8,6 +8,6 @@ repositories { // tag::scala-test-dependency[] dependencies { - testImplementation 'org.scala-lang:scala-library:2.13.12' + testImplementation("org.scala-lang:scala-library:2.13.12") } // end::scala-test-dependency[] diff --git a/platforms/documentation/docs/src/snippets/scala/scalaDependency/kotlin/build.gradle.kts b/platforms/documentation/docs/src/snippets/scala/scalaDependency/kotlin/build.gradle.kts index 6c10e96e4b686..5cfb2ea8b5022 100644 --- a/platforms/documentation/docs/src/snippets/scala/scalaDependency/kotlin/build.gradle.kts +++ b/platforms/documentation/docs/src/snippets/scala/scalaDependency/kotlin/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - scala + id("scala") } repositories { diff --git a/platforms/documentation/docs/src/snippets/scala/zinc/groovy/build.gradle b/platforms/documentation/docs/src/snippets/scala/zinc/groovy/build.gradle index f29df1267f645..fc9ff5e76bd88 100644 --- a/platforms/documentation/docs/src/snippets/scala/zinc/groovy/build.gradle +++ b/platforms/documentation/docs/src/snippets/scala/zinc/groovy/build.gradle @@ -1,13 +1,13 @@ plugins { - id 'scala' + id("scala") } repositories { mavenCentral() } -dependencies { - implementation 'org.scala-lang:scala-library:2.13.12' +scala { + scalaVersion = "2.13.12" } dependencies { diff --git a/platforms/documentation/docs/src/snippets/scala/zinc/kotlin/build.gradle.kts b/platforms/documentation/docs/src/snippets/scala/zinc/kotlin/build.gradle.kts index 0220152ab6fc0..3216331570e0c 100644 --- a/platforms/documentation/docs/src/snippets/scala/zinc/kotlin/build.gradle.kts +++ b/platforms/documentation/docs/src/snippets/scala/zinc/kotlin/build.gradle.kts @@ -1,13 +1,13 @@ plugins { - scala + id("scala") } repositories { mavenCentral() } -dependencies { - implementation("org.scala-lang:scala-library:2.13.12") +scala { + scalaVersion = "2.13.12" } dependencies { diff --git a/platforms/documentation/docs/src/snippets/scala/zincDependency/groovy/build.gradle b/platforms/documentation/docs/src/snippets/scala/zincDependency/groovy/build.gradle index ecd045ed549cd..e637b35c8cd6e 100644 --- a/platforms/documentation/docs/src/snippets/scala/zincDependency/groovy/build.gradle +++ b/platforms/documentation/docs/src/snippets/scala/zincDependency/groovy/build.gradle @@ -1,17 +1,14 @@ plugins { - id 'scala' + id("scala") } repositories { mavenCentral() } -dependencies { - implementation 'org.scala-lang:scala-library:2.13.12' -} - // tag::zinc-dependency[] scala { + scalaVersion = "2.13.12" zincVersion = "1.10.4" } // end::zinc-dependency[] diff --git a/platforms/documentation/docs/src/snippets/scala/zincDependency/kotlin/build.gradle.kts b/platforms/documentation/docs/src/snippets/scala/zincDependency/kotlin/build.gradle.kts index 56107130fd24d..1010e9b44429c 100644 --- a/platforms/documentation/docs/src/snippets/scala/zincDependency/kotlin/build.gradle.kts +++ b/platforms/documentation/docs/src/snippets/scala/zincDependency/kotlin/build.gradle.kts @@ -1,17 +1,14 @@ plugins { - scala + id("scala") } repositories { mavenCentral() } -dependencies { - implementation("org.scala-lang:scala-library:2.13.12") -} - // tag::zinc-dependency[] scala { + scalaVersion = "2.13.12" zincVersion = "1.10.4" } // end::zinc-dependency[] diff --git a/platforms/ide/ide-plugins/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/addsScalaSdkAndCompilerLibraries/project1/build.gradle b/platforms/ide/ide-plugins/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/addsScalaSdkAndCompilerLibraries/project1/build.gradle index 53da68e157774..66404025a2899 100644 --- a/platforms/ide/ide-plugins/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/addsScalaSdkAndCompilerLibraries/project1/build.gradle +++ b/platforms/ide/ide-plugins/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/addsScalaSdkAndCompilerLibraries/project1/build.gradle @@ -1,4 +1,6 @@ -apply plugin: "scala" +plugins { + id("scala") +} repositories { mavenCentral() diff --git a/platforms/ide/ide-plugins/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/addsScalaSdkAndCompilerLibraries/project2/build.gradle b/platforms/ide/ide-plugins/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/addsScalaSdkAndCompilerLibraries/project2/build.gradle index 8137350615871..82c3788f1bf05 100644 --- a/platforms/ide/ide-plugins/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/addsScalaSdkAndCompilerLibraries/project2/build.gradle +++ b/platforms/ide/ide-plugins/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/addsScalaSdkAndCompilerLibraries/project2/build.gradle @@ -1,4 +1,6 @@ -apply plugin: "scala" +plugins { + id("scala") +} repositories { mavenCentral() diff --git a/platforms/ide/ide-plugins/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/addsScalaSdkAndCompilerLibraries/project3/build.gradle b/platforms/ide/ide-plugins/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/addsScalaSdkAndCompilerLibraries/project3/build.gradle index 53da68e157774..c191c72b12d32 100644 --- a/platforms/ide/ide-plugins/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/addsScalaSdkAndCompilerLibraries/project3/build.gradle +++ b/platforms/ide/ide-plugins/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/addsScalaSdkAndCompilerLibraries/project3/build.gradle @@ -1,9 +1,11 @@ -apply plugin: "scala" +plugins { + id("scala") +} repositories { mavenCentral() } -dependencies { - implementation "org.scala-lang:scala-library:2.11.2" +scala { + scalaVersion = "2.11.2" } diff --git a/platforms/ide/ide-plugins/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/addsScalaSdkAndCompilerLibraries/project4/build.gradle b/platforms/ide/ide-plugins/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/addsScalaSdkAndCompilerLibraries/project4/build.gradle index 7f77ea637a650..853780955a728 100644 --- a/platforms/ide/ide-plugins/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/addsScalaSdkAndCompilerLibraries/project4/build.gradle +++ b/platforms/ide/ide-plugins/src/integTest/resources/org/gradle/plugins/ide/idea/IdeaIntegrationTest/addsScalaSdkAndCompilerLibraries/project4/build.gradle @@ -1,9 +1,11 @@ -apply plugin: "scala" +plugins { + id("scala") +} repositories { mavenCentral() } -dependencies { - implementation "org.scala-lang:scala3-library_3:3.0.1" +scala { + scalaVersion = "3.0.1" } diff --git a/platforms/ide/ide-plugins/src/main/java/org/gradle/plugins/ide/idea/internal/IdeaScalaConfigurer.java b/platforms/ide/ide-plugins/src/main/java/org/gradle/plugins/ide/idea/internal/IdeaScalaConfigurer.java index 8d81b8a4b6946..dc3a910303a0a 100644 --- a/platforms/ide/ide-plugins/src/main/java/org/gradle/plugins/ide/idea/internal/IdeaScalaConfigurer.java +++ b/platforms/ide/ide-plugins/src/main/java/org/gradle/plugins/ide/idea/internal/IdeaScalaConfigurer.java @@ -27,10 +27,13 @@ import org.gradle.api.Project; import org.gradle.api.Task; import org.gradle.api.XmlProvider; +import org.gradle.api.artifacts.Configuration; import org.gradle.api.file.FileCollection; import org.gradle.api.internal.project.ProjectInternal; +import org.gradle.api.internal.project.ProjectState; import org.gradle.api.invocation.Gradle; import org.gradle.api.plugins.scala.ScalaBasePlugin; +import org.gradle.api.plugins.scala.ScalaPluginExtension; import org.gradle.api.tasks.ScalaRuntime; import org.gradle.internal.Cast; import org.gradle.plugins.ide.idea.IdeaPlugin; @@ -115,13 +118,10 @@ public void execute(XmlProvider xmlProvider) { private static Map resolveScalaCompilerLibraries(Collection scalaProjects, final boolean useScalaSdk) { Map scalaCompilerLibraries = new LinkedHashMap<>(); for (final Project scalaProject : scalaProjects) { - final IdeaModule ideaModule = scalaProject.getExtensions().getByType(IdeaModel.class).getModule(); - final Iterable files = getIdeaModuleLibraryDependenciesAsFiles(ideaModule); - ProjectLibrary library = ((ProjectInternal) scalaProject).getOwner().fromMutableState(p -> createScalaSdkLibrary(scalaProject, files, useScalaSdk, ideaModule)); - if (library != null) { - ProjectLibrary duplicate = Iterables.find(scalaCompilerLibraries.values(), Predicates.equalTo(library), null); - scalaCompilerLibraries.put(scalaProject.getPath(), duplicate == null ? library : duplicate); - } + ProjectState owner = ((ProjectInternal) scalaProject).getOwner(); + ProjectLibrary library = owner.fromMutableState(p -> createScalaSdkLibrary(p, useScalaSdk)); + ProjectLibrary duplicate = Iterables.find(scalaCompilerLibraries.values(), Predicates.equalTo(library), null); + scalaCompilerLibraries.put(owner.getIdentity().getProjectPath().toString(), duplicate == null ? library : duplicate); } return scalaCompilerLibraries; } @@ -138,22 +138,28 @@ private static Iterable getIdeaModuleLibraryDependenciesAsFiles(IdeaModule return files; } - @SuppressWarnings({"deprecation", "unused"}) - private static ProjectLibrary createScalaSdkLibrary(Project scalaProject, Iterable files, boolean useScalaSdk, IdeaModule ideaModule) { - ScalaRuntime runtime = scalaProject.getExtensions().findByType(ScalaRuntime.class); - if (runtime != null) { - FileCollection scalaClasspath = runtime.inferScalaClasspath(files); - File compilerJar = runtime.findScalaJar(scalaClasspath, "compiler"); - if (compilerJar == null) { - compilerJar = runtime.findScalaJar(scalaClasspath, "compiler_3"); - } - String scalaVersion = compilerJar != null ? runtime.getScalaVersion(compilerJar) : DEFAULT_SCALA_PLATFORM_VERSION; - return createScalaSdkFromScalaVersion(scalaVersion, scalaClasspath, useScalaSdk); - } else { - // One of the Scala plugins is applied, but ScalaRuntime extension is missing or the ScalaPlatform is undefined. - // we can't create a Scala SDK without either one - return null; + private static ProjectLibrary createScalaSdkLibrary(Project scalaProject, boolean useScalaSdk) { + ScalaPluginExtension scalaPluginExtension = scalaProject.getExtensions().getByType(ScalaPluginExtension.class); + if (scalaPluginExtension.getScalaVersion().isPresent()) { + String scalaVersion = scalaPluginExtension.getScalaVersion().get(); + Configuration toolchainClasspath = scalaProject.getConfigurations().getByName("scalaToolchainRuntimeClasspath"); + return createScalaSdkFromScalaVersion(scalaVersion, toolchainClasspath.getIncoming().getFiles(), useScalaSdk); } + + // Otherwise, use legacy logic to scan classpath jars for version. + IdeaModule ideaModule = scalaProject.getExtensions().getByType(IdeaModel.class).getModule(); + Iterable files = getIdeaModuleLibraryDependenciesAsFiles(ideaModule); + + ScalaRuntime runtime = scalaProject.getExtensions().getByType(ScalaRuntime.class); + FileCollection scalaClasspath = runtime.inferScalaClasspath(files); + + File compilerJar = runtime.findScalaJar(scalaClasspath, "compiler"); + if (compilerJar == null) { + compilerJar = runtime.findScalaJar(scalaClasspath, "compiler_3"); + } + + String scalaVersion = compilerJar != null ? runtime.getScalaVersion(compilerJar) : DEFAULT_SCALA_PLATFORM_VERSION; + return createScalaSdkFromScalaVersion(scalaVersion, scalaClasspath, useScalaSdk); } private static ProjectLibrary createScalaSdkFromScalaVersion(String version, FileCollection scalaClasspath, boolean useScalaSdk) { diff --git a/platforms/jvm/scala/src/integTest/groovy/org/gradle/scala/ScalaPluginExplicitVersionIntegrationTest.groovy b/platforms/jvm/scala/src/integTest/groovy/org/gradle/scala/ScalaPluginExplicitVersionIntegrationTest.groovy new file mode 100644 index 0000000000000..a84a39d529c08 --- /dev/null +++ b/platforms/jvm/scala/src/integTest/groovy/org/gradle/scala/ScalaPluginExplicitVersionIntegrationTest.groovy @@ -0,0 +1,122 @@ +/* + * Copyright 2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.gradle.scala + +import org.gradle.integtests.fixtures.MultiVersionIntegrationSpec +import org.gradle.integtests.fixtures.ScalaCoverage +import org.gradle.integtests.fixtures.TargetCoverage +import org.gradle.util.internal.VersionNumber + +/** + * Tests the scala plugin when explicitly declaring the scala version using + * {@link org.gradle.api.plugins.scala.ScalaPluginExtension#getScalaVersion()} + */ +@TargetCoverage({ ScalaCoverage.SUPPORTED_BY_JDK }) +class ScalaPluginExplicitVersionIntegrationTest extends MultiVersionIntegrationSpec { + + def "can compile scala code"() { + buildFile << """ + plugins { + id("scala") + } + + ${mavenCentralRepository()} + + scala { + scalaVersion = "${version}" + } + """ + + file("src/main/scala/Main.scala") << """ + object Main { + def main(args: Array[String]): Unit = { + println("Hello, world!") + } + } + """ + + when: + succeeds("compileScala") + + then: + file("build/classes/scala/main/Main.class").exists() + } + + def "can compile 2.13 code"() { + buildFile << """ + plugins { + id("scala") + } + + ${mavenCentralRepository()} + + scala { + scalaVersion = "${version}" + } + """ + + file("src/main/scala/Main.scala") << """ + object Main { + def main(args: Array[String]): Unit = { + val grouped = List(1, 2, 3).groupMap(_ % 2)(_ * 2) // Introduced in 2.13.x + println(grouped) + } + } + """ + + expect: + if (VersionNumber.parse(version) >= VersionNumber.parse("2.13")) { + succeeds("compileScala") + file("build/classes/scala/main/Main.class").exists() + } else { + fails("compileScala") + file("build/classes/scala/main/Main.class").assertDoesNotExist() + } + } + + def "can compile 3.1 code"() { + buildFile << """ + plugins { + id("scala") + } + + ${mavenCentralRepository()} + + scala { + scalaVersion = "${version}" + } + """ + + file("src/main/scala/Main.scala") << """ + object Main { + inline def add(x: Int, y: Int): Int = x + y // Uses `inline` keyword + + def main(args: Array[String]): Unit = { + println(add(1, 2)) + } + } + """ + + expect: + if (VersionNumber.parse(version) >= VersionNumber.parse("3.1")) { + succeeds("compileScala") + file("build/classes/scala/main/Main.class").exists() + } else { + fails("compileScala") + file("build/classes/scala/main/Main.class").assertDoesNotExist() + } + } +} diff --git a/platforms/jvm/scala/src/main/java/org/gradle/api/internal/tasks/scala/DefaultScalaPluginExtension.java b/platforms/jvm/scala/src/main/java/org/gradle/api/internal/tasks/scala/DefaultScalaPluginExtension.java deleted file mode 100644 index f0dbed1db044b..0000000000000 --- a/platforms/jvm/scala/src/main/java/org/gradle/api/internal/tasks/scala/DefaultScalaPluginExtension.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2019 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.gradle.api.internal.tasks.scala; - -import org.gradle.api.model.ObjectFactory; -import org.gradle.api.plugins.scala.ScalaBasePlugin; -import org.gradle.api.plugins.scala.ScalaPluginExtension; -import org.gradle.api.provider.Property; - -import javax.inject.Inject; - -public class DefaultScalaPluginExtension implements ScalaPluginExtension { - private final Property zincVersion; - - @Inject - public DefaultScalaPluginExtension(ObjectFactory objectFactory) { - this.zincVersion = objectFactory.property(String.class).value(ScalaBasePlugin.DEFAULT_ZINC_VERSION); - } - - @Override - public Property getZincVersion() { - return zincVersion; - } -} diff --git a/platforms/jvm/scala/src/main/java/org/gradle/api/plugins/scala/ScalaBasePlugin.java b/platforms/jvm/scala/src/main/java/org/gradle/api/plugins/scala/ScalaBasePlugin.java index 518412ab2b8f4..b98b076ba612a 100644 --- a/platforms/jvm/scala/src/main/java/org/gradle/api/plugins/scala/ScalaBasePlugin.java +++ b/platforms/jvm/scala/src/main/java/org/gradle/api/plugins/scala/ScalaBasePlugin.java @@ -16,13 +16,20 @@ package org.gradle.api.plugins.scala; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Joiner; +import com.google.common.base.Splitter; import com.google.common.collect.ImmutableSet; import org.gradle.api.InvalidUserCodeException; import org.gradle.api.JavaVersion; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.Dependency; +import org.gradle.api.artifacts.DependencyScopeConfiguration; +import org.gradle.api.artifacts.ModuleDependency; +import org.gradle.api.artifacts.ResolvableConfiguration; import org.gradle.api.artifacts.component.ProjectComponentIdentifier; +import org.gradle.api.artifacts.dsl.DependencyFactory; import org.gradle.api.artifacts.dsl.DependencyHandler; import org.gradle.api.attributes.AttributeDisambiguationRule; import org.gradle.api.attributes.AttributeMatchingStrategy; @@ -36,7 +43,6 @@ import org.gradle.api.internal.plugins.DslObject; import org.gradle.api.internal.project.ProjectInternal; import org.gradle.api.internal.tasks.DefaultSourceSet; -import org.gradle.api.internal.tasks.scala.DefaultScalaPluginExtension; import org.gradle.api.model.ObjectFactory; import org.gradle.api.plugins.ExtensionAware; import org.gradle.api.plugins.JavaBasePlugin; @@ -53,6 +59,7 @@ import org.gradle.api.tasks.scala.IncrementalCompileOptions; import org.gradle.api.tasks.scala.ScalaCompile; import org.gradle.api.tasks.scala.ScalaDoc; +import org.gradle.api.tasks.scala.internal.ScalaRuntimeHelper; import org.gradle.internal.deprecation.DeprecationLogger; import org.gradle.internal.logging.util.Log4jBannedVersion; import org.gradle.jvm.tasks.Jar; @@ -87,6 +94,7 @@ public abstract class ScalaBasePlugin implements Plugin { @VisibleForTesting public static final String ZINC_CONFIGURATION_NAME = "zinc"; public static final String SCALA_RUNTIME_EXTENSION_NAME = "scalaRuntime"; + /** * Configuration for scala compiler plugins. * @@ -96,11 +104,17 @@ public abstract class ScalaBasePlugin implements Plugin { private final ObjectFactory objectFactory; private final JvmPluginServices jvmPluginServices; + private final DependencyFactory dependencyFactory; @Inject - public ScalaBasePlugin(ObjectFactory objectFactory, JvmPluginServices jvmPluginServices) { + public ScalaBasePlugin( + ObjectFactory objectFactory, + JvmPluginServices jvmPluginServices, + DependencyFactory dependencyFactory + ) { this.objectFactory = objectFactory; this.jvmPluginServices = jvmPluginServices; + this.dependencyFactory = dependencyFactory; } @Override @@ -108,15 +122,19 @@ public void apply(final Project project) { project.getPluginManager().apply(JavaBasePlugin.class); ScalaRuntime scalaRuntime = project.getExtensions().create(SCALA_RUNTIME_EXTENSION_NAME, ScalaRuntime.class, project); - ScalaPluginExtension scalaPluginExtension = project.getExtensions().create(ScalaPluginExtension.class, "scala", DefaultScalaPluginExtension.class); + + ScalaPluginExtension scalaPluginExtension = project.getExtensions().create("scala", ScalaPluginExtension.class); + scalaPluginExtension.getZincVersion().convention(ScalaBasePlugin.DEFAULT_ZINC_VERSION); + + Provider toolchainClasspath = createToolchainRuntimeClasspath(project, scalaPluginExtension); Usage incrementalAnalysisUsage = objectFactory.named(Usage.class, "incremental-analysis"); Category incrementalAnalysisCategory = objectFactory.named(Category.class, "scala-analysis"); configureConfigurations((ProjectInternal) project, incrementalAnalysisCategory, incrementalAnalysisUsage, scalaPluginExtension); - configureCompileDefaults(project, scalaRuntime, (DefaultJavaPluginExtension) javaPluginExtension(project)); - configureSourceSetDefaults((ProjectInternal) project, incrementalAnalysisCategory, incrementalAnalysisUsage); - configureScaladoc(project, scalaRuntime); + configureCompileDefaults(project, scalaRuntime, (DefaultJavaPluginExtension) javaPluginExtension(project), scalaPluginExtension, toolchainClasspath); + configureSourceSetDefaults((ProjectInternal) project, incrementalAnalysisCategory, incrementalAnalysisUsage, scalaPluginExtension); + configureScaladoc(project, scalaRuntime, scalaPluginExtension, toolchainClasspath); } @SuppressWarnings("deprecation") @@ -172,10 +190,26 @@ private void configureConfigurations(final ProjectInternal project, Category inc }); } - private void configureSourceSetDefaults(final ProjectInternal project, Category incrementalAnalysisCategory, final Usage incrementalAnalysisUsage) { + private Provider createToolchainRuntimeClasspath(Project project, ScalaPluginExtension scalaPluginExtension) { + Provider scalaToolchain = project.getConfigurations().dependencyScope("scalaToolchain", conf -> { + conf.setDescription("Dependencies for the Scala toolchain"); + conf.getDependencies().addLater(createScalaCompilerDependency(scalaPluginExtension)); + conf.getDependencies().addLater(createScalaBridgeDependency(scalaPluginExtension)); + conf.getDependencies().addLater(createScalaCompilerInterfaceDependency(scalaPluginExtension)); + conf.getDependencies().addLater(createScaladocDependency(scalaPluginExtension)); + }); + + return project.getConfigurations().resolvable("scalaToolchainRuntimeClasspath", conf -> { + conf.setDescription("Runtime classpath for the Scala toolchain"); + conf.extendsFrom(scalaToolchain.get()); + jvmPluginServices.configureAsRuntimeClasspath(conf); + }); + } + + private void configureSourceSetDefaults(final ProjectInternal project, Category incrementalAnalysisCategory, final Usage incrementalAnalysisUsage, ScalaPluginExtension scalaPluginExtension) { javaPluginExtension(project).getSourceSets().all(sourceSet -> { - ScalaSourceDirectorySet scalaSource = getScalaSourceDirectorySet(sourceSet); + ScalaSourceDirectorySet scalaSource = createScalaSourceDirectorySet(sourceSet); sourceSet.getExtensions().add(ScalaSourceDirectorySet.class, "scala", scalaSource); scalaSource.srcDir(project.file("src/" + sourceSet.getName() + "/scala")); @@ -187,18 +221,102 @@ private void configureSourceSetDefaults(final ProjectInternal project, Category sourceSet.getAllJava().source(scalaSource); sourceSet.getAllSource().source(scalaSource); + project.getConfigurations().getByName(sourceSet.getImplementationConfigurationName()).getDependencies().addLater(createScalaDependency(scalaPluginExtension)); + Configuration incrementalAnalysis = createIncrementalAnalysisConfigurationFor(project.getConfigurations(), incrementalAnalysisCategory, incrementalAnalysisUsage, sourceSet); createScalaCompileTask(project, sourceSet, scalaSource, incrementalAnalysis); }); } + /** + * Determines the scala standard library that user code compiles against. + */ + private Provider createScalaDependency(ScalaPluginExtension scalaPluginExtension) { + return scalaPluginExtension.getScalaVersion().map(scalaVersion -> { + if (ScalaRuntimeHelper.isScala3(scalaVersion)) { + return dependencyFactory.create("org.scala-lang", "scala3-library_3", scalaVersion); + } else { + return dependencyFactory.create("org.scala-lang", "scala-library", scalaVersion); + } + }); + } + + /** + * Determines the Scala compiler dependency. + */ + private Provider createScalaCompilerDependency(ScalaPluginExtension scalaPluginExtension) { + return scalaPluginExtension.getScalaVersion().map(scalaVersion -> { + if (ScalaRuntimeHelper.isScala3(scalaVersion)) { + return dependencyFactory.create("org.scala-lang", "scala3-compiler_3", scalaVersion); + } else { + return dependencyFactory.create("org.scala-lang", "scala-compiler", scalaVersion); + } + }); + } + + /** + * Determines Scala bridge dependency. In Scala 3 it is released for each Scala + * version together with the compiler jars. For Scala 2 we download sources jar and compile + * it later on. + * + * @see org.gradle.api.internal.tasks.scala.ZincScalaCompilerFactory + */ + private Provider createScalaBridgeDependency(ScalaPluginExtension scalaPluginExtension) { + return scalaPluginExtension.getScalaVersion().zip(scalaPluginExtension.getZincVersion(), (scalaVersion, zincVersion) -> { + if (ScalaRuntimeHelper.isScala3(scalaVersion)) { + return dependencyFactory.create("org.scala-lang", "scala3-sbt-bridge", scalaVersion); + } else { + String scalaMajorMinorVersion = Joiner.on('.').join(Splitter.on('.').splitToList(scalaVersion).subList(0, 2)); + String name = "compiler-bridge_" + scalaMajorMinorVersion; + ModuleDependency dependency = dependencyFactory.create("org.scala-sbt", name, zincVersion); + + // Use an artifact to remain compatible with Ivy repositories, which + // don't support variant derivation. + dependency.artifact(artifact -> { + artifact.setClassifier("sources"); + artifact.setType("jar"); + artifact.setExtension("jar"); + artifact.setName(name); + }); + + return dependency; + } + }); + } + + /** + * Determines Scala compiler interfaces dependency. + */ + private Provider createScalaCompilerInterfaceDependency(ScalaPluginExtension scalaPluginExtension) { + return scalaPluginExtension.getScalaVersion().zip(scalaPluginExtension.getZincVersion(), (scalaVersion, zincVersion) -> { + if (ScalaRuntimeHelper.isScala3(scalaVersion)) { + return dependencyFactory.create("org.scala-lang", "scala3-interfaces", scalaVersion); + } else { + return dependencyFactory.create("org.scala-sbt", "compiler-interface", zincVersion); + } + }); + } + + /** + * Determines Scaladoc dependency. Note that scaladoc for Scala 2 is packaged along with the compiler. + */ + private Provider createScaladocDependency(ScalaPluginExtension scalaPluginExtension) { + return scalaPluginExtension.getScalaVersion().map(scalaVersion -> { + if (ScalaRuntimeHelper.isScala3(scalaVersion)) { + return dependencyFactory.create("org.scala-lang", "scaladoc_3", scalaVersion); + } else { + return null; + } + }); + } + /** * In 9.0, once {@link org.gradle.api.internal.tasks.DefaultScalaSourceSet} is removed, we can update this to only construct the source directory * set instead of the entire source set. */ @SuppressWarnings("deprecation") - private ScalaSourceDirectorySet getScalaSourceDirectorySet(SourceSet sourceSet) { + private ScalaSourceDirectorySet createScalaSourceDirectorySet(SourceSet sourceSet) { org.gradle.api.internal.tasks.DefaultScalaSourceSet scalaSourceSet = objectFactory.newInstance(org.gradle.api.internal.tasks.DefaultScalaSourceSet.class, ((DefaultSourceSet) sourceSet).getDisplayName(), objectFactory); DeprecationLogger.whileDisabled(() -> new DslObject(sourceSet).getConvention().getPlugins().put("scala", scalaSourceSet) @@ -260,10 +378,21 @@ private void configureIncrementalAnalysis(Project project, SourceSet sourceSet, scalaCompile.dependsOn(scalaCompile.getAnalysisFiles()); } - private static void configureCompileDefaults(final Project project, final ScalaRuntime scalaRuntime, final DefaultJavaPluginExtension javaExtension) { + private static void configureCompileDefaults( + Project project, + ScalaRuntime scalaRuntime, + DefaultJavaPluginExtension javaExtension, + ScalaPluginExtension scalaPluginExtension, + Provider scalaToolchainRuntimeClasspath + ) { project.getTasks().withType(ScalaCompile.class).configureEach(compile -> { ConventionMapping conventionMapping = compile.getConventionMapping(); - conventionMapping.map("scalaClasspath", (Callable) () -> scalaRuntime.inferScalaClasspath(compile.getClasspath())); + conventionMapping.map("scalaClasspath", (Callable) () -> getScalaToolchainClasspath( + scalaPluginExtension, + scalaToolchainRuntimeClasspath, + scalaRuntime, + compile.getClasspath() + )); conventionMapping.map("zincClasspath", (Callable) () -> project.getConfigurations().getAt(ZINC_CONFIGURATION_NAME)); conventionMapping.map("scalaCompilerPlugins", (Callable) () -> project.getConfigurations().getAt(SCALA_COMPILER_PLUGINS_CONFIGURATION_NAME)); conventionMapping.map("sourceCompatibility", () -> computeJavaSourceCompatibilityConvention(javaExtension, compile).toString()); @@ -288,15 +417,39 @@ private static JavaVersion computeJavaTargetCompatibilityConvention(DefaultJavaP return JavaVersion.toVersion(compileTask.getSourceCompatibility()); } - private void configureScaladoc(final Project project, final ScalaRuntime scalaRuntime) { + private static void configureScaladoc( + Project project, + ScalaRuntime scalaRuntime, + ScalaPluginExtension scalaPluginExtension, + Provider scalaToolchainRuntimeClasspath + ) { project.getTasks().withType(ScalaDoc.class).configureEach(scalaDoc -> { - scalaDoc.getConventionMapping().map("scalaClasspath", (Callable) () -> scalaRuntime.inferScalaClasspath(scalaDoc.getClasspath())); + scalaDoc.getConventionMapping().map("scalaClasspath", (Callable) () -> getScalaToolchainClasspath( + scalaPluginExtension, + scalaToolchainRuntimeClasspath, + scalaRuntime, + scalaDoc.getClasspath() + )); scalaDoc.getConventionMapping().map("destinationDir", (Callable) () -> javaPluginExtension(project).getDocsDir().dir("scaladoc").get().getAsFile()); scalaDoc.getConventionMapping().map("title", (Callable) () -> project.getExtensions().getByType(ReportingExtension.class).getApiDocTitle()); scalaDoc.getJavaLauncher().convention(getJavaLauncher(project)); }); } + private static FileCollection getScalaToolchainClasspath( + ScalaPluginExtension scalaPluginExtension, + Provider scalaToolchainRuntimeClasspath, + ScalaRuntime scalaRuntime, + FileCollection taskClasspath + ) { + if (scalaPluginExtension.getScalaVersion().isPresent()) { + return scalaToolchainRuntimeClasspath.get(); + } else { + // TODO: Deprecate this path in 9.x when we de-incubate ScalaPluginExtension#getScalaVersion() + return scalaRuntime.inferScalaClasspath(taskClasspath); + } + } + private static Provider getJavaLauncher(Project project) { final JavaPluginExtension extension = javaPluginExtension(project); final JavaToolchainService service = extensionOf(project, JavaToolchainService.class); diff --git a/platforms/jvm/scala/src/main/java/org/gradle/api/plugins/scala/ScalaPluginExtension.java b/platforms/jvm/scala/src/main/java/org/gradle/api/plugins/scala/ScalaPluginExtension.java index 1a8c5391a24bd..e27ec8425534a 100644 --- a/platforms/jvm/scala/src/main/java/org/gradle/api/plugins/scala/ScalaPluginExtension.java +++ b/platforms/jvm/scala/src/main/java/org/gradle/api/plugins/scala/ScalaPluginExtension.java @@ -16,6 +16,7 @@ package org.gradle.api.plugins.scala; +import org.gradle.api.Incubating; import org.gradle.api.provider.Property; /** @@ -24,6 +25,7 @@ * @since 6.0 */ public interface ScalaPluginExtension { + /** * The version of the Zinc compiler to use for compiling Scala code. *

@@ -36,4 +38,15 @@ public interface ScalaPluginExtension { * @since 6.0 */ Property getZincVersion(); + + /** + * The version of the Scala to use for compiling Scala code. + * + * @return The scala version + * + * @since 8.13 + */ + @Incubating + Property getScalaVersion(); + } diff --git a/platforms/jvm/scala/src/main/java/org/gradle/api/tasks/ScalaRuntime.java b/platforms/jvm/scala/src/main/java/org/gradle/api/tasks/ScalaRuntime.java index f40cb9396fb48..573421054205a 100644 --- a/platforms/jvm/scala/src/main/java/org/gradle/api/tasks/ScalaRuntime.java +++ b/platforms/jvm/scala/src/main/java/org/gradle/api/tasks/ScalaRuntime.java @@ -60,6 +60,8 @@ */ public abstract class ScalaRuntime { + // TODO: Deprecate this class in 9.x when we de-incubate ScalaPluginExtension#getScalaVersion() + private final Project project; private final JvmPluginServices jvmPluginServices; diff --git a/testing/architecture-test/src/changes/archunit-store/internal-api-nullability.txt b/testing/architecture-test/src/changes/archunit-store/internal-api-nullability.txt index 8d97bda8275cd..7e44bcc5f86f2 100644 --- a/testing/architecture-test/src/changes/archunit-store/internal-api-nullability.txt +++ b/testing/architecture-test/src/changes/archunit-store/internal-api-nullability.txt @@ -687,7 +687,6 @@ Class is not annotat Class is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonScalaCompiler.java:0) Class is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DaemonScalaCompiler.java:0) Class is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultScalaJavaJointCompileSpec.java:0) -Class is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (DefaultScalaPluginExtension.java:0) Class is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GuavaBackedClassLoaderCache.java:0) Class is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (GuavaBackedClassLoaderCache.java:0) Class is not annotated (directly or via its package) with @org.gradle.api.NonNullApi in (HashedClasspath.java:0)