Browse Source

Move precommit task implementation to java (#33407)

Replace precommit tasks that execute with Java implementations
Alpar Torok 7 years ago
parent
commit
5ca6f31205

+ 0 - 67
buildSrc/src/main/groovy/org/elasticsearch/gradle/precommit/JarHellTask.groovy

@@ -1,67 +0,0 @@
-/*
- * Licensed to Elasticsearch under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch 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.
- */
-
-package org.elasticsearch.gradle.precommit
-
-import com.github.jengelman.gradle.plugins.shadow.ShadowPlugin
-import org.elasticsearch.gradle.LoggedExec
-import org.gradle.api.file.FileCollection
-import org.gradle.api.tasks.Classpath
-import org.gradle.api.tasks.OutputFile
-/**
- * Runs CheckJarHell on a classpath.
- */
-public class JarHellTask extends LoggedExec {
-
-    /**
-     * We use a simple "marker" file that we touch when the task succeeds
-     * as the task output. This is compared against the modified time of the
-     * inputs (ie the jars/class files).
-     */
-    @OutputFile
-    File successMarker
-
-    @Classpath
-    FileCollection classpath
-
-    public JarHellTask() {
-        successMarker = new File(project.buildDir, 'markers/jarHell-' + getName())
-        project.afterEvaluate {
-            FileCollection classpath = project.sourceSets.test.runtimeClasspath
-            if (project.plugins.hasPlugin(ShadowPlugin)) {
-                classpath += project.configurations.bundle
-            }
-            inputs.files(classpath)
-            dependsOn(classpath)
-            description = "Runs CheckJarHell on ${classpath}"
-            executable = new File(project.runtimeJavaHome, 'bin/java')
-            doFirst({
-                /* JarHell doesn't like getting directories that don't exist but
-                  gradle isn't especially careful about that. So we have to do it
-                  filter it ourselves. */
-                FileCollection taskClasspath = classpath.filter { it.exists() }
-                args('-cp', taskClasspath.asPath, 'org.elasticsearch.bootstrap.JarHell')
-            })
-            doLast({
-                successMarker.parentFile.mkdirs()
-                successMarker.setText("", 'UTF-8')
-            })
-        }
-    }
-}

+ 0 - 108
buildSrc/src/main/groovy/org/elasticsearch/gradle/precommit/LoggerUsageTask.groovy

@@ -1,108 +0,0 @@
-/*
- * Licensed to Elasticsearch under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch 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.
- */
-
-package org.elasticsearch.gradle.precommit
-
-import org.elasticsearch.gradle.LoggedExec
-import org.gradle.api.file.FileCollection
-import org.gradle.api.tasks.InputFiles
-import org.gradle.api.tasks.OutputFile
-
-/**
- * Runs LoggerUsageCheck on a set of directories.
- */
-public class LoggerUsageTask extends LoggedExec {
-
-    /**
-     * We use a simple "marker" file that we touch when the task succeeds
-     * as the task output. This is compared against the modified time of the
-     * inputs (ie the jars/class files).
-     */
-    private File successMarker = new File(project.buildDir, 'markers/loggerUsage')
-
-    private FileCollection classpath;
-
-    private FileCollection classDirectories;
-
-    public LoggerUsageTask() {
-        project.afterEvaluate {
-            dependsOn(classpath)
-            description = "Runs LoggerUsageCheck on ${classDirectories}"
-            executable = new File(project.runtimeJavaHome, 'bin/java')
-            if (classDirectories == null) {
-                // Default to main and test class files
-                List files = []
-                // But only if the source sets that will make them exist
-                if (project.sourceSets.findByName("main")) {
-                    files.addAll(project.sourceSets.main.output.classesDirs.getFiles())
-                    dependsOn project.tasks.classes
-                }
-                if (project.sourceSets.findByName("test")) {
-                    files.addAll(project.sourceSets.test.output.classesDirs.getFiles())
-                    dependsOn project.tasks.testClasses
-                }
-                /* In an extra twist, it isn't good enough that the source set
-                 * exists. Empty source sets won't make a classes directory
-                 * which will cause the check to fail. We have to filter the
-                 * empty directories out manually. This filter is done right
-                 * before the actual logger usage check giving the rest of the
-                 * build the opportunity to actually build the directory.
-                 */
-                classDirectories = project.files(files).filter { it.exists() }
-            }
-            doFirst({
-                args('-cp', getClasspath().asPath, 'org.elasticsearch.test.loggerusage.ESLoggerUsageChecker')
-                getClassDirectories().each {
-                    args it.getAbsolutePath()
-                }
-            })
-            doLast({
-                successMarker.parentFile.mkdirs()
-                successMarker.setText("", 'UTF-8')
-            })
-        }
-    }
-
-    @InputFiles
-    FileCollection getClasspath() {
-        return classpath
-    }
-
-    void setClasspath(FileCollection classpath) {
-        this.classpath = classpath
-    }
-
-    @InputFiles
-    FileCollection getClassDirectories() {
-        return classDirectories
-    }
-
-    void setClassDirectories(FileCollection classDirectories) {
-        this.classDirectories = classDirectories
-    }
-
-    @OutputFile
-    File getSuccessMarker() {
-        return successMarker
-    }
-
-    void setSuccessMarker(File successMarker) {
-        this.successMarker = successMarker
-    }
-}

+ 11 - 7
buildSrc/src/main/groovy/org/elasticsearch/gradle/precommit/PrecommitTasks.groovy

@@ -18,6 +18,7 @@
  */
 package org.elasticsearch.gradle.precommit
 
+import com.github.jengelman.gradle.plugins.shadow.ShadowPlugin
 import org.elasticsearch.gradle.ExportElasticsearchBuildResourcesTask
 import org.gradle.api.Project
 import org.gradle.api.Task
@@ -87,6 +88,11 @@ class PrecommitTasks {
     private static Task configureJarHell(Project project) {
         Task task = project.tasks.create('jarHell', JarHellTask.class)
         task.classpath = project.sourceSets.test.runtimeClasspath
+        if (project.plugins.hasPlugin(ShadowPlugin)) {
+            task.classpath += project.configurations.bundle
+        }
+        task.dependsOn(project.sourceSets.test.classesTaskName)
+        task.javaHome = project.runtimeJavaHome
         return task
     }
 
@@ -205,22 +211,20 @@ class PrecommitTasks {
 
     private static Task configureNamingConventions(Project project) {
         if (project.sourceSets.findByName("test")) {
-            return project.tasks.create('namingConventions', NamingConventionsTask)
+            Task namingConventionsTask = project.tasks.create('namingConventions', NamingConventionsTask)
+            namingConventionsTask.javaHome = project.runtimeJavaHome
+            return namingConventionsTask
         }
         return null
     }
 
     private static Task configureLoggerUsage(Project project) {
-        Task loggerUsageTask = project.tasks.create('loggerUsageCheck', LoggerUsageTask.class)
-
         project.configurations.create('loggerUsagePlugin')
         project.dependencies.add('loggerUsagePlugin',
                 "org.elasticsearch.test:logger-usage:${org.elasticsearch.gradle.VersionProperties.elasticsearch}")
-
-        loggerUsageTask.configure {
+        return project.tasks.create('loggerUsageCheck', LoggerUsageTask.class) {
             classpath = project.configurations.loggerUsagePlugin
+            javaHome = project.runtimeJavaHome
         }
-
-        return loggerUsageTask
     }
 }

+ 43 - 2
buildSrc/src/main/java/org/elasticsearch/gradle/LoggedExec.java

@@ -1,10 +1,17 @@
 package org.elasticsearch.gradle;
 
+import org.gradle.api.Action;
 import org.gradle.api.GradleException;
+import org.gradle.api.Project;
 import org.gradle.api.tasks.Exec;
+import org.gradle.process.BaseExecSpec;
+import org.gradle.process.ExecResult;
+import org.gradle.process.ExecSpec;
+import org.gradle.process.JavaExecSpec;
 
 import java.io.ByteArrayOutputStream;
 import java.io.UnsupportedEncodingException;
+import java.util.function.Function;
 
 /**
  * A wrapper around gradle's Exec task to capture output and log on error.
@@ -12,9 +19,8 @@ import java.io.UnsupportedEncodingException;
 @SuppressWarnings("unchecked")
 public class LoggedExec extends Exec {
 
-    protected ByteArrayOutputStream output = new ByteArrayOutputStream();
-
     public LoggedExec() {
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
         if (getLogger().isInfoEnabled() == false) {
             setStandardOutput(output);
             setErrorOutput(output);
@@ -41,4 +47,39 @@ public class LoggedExec extends Exec {
             );
         }
     }
+
+    public static ExecResult exec(Project project, Action<ExecSpec> action) {
+        return genericExec(project, project::exec, action);
+    }
+
+    public static ExecResult javaexec(Project project, Action<JavaExecSpec> action) {
+        return genericExec(project, project::javaexec, action);
+    }
+
+    private static <T extends BaseExecSpec>  ExecResult genericExec(
+        Project project,
+        Function<Action<T>,ExecResult> function,
+        Action<T> action
+    ) {
+        if (project.getLogger().isInfoEnabled()) {
+            return function.apply(action);
+        }
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
+        try {
+            return function.apply(spec -> {
+                spec.setStandardOutput(output);
+                spec.setErrorOutput(output);
+                action.execute(spec);
+            });
+        } catch (Exception e) {
+            try {
+                for (String line : output.toString("UTF-8").split("\\R")) {
+                    project.getLogger().error(line);
+                }
+            } catch (UnsupportedEncodingException ue) {
+                throw new GradleException("Failed to read exec output", ue);
+            }
+            throw e;
+        }
+    }
 }

+ 4 - 17
buildSrc/src/main/java/org/elasticsearch/gradle/precommit/ForbiddenApisCliTask.java

@@ -18,7 +18,7 @@
  */
 package org.elasticsearch.gradle.precommit;
 
-import org.gradle.api.DefaultTask;
+import org.elasticsearch.gradle.LoggedExec;
 import org.gradle.api.JavaVersion;
 import org.gradle.api.artifacts.Configuration;
 import org.gradle.api.file.FileCollection;
@@ -26,22 +26,18 @@ import org.gradle.api.logging.Logger;
 import org.gradle.api.logging.Logging;
 import org.gradle.api.tasks.Input;
 import org.gradle.api.tasks.InputFiles;
-import org.gradle.api.tasks.OutputFile;
 import org.gradle.api.tasks.SkipWhenEmpty;
 import org.gradle.api.tasks.SourceSet;
 import org.gradle.api.tasks.TaskAction;
 import org.gradle.process.JavaExecSpec;
 
 import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
 
-public class ForbiddenApisCliTask extends DefaultTask {
+public class ForbiddenApisCliTask extends PrecommitTask {
 
     private final Logger logger = Logging.getLogger(ForbiddenApisCliTask.class);
     private FileCollection signaturesFiles;
@@ -71,14 +67,6 @@ public class ForbiddenApisCliTask extends DefaultTask {
         }
     }
 
-    @OutputFile
-    public File getMarkerFile() {
-        return new File(
-            new File(getProject().getBuildDir(), "precommit"),
-            getName()
-        );
-    }
-
     @InputFiles
     @SkipWhenEmpty
     public FileCollection getClassesDirs() {
@@ -152,8 +140,8 @@ public class ForbiddenApisCliTask extends DefaultTask {
     }
 
     @TaskAction
-    public void runForbiddenApisAndWriteMarker() throws IOException {
-        getProject().javaexec((JavaExecSpec spec) -> {
+    public void runForbiddenApisAndWriteMarker() {
+        LoggedExec.javaexec(getProject(), (JavaExecSpec spec) -> {
             spec.classpath(
                 getForbiddenAPIsConfiguration(),
                 getClassPathFromSourceSet()
@@ -184,7 +172,6 @@ public class ForbiddenApisCliTask extends DefaultTask {
                 spec.args("-d", dir)
             );
         });
-        Files.write(getMarkerFile().toPath(), Collections.emptyList());
     }
 
 }

+ 68 - 0
buildSrc/src/main/java/org/elasticsearch/gradle/precommit/JarHellTask.java

@@ -0,0 +1,68 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch 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.
+ */
+
+package org.elasticsearch.gradle.precommit;
+
+import org.elasticsearch.gradle.LoggedExec;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.tasks.Classpath;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.TaskAction;
+
+/**
+ * Runs CheckJarHell on a classpath.
+ */
+public class JarHellTask extends PrecommitTask {
+
+    private FileCollection classpath;
+
+    private Object javaHome;
+
+    public JarHellTask() {
+        setDescription("Runs CheckJarHell on the configured classpath");
+    }
+
+    @TaskAction
+    public void runJarHellCheck() {
+        LoggedExec.javaexec(getProject(), spec -> {
+            spec.classpath(getClasspath());
+            spec.executable(getJavaHome() + "/bin/java");
+            spec.setMain("org.elasticsearch.bootstrap.JarHell");
+        });
+    }
+
+    @Input
+    public Object getJavaHome() {
+        return javaHome;
+    }
+
+    public void setJavaHome(Object javaHome) {
+        this.javaHome = javaHome;
+    }
+
+    @Classpath
+    public FileCollection getClasspath() {
+        return classpath.filter(file -> file.exists());
+    }
+
+    public void setClasspath(FileCollection classpath) {
+        this.classpath = classpath;
+    }
+
+}

+ 87 - 0
buildSrc/src/main/java/org/elasticsearch/gradle/precommit/LoggerUsageTask.java

@@ -0,0 +1,87 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch 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.
+ */
+
+package org.elasticsearch.gradle.precommit;
+
+import org.elasticsearch.gradle.LoggedExec;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.plugins.JavaPluginConvention;
+import org.gradle.api.tasks.Classpath;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.InputFiles;
+import org.gradle.api.tasks.SkipWhenEmpty;
+import org.gradle.api.tasks.TaskAction;
+
+import java.io.File;
+
+/**
+ * Runs LoggerUsageCheck on a set of directories.
+ */
+public class LoggerUsageTask extends PrecommitTask {
+
+    public LoggerUsageTask() {
+        setDescription("Runs LoggerUsageCheck on output directories of all source sets");
+        getProject().getConvention().getPlugin(JavaPluginConvention.class).getSourceSets().all(sourceSet -> {
+            dependsOn(sourceSet.getClassesTaskName());
+        });
+    }
+
+    @TaskAction
+    public void runLoggerUsageTask() {
+        LoggedExec.javaexec(getProject(), spec -> {
+            spec.setMain("org.elasticsearch.test.loggerusage.ESLoggerUsageChecker");
+            spec.classpath(getClasspath());
+            spec.executable(getJavaHome() + "/bin/java");
+            getClassDirectories().forEach(spec::args);
+        });
+    }
+
+    @Classpath
+    public FileCollection getClasspath() {
+        return classpath;
+    }
+
+    public void setClasspath(FileCollection classpath) {
+        this.classpath = classpath;
+    }
+
+    @InputFiles
+    @SkipWhenEmpty
+    public FileCollection getClassDirectories() {
+        return getProject().getConvention().getPlugin(JavaPluginConvention.class).getSourceSets().stream()
+            // Don't pick up all source sets like the java9 ones as logger-check doesn't support the class format
+            .filter(sourceSet -> sourceSet.getName().equals("main") || sourceSet.getName().equals("test"))
+            .map(sourceSet -> sourceSet.getOutput().getClassesDirs())
+            .reduce(FileCollection::plus)
+            .orElse(getProject().files())
+            .filter(File::exists);
+    }
+
+    @Input
+    public Object getJavaHome() {
+        return javaHome;
+    }
+
+    public void setJavaHome(Object javaHome) {
+        this.javaHome = javaHome;
+    }
+
+    private FileCollection classpath;
+    private Object javaHome;
+}

+ 77 - 100
buildSrc/src/main/java/org/elasticsearch/gradle/precommit/NamingConventionsTask.java

@@ -1,24 +1,20 @@
 package org.elasticsearch.gradle.precommit;
 
-import groovy.lang.Closure;
 import org.elasticsearch.gradle.LoggedExec;
 import org.elasticsearch.test.NamingConventionsCheck;
 import org.gradle.api.GradleException;
-import org.gradle.api.Project;
-import org.gradle.api.Task;
 import org.gradle.api.file.FileCollection;
-import org.gradle.api.plugins.ExtraPropertiesExtension;
 import org.gradle.api.plugins.JavaPluginConvention;
+import org.gradle.api.tasks.Classpath;
 import org.gradle.api.tasks.Input;
-import org.gradle.api.tasks.OutputFile;
+import org.gradle.api.tasks.InputFiles;
+import org.gradle.api.tasks.SkipWhenEmpty;
 import org.gradle.api.tasks.SourceSetContainer;
+import org.gradle.api.tasks.TaskAction;
 
 import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
 import java.net.URISyntaxException;
 import java.net.URL;
-import java.util.Objects;
 
 /**
  * Runs NamingConventionsCheck on a classpath/directory combo to verify that
@@ -26,102 +22,83 @@ import java.util.Objects;
  * gradle. Read the Javadoc for NamingConventionsCheck to learn more.
  */
 @SuppressWarnings("unchecked")
-public class NamingConventionsTask extends LoggedExec {
+public class NamingConventionsTask extends PrecommitTask {
+
     public NamingConventionsTask() {
         setDescription("Tests that test classes aren't misnamed or misplaced");
-        final Project project = getProject();
+        dependsOn(getJavaSourceSets().getByName(checkForTestsInMain ? "main" : "test").getClassesTaskName());
+    }
 
-        SourceSetContainer sourceSets = getJavaSourceSets();
-        final FileCollection classpath;
-        try {
-            URL location = NamingConventionsCheck.class.getProtectionDomain().getCodeSource().getLocation();
-            if (location.getProtocol().equals("file") == false) {
-                throw new GradleException("Unexpected location for NamingConventionCheck class: "+ location);
-            }
-            classpath = project.files(
-                    // This works because the class only depends on one class from junit that will be available from the
-                    // tests compile classpath. It's the most straight forward way of telling Java where to find the main
-                    // class.
-                    location.toURI().getPath(),
-                    // the tests to be loaded
-                    checkForTestsInMain ? sourceSets.getByName("main").getRuntimeClasspath() : project.files(),
-                    sourceSets.getByName("test").getCompileClasspath(),
-                    sourceSets.getByName("test").getOutput()
+    @TaskAction
+    public void runNamingConventions() {
+        LoggedExec.javaexec(getProject(), spec -> {
+            spec.classpath(
+                getNamingConventionsCheckClassFiles(),
+                getSourceSetClassPath()
             );
-        } catch (URISyntaxException e) {
-            throw new AssertionError(e);
-        }
-        dependsOn(project.getTasks().matching(it -> "testCompileClasspath".equals(it.getName())));
-        getInputs().files(classpath);
-
-        setExecutable(new File(
-            Objects.requireNonNull(
-                project.getExtensions().getByType(ExtraPropertiesExtension.class).get("runtimeJavaHome")
-            ).toString(),
-            "bin/java")
-        );
-
-        if (checkForTestsInMain == false) {
-            /* This task is created by default for all subprojects with this
-             * setting and there is no point in running it if the files don't
-             * exist. */
-            onlyIf((unused) -> getExistingClassesDirs().isEmpty() == false);
-        }
-
-        /*
-         * We build the arguments in a funny afterEvaluate/doFirst closure so that we can wait for the classpath to be
-         * ready for us. Strangely neither one on their own are good enough.
-         */
-        project.afterEvaluate(new Closure<Void>(this, this) {
-            public void doCall(Project it) {
-                doFirst(unused -> {
-                    args("-Djna.nosys=true");
-                    args("-cp", classpath.getAsPath(), "org.elasticsearch.test.NamingConventionsCheck");
-                    args("--test-class", getTestClass());
-                    if (skipIntegTestInDisguise) {
-                        args("--skip-integ-tests-in-disguise");
-                    } else {
-                        args("--integ-test-class", getIntegTestClass());
-                    }
-                    if (getCheckForTestsInMain()) {
-                        args("--main");
-                        args("--");
-                    } else {
-                        args("--");
-                    }
-                    args(getExistingClassesDirs().getAsPath());
-                });
+            spec.executable(getJavaHome() + "/bin/java");
+            spec.jvmArgs("-Djna.nosys=true");
+            spec.setMain(NamingConventionsCheck.class.getName());
+            spec.args("--test-class", getTestClass());
+            if (isSkipIntegTestInDisguise()) {
+                spec.args("--skip-integ-tests-in-disguise");
+            } else {
+                spec.args("--integ-test-class", getIntegTestClass());
             }
-        });
-        doLast((Task it) -> {
-            try {
-                try (FileWriter fw = new FileWriter(getSuccessMarker())) {
-                    fw.write("");
-                }
-            } catch (IOException e) {
-                throw new GradleException("io exception", e);
+            if (isCheckForTestsInMain()) {
+                spec.args("--main");
+                spec.args("--");
+            } else {
+                spec.args("--");
             }
+            spec.args(getExistingClassesDirs().getAsPath());
         });
     }
 
-    private SourceSetContainer getJavaSourceSets() {
-        return getProject().getConvention().getPlugin(JavaPluginConvention.class).getSourceSets();
+    @Input
+    public Object getJavaHome() {
+        return javaHome;
     }
 
-    public FileCollection getExistingClassesDirs() {
-        FileCollection classesDirs = getJavaSourceSets().getByName(checkForTestsInMain ? "main" : "test")
-            .getOutput().getClassesDirs();
-        return classesDirs.filter(it -> it.exists());
+    public void setJavaHome(Object javaHome) {
+        this.javaHome = javaHome;
     }
 
-    public File getSuccessMarker() {
-        return successMarker;
+    @Classpath
+    public FileCollection getSourceSetClassPath() {
+        SourceSetContainer sourceSets = getJavaSourceSets();
+        return getProject().files(
+            sourceSets.getByName("test").getCompileClasspath(),
+            sourceSets.getByName("test").getOutput(),
+            checkForTestsInMain ? sourceSets.getByName("main").getRuntimeClasspath() : getProject().files()
+        );
+    }
+
+    @InputFiles
+    public File getNamingConventionsCheckClassFiles() {
+        // This works because the class only depends on one class from junit that will be available from the
+        // tests compile classpath. It's the most straight forward way of telling Java where to find the main
+        // class.
+        URL location = NamingConventionsCheck.class.getProtectionDomain().getCodeSource().getLocation();
+        if (location.getProtocol().equals("file") == false) {
+            throw new GradleException("Unexpected location for NamingConventionCheck class: "+ location);
+        }
+        try {
+            return new File(location.toURI().getPath());
+        } catch (URISyntaxException e) {
+            throw new AssertionError(e);
+        }
     }
 
-    public void setSuccessMarker(File successMarker) {
-        this.successMarker = successMarker;
+    @InputFiles
+    @SkipWhenEmpty
+    public FileCollection getExistingClassesDirs() {
+        FileCollection classesDirs = getJavaSourceSets().getByName(checkForTestsInMain ? "main" : "test")
+            .getOutput().getClassesDirs();
+        return classesDirs.filter(it -> it.exists());
     }
 
+    @Input
     public boolean isSkipIntegTestInDisguise() {
         return skipIntegTestInDisguise;
     }
@@ -130,6 +107,7 @@ public class NamingConventionsTask extends LoggedExec {
         this.skipIntegTestInDisguise = skipIntegTestInDisguise;
     }
 
+    @Input
     public String getTestClass() {
         return testClass;
     }
@@ -138,6 +116,7 @@ public class NamingConventionsTask extends LoggedExec {
         this.testClass = testClass;
     }
 
+    @Input
     public String getIntegTestClass() {
         return integTestClass;
     }
@@ -146,10 +125,7 @@ public class NamingConventionsTask extends LoggedExec {
         this.integTestClass = integTestClass;
     }
 
-    public boolean getCheckForTestsInMain() {
-        return checkForTestsInMain;
-    }
-
+    @Input
     public boolean isCheckForTestsInMain() {
         return checkForTestsInMain;
     }
@@ -158,33 +134,34 @@ public class NamingConventionsTask extends LoggedExec {
         this.checkForTestsInMain = checkForTestsInMain;
     }
 
+    private SourceSetContainer getJavaSourceSets() {
+        return getProject().getConvention().getPlugin(JavaPluginConvention.class).getSourceSets();
+    }
+
     /**
-     * We use a simple "marker" file that we touch when the task succeeds
-     * as the task output. This is compared against the modified time of the
-     * inputs (ie the jars/class files).
+     * The java home to run the check with
      */
-    @OutputFile
-    private File successMarker = new File(getProject().getBuildDir(), "markers/" + this.getName());
+    private Object javaHome; // Make it an Object to allow for Groovy GString
+
     /**
      * Should we skip the integ tests in disguise tests? Defaults to true because only core names its
      * integ tests correctly.
      */
-    @Input
     private boolean skipIntegTestInDisguise = false;
+
     /**
      * Superclass for all tests.
      */
-    @Input
     private String testClass = "org.apache.lucene.util.LuceneTestCase";
+
     /**
      * Superclass for all integration tests.
      */
-    @Input
     private String integTestClass = "org.elasticsearch.test.ESIntegTestCase";
+
     /**
      * Should the test also check the main classpath for test classes instead of
      * doing the usual checks to the test classpath.
      */
-    @Input
     private boolean checkForTestsInMain = false;
 }

+ 43 - 0
buildSrc/src/main/java/org/elasticsearch/gradle/precommit/PrecommitTask.java

@@ -0,0 +1,43 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch 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.
+ */
+package org.elasticsearch.gradle.precommit;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.tasks.OutputFile;
+import org.gradle.api.tasks.TaskAction;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.StandardOpenOption;
+
+public class PrecommitTask extends DefaultTask {
+
+    @OutputFile
+    public File getSuccessMarker() {
+        return new File(getProject().getBuildDir(), "markers/" + this.getName());
+    }
+
+    @TaskAction
+    public void writeMarker() throws IOException {
+        getSuccessMarker().getParentFile().mkdirs();
+        Files.write(getSuccessMarker().toPath(), new byte[]{}, StandardOpenOption.CREATE);
+    }
+
+}

+ 1 - 0
test/framework/build.gradle

@@ -64,6 +64,7 @@ thirdPartyAudit.excludes = [
 
 task namingConventionsMain(type: org.elasticsearch.gradle.precommit.NamingConventionsTask) {
   checkForTestsInMain = true
+  javaHome = project.runtimeJavaHome
 }
 precommit.dependsOn namingConventionsMain