This is an automated email from the ASF dual-hosted git repository.

dweiss pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git


The following commit(s) were added to refs/heads/main by this push:
     new 6131709  SOLR-15602: upgrade to gradle 7.2, clean up gradle code a 
bit.  (#275)
6131709 is described below

commit 61317098057c2134d8d2f0672e410083d09b9d8b
Author: Dawid Weiss <[email protected]>
AuthorDate: Wed Sep 1 10:41:43 2021 +0200

    SOLR-15602: upgrade to gradle 7.2, clean up gradle code a bit.  (#275)
---
 build.gradle                                       |  16 ++-
 buildSrc/build.gradle                              |  10 +-
 gradle/defaults.gradle                             |  82 -----------
 gradle/documentation/documentation.gradle          |   5 +-
 gradle/documentation/render-javadoc.gradle         |  56 +-------
 gradle/generation/javacc.gradle                    |  38 ++---
 .../local-settings.gradle}                         |  62 ++++++---
 .../generation/regenerate.gradle                   |  23 +++-
 gradle/globals.gradle                              | 153 +++++++++++++++++++++
 .../hacks/dummy-outputs.gradle                     |  27 +++-
 gradle/{ant-compat => java}/folder-layout.gradle   |   4 -
 gradle/{ => java}/jar-manifest.gradle              |   5 +-
 gradle/{defaults-java.gradle => java/javac.gradle} |  13 ++
 gradle/testing/alternative-jdk-support.gradle      |  62 +++++----
 gradle/validation/check-environment.gradle         |   3 +-
 gradle/validation/ecj-lint.gradle                  |   3 -
 gradle/validation/gradlew-scripts-tweaked.gradle   |   3 -
 gradle/validation/spotless.gradle                  |   2 +-
 gradle/validation/validate-log-calls.gradle        |   8 +-
 gradle/validation/validate-source-patterns.gradle  |   6 -
 gradle/wrapper/gradle-wrapper.jar                  | Bin 59203 -> 59536 bytes
 gradle/wrapper/gradle-wrapper.jar.sha256           |   2 +-
 gradle/wrapper/gradle-wrapper.jar.version          |   2 +-
 gradle/wrapper/gradle-wrapper.properties           |   2 +-
 gradlew.bat                                        |  24 +---
 solr/contrib/prometheus-exporter/build.gradle      |   2 +
 solr/docker/build.gradle                           |  23 +++-
 solr/solr-ref-guide/build.gradle                   |  10 +-
 28 files changed, 370 insertions(+), 276 deletions(-)

diff --git a/build.gradle b/build.gradle
index 057d6a2..61224b5 100644
--- a/build.gradle
+++ b/build.gradle
@@ -20,15 +20,15 @@ import java.time.format.DateTimeFormatter
 
 plugins {
   id "base"
-  id "com.palantir.consistent-versions" version "1.14.0"
+  id "com.palantir.consistent-versions" version "2.0.0"
   id "org.owasp.dependencycheck" version "5.3.0"
   id 'de.thetaphi.forbiddenapis' version '3.1' apply false
   id "de.undercouch.download" version "4.0.2" apply false
   id "net.ltgt.errorprone" version "2.0.2" apply false
-  id 'com.diffplug.spotless' version "5.8.2" apply false
+  id 'com.diffplug.spotless' version "5.14.3" apply false
 }
 
-apply from: file('gradle/defaults.gradle')
+apply from: file('gradle/globals.gradle')
 
 // General metadata.
 
@@ -107,21 +107,21 @@ ext {
 // if the build file is incorrectly written and evaluates something
 // eagerly).
 
-apply from: file('gradle/generate-defaults.gradle')
+apply from: file('gradle/generation/local-settings.gradle')
 
 // Ant-compatibility layer: apply folder layout early so that
 // evaluation of other scripts doesn't need to be deferred.
-apply from: file('gradle/ant-compat/folder-layout.gradle')
 apply from: file('gradle/ant-compat/solr.folder-layout.gradle')
 
 // Set up defaults and configure aspects for certain modules or functionality
 // (java, tests)
-apply from: file('gradle/defaults-java.gradle')
+apply from: file('gradle/java/folder-layout.gradle')
+apply from: file('gradle/java/javac.gradle')
 apply from: file('gradle/testing/defaults-tests.gradle')
 apply from: file('gradle/testing/randomization.gradle')
 apply from: file('gradle/testing/fail-on-no-tests.gradle')
 apply from: file('gradle/testing/alternative-jdk-support.gradle')
-apply from: file('gradle/jar-manifest.gradle')
+apply from: file('gradle/java/jar-manifest.gradle')
 
 // Publishing and releasing
 apply from: file('gradle/maven/defaults-maven.gradle')
@@ -152,6 +152,7 @@ apply from: 
file('gradle/validation/solr.config-file-sanity.gradle')
 apply from: file('gradle/validation/spotless.gradle')
 
 // Source or data regeneration tasks
+apply from: file('gradle/generation/regenerate.gradle')
 apply from: file('gradle/generation/javacc.gradle')
 
 // Additional development aids.
@@ -183,6 +184,7 @@ apply from: file('gradle/hacks/solr.findbugs.gradle')
 apply from: file('gradle/hacks/wipe-temp.gradle')
 apply from: file('gradle/hacks/hashmapAssertions.gradle')
 apply from: file('gradle/hacks/turbocharge-jvm-opts.gradle')
+apply from: file('gradle/hacks/dummy-outputs.gradle')
 
 apply from: file('gradle/solr/packaging.gradle')
 apply from: file('gradle/solr/solr-forbidden-apis.gradle')
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
index 63e733b..b1aee14 100644
--- a/buildSrc/build.gradle
+++ b/buildSrc/build.gradle
@@ -15,11 +15,15 @@
  * limitations under the License.
  */
 
-
-// Make sure the build environment is consistent.
-apply from: file('../gradle/validation/check-environment.gradle')
+ext {
+  // Minimum Java version required to compile buildSrc.
+  minJavaVersion = JavaVersion.VERSION_11
+}
 
 dependencies {
   implementation gradleApi()
   implementation localGroovy()
 }
+
+// Make sure the build environment is consistent.
+apply from: file('../gradle/validation/check-environment.gradle')
diff --git a/gradle/defaults.gradle b/gradle/defaults.gradle
deleted file mode 100644
index a1e1aad..0000000
--- a/gradle/defaults.gradle
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.
- */
-
-allprojects {
-  apply plugin: 'base'
-
-  group "org.apache"
-  
-  def lucenePrereleaseBuild = '5'
-
-  // Repositories to fetch dependencies from.
-  repositories {
-    mavenCentral()
-    maven {
-      name "LucenePrerelease${lucenePrereleaseBuild}"
-      url 
"https://nightlies.apache.org/solr/lucene-prereleases/${lucenePrereleaseBuild}/";
-    }
-  }
-
-  // Artifacts will have names after full gradle project path
-  // so :solr:core will have solr-core.jar, etc.
-  project.archivesBaseName = project.path.replaceAll("^:", "").replace(':', 
'-')
-
-  ext {
-    // Utility method to support passing overrides via -P or -D.
-    propertyOrDefault = { propName, defValue ->
-      def result
-      if (project.hasProperty(propName)) {
-        result = project.getProperty(propName)
-      } else if (System.properties.containsKey(propName)) {
-        result = System.properties.get(propName)
-      } else if (defValue instanceof Closure) {
-        result = defValue.call()
-      } else {
-        result = defValue
-      }
-      return result
-    }
-
-    // System environment variable or default.
-    envOrDefault = { envName, defValue ->
-      return Objects.requireNonNullElse(System.getenv(envName), defValue);
-    }
-
-    // Either a project, system property, environment variable or default 
value.
-    propertyOrEnvOrDefault = { propName, envName, defValue ->
-      return propertyOrDefault(propName, envOrDefault(envName, defValue));
-    }
-
-    // Locate script-relative resource folder. This is context-sensitive so 
pass
-    // the right buildscript (top-level).
-    scriptResources = { buildscript ->
-      return file(buildscript.sourceFile.absolutePath.replaceAll('.gradle$', 
""))
-    }
-
-    // LUCENE-9505: utility function that sets up dummy outputs for a task so 
that
-    // clean[TaskName] works and allows selective re-runs.
-    setupDummyOutputs = { Task task ->
-      File dummyOutput = 
file("${task.project.buildDir}/tasks/${task.name}/dummy-output.txt")
-      task.outputs.file(dummyOutput)
-      task.doLast {
-        if (!dummyOutput.exists()) {
-          dummyOutput.createNewFile()
-        }
-      }
-    }
-  }
-}
diff --git a/gradle/documentation/documentation.gradle 
b/gradle/documentation/documentation.gradle
index 6d2b684..2d5de0d 100644
--- a/gradle/documentation/documentation.gradle
+++ b/gradle/documentation/documentation.gradle
@@ -91,9 +91,7 @@ configure(project(':solr:documentation')) {
       builtBy documentation
     }
   }
-}
 
-configure(project(':solr:documentation')) {
   task documentationMinimal() {
     group = 'documentation'
     description = "Generate stub Solr documentation pointing to web page 
(that's part of Solr TGZ)"
@@ -101,6 +99,9 @@ configure(project(':solr:documentation')) {
   }
 
   task copyChangesToHtmlForMiniSite(type: Copy) {
+    dependsOn 'copyDocumentationAssets'
+    dependsOn 'markdownToHtml'
+
     from project.docroot
     into project.docrootMinimal
     include 'changes/**'
diff --git a/gradle/documentation/render-javadoc.gradle 
b/gradle/documentation/render-javadoc.gradle
index 2e462d6..61cd8af 100644
--- a/gradle/documentation/render-javadoc.gradle
+++ b/gradle/documentation/render-javadoc.gradle
@@ -1,3 +1,5 @@
+import org.gradle.internal.jvm.Jvm
+
 import javax.annotation.Nullable
 
 /*
@@ -214,7 +216,7 @@ class RenderJavadocTask extends DefaultTask {
 
   @Input
   @Optional
-  Property<String> solrDocUrl = project.objects.property(String)
+  final Property<String> solrDocUrl = project.objects.property(String)
 
   // default is to require full javadocs
   @Input
@@ -232,10 +234,10 @@ class RenderJavadocTask extends DefaultTask {
   @Optional
   ListProperty<String> extraOpts = project.objects.listProperty(String)
 
-  @Nullable
   @Optional
   @Input
-  def executable
+  final Property<String> executable = 
project.objects.property(String).convention(
+      project.provider { Jvm.current().javadocExecutable.toString() })
 
   @Input
   def taskResources
@@ -372,58 +374,14 @@ class RenderJavadocTask extends DefaultTask {
       }
     })
 
-    def javadocCmd = {
-      if (executable == null) {
-        JavaInstallationRegistry registry = 
project.extensions.getByType(JavaInstallationRegistry)
-        JavaInstallation currentJvm = 
registry.installationForCurrentVirtualMachine.get()
-        return currentJvm.jdk.get().javadocExecutable.asFile
-      } else {
-        return project.file(executable)
-      }
-    }()
-
+    def javadocCmd = project.file(executable.get())
     logger.info("Javadoc executable used: ${javadocCmd}")
 
-    def outputFile = project.file("${getTemporaryDir()}/javadoc-output.txt")
-    def result
-
-    outputFile.withOutputStream { output ->
-      result = project.exec {
+    project.quietExec {
         executable javadocCmd
 
-        // we want to capture both stdout and stderr to the same
-        // stream but gradle attempts to close these separately
-        // (it has two independent pumping threads) and it can happen
-        // that one still tries to write something when the other closed
-        // the underlying output stream.
-        def wrapped = new java.io.FilterOutputStream(output) {
-          public void close() { 
-            // no-op. we close this stream manually.
-          }
-        }
-        standardOutput = wrapped
-        errorOutput = wrapped
-
         args += [ "@${optionsFile}" ]
         args += jOpts
-
-        ignoreExitValue true
-      }
-    }
-
-    if (result.getExitValue() != 0) {
-      // Pipe the output to console. Intentionally skips any encoding 
conversion 
-      // and pumps raw bytes.
-      System.out.write(outputFile.bytes)
-      System.out.flush()
-
-      def cause
-      try {
-        result.rethrowFailure()
-      } catch (ex) {
-        cause = ex
-      }
-      throw new GradleException("Javadoc generation failed for 
${project.path},\n  Options file at: ${optionsFile}\n  Command output at: 
${outputFile}", cause)
     }
 
     // append some special table css, prettify css
diff --git a/gradle/generation/javacc.gradle b/gradle/generation/javacc.gradle
index 68bd0c9..9adfebc 100644
--- a/gradle/generation/javacc.gradle
+++ b/gradle/generation/javacc.gradle
@@ -37,20 +37,6 @@ configure(rootProject) {
   }
 }
 
-/**
- * Utility function to read a file, apply changes to its content and write it 
back.
- */
-def modifyFile = { File path, Function<String, String> modify ->
-  Function<String, String> normalizeEols = { text -> text.replace("\r\n", 
"\n") }
-  modify = normalizeEols.andThen(modify).andThen(normalizeEols)
-
-  String original = path.getText("UTF-8")
-  String modified = modify.apply(original)
-  if (!original.equals(modified)) {
-    path.write(modified, "UTF-8")
-  }
-}
-
 def commonCleanups = { FileTree generatedFiles ->
   // This is a minor typo in a comment that nonetheless people have 
hand-corrected in the past.
   generatedFiles.matching({ include "CharStream.java" }).each {file ->
@@ -129,17 +115,33 @@ configure(project(":solr:core")) {
 
 // We always regenerate, no need to declare outputs.
 class JavaCCTask extends DefaultTask {
-  @Input
+  @InputFile
   File javaccFile
 
   /**
    * Apply closures to all generated files before they're copied back
    * to mainline code.
    */
-  @Optional
-  @Input
+  // A subtle bug here is that this makes it not an input... should be a list 
of replacements instead?
+  @Internal
   List<Closure<FileTree>> afterGenerate = new ArrayList<>()
 
+  @OutputFiles
+  List<File> getGeneratedSources() {
+    // Return the list of generated files.
+    def baseDir = javaccFile.parentFile
+    def baseName = javaccFile.name.replace(".jj", "")
+
+    return [
+        project.file("${baseDir}/${baseName}.java"),
+        project.file("${baseDir}/${baseName}Constants.java"),
+        project.file("${baseDir}/${baseName}TokenManager.java"),
+        project.file("${baseDir}/ParseException.java"),
+        project.file("${baseDir}/Token.java"),
+        project.file("${baseDir}/TokenMgrError.java")
+    ]
+  }
+
   JavaCCTask() {
     dependsOn(project.rootProject.configurations.javacc)
   }
@@ -157,7 +159,7 @@ class JavaCCTask extends DefaultTask {
     project.delete project.fileTree(tempDir, { include: "**/*.java" })
 
     def targetDir = javaccFile.parentFile
-    logger.lifecycle("Regenerating JavaCC:\n  from: ${javaccFile}\n    to: 
${targetDir}")
+    logger.lifecycle("Recompiling JavaCC: 
${project.rootDir.relativePath(javaccFile)}")
 
     def output = new ByteArrayOutputStream()
     def result = project.javaexec {
diff --git a/gradle/generate-defaults.gradle 
b/gradle/generation/local-settings.gradle
similarity index 58%
rename from gradle/generate-defaults.gradle
rename to gradle/generation/local-settings.gradle
index 806049f..f41ad03 100644
--- a/gradle/generate-defaults.gradle
+++ b/gradle/generation/local-settings.gradle
@@ -35,26 +35,48 @@ configure(rootProject) {
         def testsJvms = (int) Math.max(1d, Math.min(cpus * 0.5d, 12))
 
         // Write the defaults for this machine.
-        rootProject.file("gradle.properties").write(
-            [
-                "# These settings have been generated automatically on the 
first run.",
-                "# See gradlew :helpLocalSettings for more information.",
-                "systemProp.file.encoding=UTF-8",
-                "org.gradle.jvmargs=-Xmx3g", // TODO figure out why "gradlew 
check" runs out of memory if 2g
-                "org.gradle.parallel=true",
-                "org.gradle.priority=normal",
-                "org.gradle.warning.mode=none", // Silence gradle warnings. 
We'll deal with them when we upgrade the wrapper.
-                "",
-                "# You may disable the background daemon if it consumes too 
much memory.",
-                "org.gradle.daemon=true",
-                "org.gradle.daemon.idletimeout=900000", // timeout after 15 
mins.
-                "",
-                "# Maximum number of parallel gradle workers.",
-                "org.gradle.workers.max=${maxWorkers}",
-                "",
-                "# Maximum number of test JVMs forked per test task.",
-                "tests.jvms=${testsJvms}"
-            ].join("\n"), "UTF-8")
+        rootProject.file("gradle.properties").write("""
+# These settings have been generated automatically on the first run.
+# See gradlew :helpLocalSettings for more information.
+systemProp.file.encoding=UTF-8
+
+# Set up gradle JVM defaults.
+# The heap seems huge but gradle runs out of memory on lower values (don't 
know why).
+#
+# We also open up internal compiler modules for spotless/ google jaa format.
+org.gradle.jvmargs=-Xmx3g \\
+ --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \\
+ --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \\
+ --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \\
+ --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \\
+ --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
+
+# Run at normal priority, in parallel
+org.gradle.parallel=true
+org.gradle.priority=normal
+
+# Silence gradle warnings. We'll deal with them when we upgrade the wrapper.
+org.gradle.warning.mode=none
+
+# You may disable the background daemon if it consumes too much memory.
+org.gradle.daemon=true
+# timeout after 15 mins of inactivity.
+org.gradle.daemon.idletimeout=900000
+
+# Maximum number of parallel gradle workers.
+org.gradle.workers.max=${maxWorkers}
+
+# Maximum number of test JVMs forked per test task.
+tests.jvms=${testsJvms}
+
+# Disable auto JVM provisioning (we don't use toolchains yet but want no 
surprises).
+org.gradle.java.installations.auto-download=false
+
+# Set these to enable automatic JVM location discovery.
+org.gradle.java.installations.fromEnv=JDK11,JDK12,JDK13,JDK14,JDK15,JDK16,JDK17
+org.gradle.java.installations.paths=(custom paths)
+
+""", "UTF-8")
 
         logger.log(LogLevel.WARN, "\nIMPORTANT. This is the first time you ran 
the build. " +
             "I wrote some sane defaults (for this machine) to 
'gradle.properties', " +
diff --git a/buildSrc/build.gradle b/gradle/generation/regenerate.gradle
similarity index 58%
copy from buildSrc/build.gradle
copy to gradle/generation/regenerate.gradle
index 63e733b..5fc2845 100644
--- a/buildSrc/build.gradle
+++ b/gradle/generation/regenerate.gradle
@@ -1,3 +1,5 @@
+import java.util.function.Function
+
 /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -15,11 +17,20 @@
  * limitations under the License.
  */
 
+configure(rootProject) {
+  ext {
+    /**
+     * Utility function to read a file, apply changes to its content and write 
it back.
+     */
+    modifyFile = { File path, Function<String, String> modify ->
+      Function<String, String> normalizeEols = { text -> text.replace("\r\n", 
"\n") }
+      modify = normalizeEols.andThen(modify).andThen(normalizeEols)
 
-// Make sure the build environment is consistent.
-apply from: file('../gradle/validation/check-environment.gradle')
-
-dependencies {
-  implementation gradleApi()
-  implementation localGroovy()
+      String original = path.getText("UTF-8")
+      String modified = modify.apply(original)
+      if (!original.equals(modified)) {
+        path.write(modified, "UTF-8")
+      }
+    }
+  }
 }
diff --git a/gradle/globals.gradle b/gradle/globals.gradle
new file mode 100644
index 0000000..816d77b
--- /dev/null
+++ b/gradle/globals.gradle
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+allprojects {
+  apply plugin: 'base'
+
+  group "org.apache"
+
+  def lucenePrereleaseBuild = '5'
+
+  // Repositories to fetch dependencies from.
+  repositories {
+    mavenCentral()
+    maven {
+      name "LucenePrerelease${lucenePrereleaseBuild}"
+      url 
"https://nightlies.apache.org/solr/lucene-prereleases/${lucenePrereleaseBuild}/";
+    }
+  }
+
+  // Artifacts will have names after full gradle project path
+  // so :solr:core will have solr-core.jar, etc.
+  project.archivesBaseName = project.path.replaceAll("^:", "").replace(':', 
'-')
+
+  ext {
+    // Utility method to support passing overrides via -P or -D.
+    propertyOrDefault = { propName, defValue ->
+      def result
+      if (project.hasProperty(propName)) {
+        result = project.getProperty(propName)
+      } else if (System.properties.containsKey(propName)) {
+        result = System.properties.get(propName)
+      } else if (defValue instanceof Closure) {
+        result = defValue.call()
+      } else {
+        result = defValue
+      }
+      return result
+    }
+
+    // System environment variable or default.
+    envOrDefault = { envName, defValue ->
+      return Objects.requireNonNullElse(System.getenv(envName), defValue);
+    }
+
+    // Either a project, system property, environment variable or default 
value.
+    propertyOrEnvOrDefault = { propName, envName, defValue ->
+      return propertyOrDefault(propName, envOrDefault(envName, defValue));
+    }
+
+    // Locate script-relative resource folder. This is context-sensitive so 
pass
+    // the right buildscript (top-level).
+    scriptResources = { buildscript ->
+      return file(buildscript.sourceFile.absolutePath.replaceAll('.gradle$', 
""))
+    }
+
+    // Utility function similar to project.exec but not emitting
+    // any output unless an error code is returned from the executed command.
+    quietExec = { closure ->
+      // Resolve any properties against the provided closure.
+      resolveStrategy = Closure.DELEGATE_ONLY
+      delegate = closure.delegate
+
+      File outputFile = File.createTempFile("exec-output-", ".txt", 
getTemporaryDir())
+      ExecResult result
+      boolean saveIgnoreExitValue
+      ExecSpec saveExecSpec
+
+      outputFile.withOutputStream { output ->
+        // we want to capture both stdout and stderr to the same
+        // stream but gradle attempts to close these separately
+        // (it has two independent pumping threads) and it can happen
+        // that one still tries to write something when the other closed
+        // the underlying output stream.
+        def wrapped = new java.io.FilterOutputStream(output) {
+          public void close() {
+            // no-op. we close this stream manually.
+          }
+        }
+
+        result = project.exec { ExecSpec execSpec ->
+          project.configure(execSpec, closure)
+
+          saveIgnoreExitValue = execSpec.ignoreExitValue
+          saveExecSpec = execSpec
+
+          standardOutput = wrapped
+          errorOutput = wrapped
+          ignoreExitValue true
+        }
+      }
+
+      if (result.getExitValue() != 0) {
+        // Pipe the output to console. Intentionally skips any encoding 
conversion
+        // and pumps raw bytes.
+        logger.error(new String(outputFile.bytes))
+
+        if (!saveIgnoreExitValue) {
+          result.rethrowFailure()
+          throw new GradleException("The executed process 
${saveExecSpec.executable} " +
+              "returned an odd status " +
+              "code: ${result.exitValue}, " +
+              "output at: ${outputFile} (and logged above).")
+        }
+      }
+
+      return result
+    }
+
+    // Convert a list of strings, tasks and task providers into resolved tasks 
or task providers.
+    resolveTaskRefs = { List<Object> refs ->
+      def resolved = refs.collect {
+        if (it instanceof Task) return it
+        if (it instanceof TaskProvider) return it
+        if (it instanceof String) return project.tasks.named((String) it)
+        throw new GradleException("Can't resolve task: ${it}")
+      }
+      return resolved
+    }
+
+    // Forces sequential ordering of a list of tasks (via mustRunAfter).
+    // This method should not be required in 99% of cases, consider regular 
dependsOn links.
+    // This method does NOT imply any ordering between dependencies of task on 
the input
+    // list - the execution of these may still be unordered.
+    mustRunInOrder = { List<Object> taskList ->
+      project.afterEvaluate {
+        def resolved = resolveTaskRefs(taskList)
+
+        // Enforce sequential ordering between tasks (this does NOT apply to 
their dependencies!)
+        for (int i = 1; i < resolved.size(); i++) {
+          resolved[i].configure {
+            logger.info("Scheduling " + resolved[i].name + " to run after " + 
resolved[i - 1].name)
+            mustRunAfter resolved[i - 1]
+          }
+        }
+      }
+      return taskList
+    }
+  }
+}
\ No newline at end of file
diff --git a/buildSrc/build.gradle b/gradle/hacks/dummy-outputs.gradle
similarity index 53%
copy from buildSrc/build.gradle
copy to gradle/hacks/dummy-outputs.gradle
index 63e733b..827feae 100644
--- a/buildSrc/build.gradle
+++ b/gradle/hacks/dummy-outputs.gradle
@@ -15,11 +15,28 @@
  * limitations under the License.
  */
 
+// LUCENE-9505: utility function that sets up dummy outputs for a task so that
+// clean[TaskName] works and allows selective re-runs.
 
-// Make sure the build environment is consistent.
-apply from: file('../gradle/validation/check-environment.gradle')
+def setupDummyOutputs = { Task task ->
+  File dummyOutput = 
file("${task.project.buildDir}/tasks/${task.name}/dummy-output.txt")
+  task.outputs.file(dummyOutput)
+  task.doLast {
+    if (!dummyOutput.exists()) {
+      dummyOutput.createNewFile()
+    }
+  }
+}
 
-dependencies {
-  implementation gradleApi()
-  implementation localGroovy()
+allprojects {
+  tasks.matching {
+    it.name.startsWith("ecjLint") ||
+    it.name in [
+        "gradlewScriptsTweaked",
+        "validateSourcePatterns",
+        "validateLogCalls"
+    ] }.all { Task task ->
+    task.logger.info("Setting up ${task.path} as no-output.")
+    setupDummyOutputs(task)
+  }
 }
diff --git a/gradle/ant-compat/folder-layout.gradle 
b/gradle/java/folder-layout.gradle
similarity index 96%
rename from gradle/ant-compat/folder-layout.gradle
rename to gradle/java/folder-layout.gradle
index c86f68b..d770ff3 100644
--- a/gradle/ant-compat/folder-layout.gradle
+++ b/gradle/java/folder-layout.gradle
@@ -32,11 +32,7 @@ allprojects {
       into sourceSets.test.java.outputDir
     }
     processTestResources.dependsOn copyTestResources
-  }
-}
 
-allprojects {
-  plugins.withType(JavaPlugin) {
     // if 'src/tools' exists, add it as a separate sourceSet.
     if (file('src/tools/java').exists()) {
       sourceSets {
diff --git a/gradle/jar-manifest.gradle b/gradle/java/jar-manifest.gradle
similarity index 92%
rename from gradle/jar-manifest.gradle
rename to gradle/java/jar-manifest.gradle
index ea885fe..9bf773e 100644
--- a/gradle/jar-manifest.gradle
+++ b/gradle/java/jar-manifest.gradle
@@ -61,8 +61,9 @@ subprojects {
                             "Specification-Version" : project.baseVersion,
                             "Specification-Title"   : title,
 
-                            "X-Compile-Source-JDK"  : 
"${project.sourceCompatibility}",
-                            "X-Compile-Target-JDK"  : 
"${project.targetCompatibility}",
+                            // Evaluate these properties lazily so that the 
defaults are applied properly.
+                            "X-Compile-Source-JDK"  : "${-> 
project.sourceCompatibility}",
+                            "X-Compile-Target-JDK"  : "${-> 
project.targetCompatibility}",
 
                             "X-Build-JDK"           : 
"${System.properties['java.version']} (${System.properties['java.vendor']} 
${System.properties['java.vm.version']})",
                             "X-Build-OS"            : 
"${System.properties['os.name']} ${System.properties['os.arch']} 
${System.properties['os.version']}"
diff --git a/gradle/defaults-java.gradle b/gradle/java/javac.gradle
similarity index 85%
rename from gradle/defaults-java.gradle
rename to gradle/java/javac.gradle
index 038b602..9812ed5 100644
--- a/gradle/defaults-java.gradle
+++ b/gradle/java/javac.gradle
@@ -54,6 +54,19 @@ allprojects {
         "-proc:none",  // proc:none was added because of LOG4J2-1925 / 
JDK-8186647
       ]
 
+      // enable some warnings only relevant to newer language features
+      if (rootProject.runtimeJavaVersion >= JavaVersion.VERSION_15) {
+        options.compilerArgs += [
+          "-Xlint:text-blocks",
+        ]
+      }
+
+      if (rootProject.runtimeJavaVersion >= JavaVersion.VERSION_16) {
+        options.compilerArgs += [
+          "-Xlint:synchronization",
+        ]
+      }
+
       if (propertyOrDefault("javac.failOnWarnings", true).toBoolean()) {
         options.compilerArgs += "-Werror"
       }
diff --git a/gradle/testing/alternative-jdk-support.gradle 
b/gradle/testing/alternative-jdk-support.gradle
index 2b252af..d12a39d 100644
--- a/gradle/testing/alternative-jdk-support.gradle
+++ b/gradle/testing/alternative-jdk-support.gradle
@@ -1,3 +1,9 @@
+
+import org.gradle.internal.jvm.JavaInfo
+import org.gradle.internal.jvm.Jvm
+import org.gradle.internal.jvm.inspection.JvmInstallationMetadata
+import org.gradle.internal.jvm.inspection.JvmMetadataDetector
+
 /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -16,62 +22,68 @@
  */
 
 // This adds support for compiling and testing against a different Java 
runtime.
-// This is the only way to build against JVMs not yet supported by Gradle 
itself.
-
-JavaInstallationRegistry registry = 
extensions.getByType(JavaInstallationRegistry)
-
-JavaInstallation currentJvm = 
registry.installationForCurrentVirtualMachine.get()
+//
+// I failed to set it up leveraging Gradle's toolchains because
+// a toolchain spec is not flexible enough to provide an exact location of the 
JVM to be used;
+// if you have two identical JVM lang. versions in auto-discovered JVMs, an 
arbitrary one is used (?).
+// This situation is not uncommon when debugging low-level stuff 
(hand-compiled JVM binaries).
+//
+// The code below is a workaround using internal gradle classes. It may stop 
working in the future
+// but for now it's fine.
 
-JavaInstallation altJvm = {
+JavaInfo jvmGradle = Jvm.current();
+JavaInfo jvmCurrent = {
   def runtimeJavaHome = propertyOrDefault("runtime.java.home", 
System.getenv('RUNTIME_JAVA_HOME'))
-  if (!runtimeJavaHome) {
-    return currentJvm
+  if (runtimeJavaHome != null) {
+    return Jvm.forHome(file(runtimeJavaHome))
   } else {
-    return registry.installationForDirectory(
-        layout.projectDirectory.dir(runtimeJavaHome)).get()
+    return jvmGradle
   }
 }()
 
-// Set up root project's property.
-rootProject.ext.runtimeJava = altJvm
-rootProject.ext.runtimeJavaVersion = altJvm.javaVersion
-
-if (!currentJvm.javaExecutable.equals(altJvm.javaExecutable)) {
-  // Set up java toolchain tasks to use the alternative Java.
-  // This is a related Gradle issue for the future:
-  // https://github.com/gradle/gradle/issues/1652
+JvmMetadataDetector jvmDetector = project.services.get(JvmMetadataDetector)
 
+if (jvmGradle != jvmCurrent) {
   configure(rootProject) {
     task altJvmWarning() {
       doFirst {
+
+        def jvmInfo = { JavaInfo javaInfo ->
+          JvmInstallationMetadata jvmMetadata = 
jvmDetector.getMetadata(javaInfo.javaHome)
+          return "${jvmMetadata.languageVersion} (${jvmMetadata.displayName} 
${jvmMetadata.runtimeVersion}, home at: ${jvmMetadata.javaHome})"
+        }
+
         logger.warn("""NOTE: Alternative java toolchain will be used for 
compilation and tests:
-  Project will use Java ${altJvm.javaVersion} from: 
${altJvm.installationDirectory}
-  Gradle runs with Java ${currentJvm.javaVersion} from: 
${currentJvm.installationDirectory}
+  Project will use ${jvmInfo(jvmCurrent)}
+  Gradle runs with ${jvmInfo(jvmGradle)}
 """)
       }
     }
   }
 
-  // Set up toolchain-dependent tasks to use the alternative JVM.
   allprojects {
     // Any tests
     tasks.withType(Test) {
       dependsOn ":altJvmWarning"
-      executable = altJvm.javaExecutable
+      executable = jvmCurrent.javaExecutable
     }
 
     // Any javac compilation tasks
     tasks.withType(JavaCompile) {
       dependsOn ":altJvmWarning"
       options.fork = true
-      options.forkOptions.javaHome = altJvm.installationDirectory.asFile
+      options.forkOptions.javaHome = jvmCurrent.javaHome
     }
 
     // Javadoc compilation.
-    def javadocExecutable = altJvm.jdk.get().javadocExecutable.asFile
+    def javadocExecutable = jvmCurrent.javadocExecutable
     tasks.matching { it.name == "renderJavadoc" || it.name == 
"renderSiteJavadoc" }.all {
       dependsOn ":altJvmWarning"
-      executable = javadocExecutable
+      executable = javadocExecutable.toString()
     }
   }
 }
+
+// Set up root project's properties.
+rootProject.ext.runtimeJavaHome = jvmCurrent.javaHome
+rootProject.ext.runtimeJavaVersion = 
jvmDetector.getMetadata(jvmCurrent.javaHome).languageVersion
diff --git a/gradle/validation/check-environment.gradle 
b/gradle/validation/check-environment.gradle
index 46b2f86..7694f8d 100644
--- a/gradle/validation/check-environment.gradle
+++ b/gradle/validation/check-environment.gradle
@@ -22,8 +22,7 @@ import org.gradle.util.GradleVersion
 
 configure(rootProject) {
   ext {
-    expectedGradleVersion = '6.8.3'
-    minJavaVersion = JavaVersion.VERSION_11
+    expectedGradleVersion = '7.2'
   }
 
   wrapper {
diff --git a/gradle/validation/ecj-lint.gradle 
b/gradle/validation/ecj-lint.gradle
index 1e645d0..0a7b044 100644
--- a/gradle/validation/ecj-lint.gradle
+++ b/gradle/validation/ecj-lint.gradle
@@ -47,9 +47,6 @@ allprojects {
         // The inputs are all source files from the sourceSet.
         inputs.files sourceSet.allSource.asFileTree
 
-        // This task has no proper outputs.
-        setupDummyOutputs(task)
-
         // We create a task for all source sets but ignore those
         // that don't have any Java source directories.
         enabled = !srcDirs.isEmpty()
diff --git a/gradle/validation/gradlew-scripts-tweaked.gradle 
b/gradle/validation/gradlew-scripts-tweaked.gradle
index fa3ba6d..a37d4e5 100644
--- a/gradle/validation/gradlew-scripts-tweaked.gradle
+++ b/gradle/validation/gradlew-scripts-tweaked.gradle
@@ -29,9 +29,6 @@ configure(rootProject) {
 
     inputs.files(scripts)
 
-    // This task has no proper outputs.
-    setupDummyOutputs(task)
-
     doFirst {
       scripts.each { file ->
         def content = new String(file.readBytes(), StandardCharsets.US_ASCII)
diff --git a/gradle/validation/spotless.gradle 
b/gradle/validation/spotless.gradle
index 34f93fc..a0b10b0 100644
--- a/gradle/validation/spotless.gradle
+++ b/gradle/validation/spotless.gradle
@@ -37,7 +37,7 @@ configure(project(":solr").subprojects) { prj ->
 
         lineEndings 'UNIX'
         endWithNewline()
-        googleJavaFormat('1.9')
+        googleJavaFormat('1.11.0')
 
         // Apply to all Java sources
         target "src/**/*.java"
diff --git a/gradle/validation/validate-log-calls.gradle 
b/gradle/validation/validate-log-calls.gradle
index d759c74..6dc6c72 100644
--- a/gradle/validation/validate-log-calls.gradle
+++ b/gradle/validation/validate-log-calls.gradle
@@ -40,9 +40,6 @@ configure(subprojects.findAll { it.path.startsWith(':solr') 
}) {
       description "Checks that log calls are either validated or conform to 
efficient patterns."
       group "verification"
 
-      // This task has no proper outputs.
-      setupDummyOutputs(task)
-
       sourceFiles = files(sourceSets*.java)
     }
  
@@ -52,10 +49,11 @@ configure(subprojects.findAll { it.path.startsWith(':solr') 
}) {
 }
 
 class ValidateLogCallsTask extends DefaultTask {
+  @Input
   def logLevels = ["log.trace", "log.debug", "log.info", "log.warn", 
"log.error", "log.fatal"]
 
-  def errsFound = 0;
-  def violations = new ArrayList();
+  private def errsFound = 0;
+  private def violations = new ArrayList();
 
   def reportViolation(String msg) {
     violations.add(System.lineSeparator + msg);
diff --git a/gradle/validation/validate-source-patterns.gradle 
b/gradle/validation/validate-source-patterns.gradle
index 74a5af3..009c6d3 100644
--- a/gradle/validation/validate-source-patterns.gradle
+++ b/gradle/validation/validate-source-patterns.gradle
@@ -66,9 +66,6 @@ subprojects {
     group = 'Verification'
     description = 'Validate Source Patterns'
 
-    // This task has no proper outputs.
-    setupDummyOutputs(task)
-
     sourceFiles = fileTree(projectDir) {
       extensions.each{
         include "**/*.${it}"
@@ -100,9 +97,6 @@ configure(rootProject) {
     group = 'Verification'
     description = 'Validate Source Patterns'
 
-    // This task has no proper outputs.
-    setupDummyOutputs(task)
-
     sourceFiles = fileTree(projectDir) {
       extensions.each{
         include "**/*.${it}"
diff --git a/gradle/wrapper/gradle-wrapper.jar 
b/gradle/wrapper/gradle-wrapper.jar
index e708b1c..7454180 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and 
b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.jar.sha256 
b/gradle/wrapper/gradle-wrapper.jar.sha256
index e85f20f..168e29f 100644
--- a/gradle/wrapper/gradle-wrapper.jar.sha256
+++ b/gradle/wrapper/gradle-wrapper.jar.sha256
@@ -1 +1 @@
-e996d452d2645e70c01c11143ca2d3742734a28da2bf61f25c82bdc288c9e637
+33ad4583fd7ee156f533778736fa1b4940bd83b433934d1cc4e9f608e99a6a89
diff --git a/gradle/wrapper/gradle-wrapper.jar.version 
b/gradle/wrapper/gradle-wrapper.jar.version
index 021c940..0ee843c 100644
--- a/gradle/wrapper/gradle-wrapper.jar.version
+++ b/gradle/wrapper/gradle-wrapper.jar.version
@@ -1 +1 @@
-6.8.3
+7.2.0
diff --git a/gradle/wrapper/gradle-wrapper.properties 
b/gradle/wrapper/gradle-wrapper.properties
index 8cf6eb5..a0f7639 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/gradlew.bat b/gradlew.bat
index 7ff625a..fe7be72 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=.
 set APP_BASE_NAME=%~n0
 set APP_HOME=%DIRNAME%
 
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
 @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS 
to pass JVM options to this script.
 set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
 
@@ -42,7 +45,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
 
 set JAVA_EXE=java.exe
 %JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
+if "%ERRORLEVEL%" == "0" goto execute
 
 echo.
 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your 
PATH.
@@ -56,7 +59,7 @@ goto fail
 set JAVA_HOME=%JAVA_HOME:"=%
 set JAVA_EXE=%JAVA_HOME%/bin/java.exe
 
-if exist "%JAVA_EXE%" goto init
+if exist "%JAVA_EXE%" goto execute
 
 echo.
 echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@@ -66,21 +69,6 @@ echo location of your Java installation.
 
 goto fail
 
-:init
-@rem Get command-line arguments, handling Windows variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-
 :execute
 
 @rem LUCENE-9266: verify and download the gradle wrapper jar if we don't have 
one.
@@ -96,7 +84,7 @@ SET GRADLE_DAEMON_CTRL=
 IF NOT EXIST "%DIRNAME%\gradle.properties" SET GRADLE_DAEMON_CTRL=--no-daemon
 
 @rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% 
"-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" 
org.gradle.wrapper.GradleWrapperMain %GRADLE_DAEMON_CTRL% %CMD_LINE_ARGS%
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% 
"-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" 
org.gradle.wrapper.GradleWrapperMain %GRADLE_DAEMON_CTRL% %*
 
 :end
 @rem End local scope for the variables with windows NT shell
diff --git a/solr/contrib/prometheus-exporter/build.gradle 
b/solr/contrib/prometheus-exporter/build.gradle
index ca4f625..311e942 100644
--- a/solr/contrib/prometheus-exporter/build.gradle
+++ b/solr/contrib/prometheus-exporter/build.gradle
@@ -81,6 +81,8 @@ assemblePackaging {
     into "lib"
   })
 
+  // TODO: SOLR-15605 this copy spec has duplicate jars and this fails by 
default in gradle7
+  duplicatesStrategy = 'warn'
 
   into deps
 }
\ No newline at end of file
diff --git a/solr/docker/build.gradle b/solr/docker/build.gradle
index a738c7c..367c9d5 100644
--- a/solr/docker/build.gradle
+++ b/solr/docker/build.gradle
@@ -436,18 +436,26 @@ task testDockerfileOfficial(type: TestDockerImageTask, 
dependsOn: configurations
 }
 
 // Re-usable class for running tests...
-public abstract class TestDockerImageTask extends DefaultTask {
+abstract class TestDockerImageTask extends DefaultTask {
 
   // Ensure that the docker image is re-tested if the image ID changes or the 
test files change
-  @InputFile abstract public RegularFileProperty getIdFile()
-  @InputDirectory final public File sourceDir = project.file("tests/cases")
+  @InputFile
+  abstract public RegularFileProperty getIdFile()
 
-  @Input final public SetProperty<String> testCasesInclude = 
project.objects.setProperty(String)
-  @Input final public SetProperty<String> testCasesExclude = 
project.objects.setProperty(String)
+  @InputDirectory
+  File sourceDir = project.file("tests/cases")
+
+  @Input
+  SetProperty<String> testCasesInclude = project.objects.setProperty(String)
+
+  @Input
+  SetProperty<String> testCasesExclude = project.objects.setProperty(String)
   
-  @OutputDirectory abstract public DirectoryProperty getOutputDir()
+  @OutputDirectory
+  abstract public DirectoryProperty getOutputDir()
 
-  @Inject abstract public WorkerExecutor getWorkerExecutor();
+  @Inject
+  abstract public WorkerExecutor getWorkerExecutor();
 
   public static interface SingleTestParameters extends WorkParameters {
     // NOTE: we explicitly don't use DirectoryProperty here because the way 
WorkerExecutor serializes the params
@@ -457,6 +465,7 @@ public abstract class TestDockerImageTask extends 
DefaultTask {
     Property<String> getWorkDir();
     Property<String> getImageId();
   }
+
   public abstract static class SingleTestAction implements 
WorkAction<SingleTestParameters> {
     @Inject
     abstract public ExecOperations getExec();
diff --git a/solr/solr-ref-guide/build.gradle b/solr/solr-ref-guide/build.gradle
index 03e08fa..866ed52 100644
--- a/solr/solr-ref-guide/build.gradle
+++ b/solr/solr-ref-guide/build.gradle
@@ -28,7 +28,7 @@ buildscript {
 
 plugins {
     id 'java'
-    id 'com.github.jruby-gradle.base' version '2.0.0'
+    id 'com.github.jruby-gradle.base' version '2.0.1'
 }
 
 // This project does not contribute anything to main dependencies.
@@ -215,20 +215,20 @@ check.dependsOn checkLocalJavadocLinksSite, checkSite
 // Hook site building to assemble.
 assemble.dependsOn buildSite
 
-abstract class PrepareSources extends DefaultTask {
+class PrepareSources extends DefaultTask {
     // Original Source files we'll be syncing <b>FROM</b>
     @InputDirectory
-    public DirectoryProperty srcDir = project.objects.directoryProperty()
+    final DirectoryProperty srcDir = project.objects.directoryProperty()
 
     // Destination folder.
     @OutputDirectory
-    public DirectoryProperty outDir = project.objects.directoryProperty()
+    final DirectoryProperty outDir = project.objects.directoryProperty()
 
     /**
      * Task specific props (lazy provider)
      */
     @Input
-    public MapProperty<String, String> props = 
project.objects.mapProperty(String, String)
+    final MapProperty<String, String> props = 
project.objects.mapProperty(String, String)
 
     public PrepareSources() {
         // setup 'dependsOn classes, configurations.depVer' here

Reply via email to