Skip to content

Commit 02f2fad

Browse files
authored
Add a task to run forbiddenapis using cli (#32076)
* Add a task to run forbiddenapis using cli Add a task that offers an equivalent check to the forbidden APIs plugin, but runs it using the forbiddenAPIs CLI instead. This isn't wired into precommit first, and doesn't work for projects that require specific signatures, etc. It's meant to show how this can be used. The next step is to make a custom task type and configure it based on the project extension from the pugin and make some minor adjustments to some build scripts as we can't bee 100% compatible with that at least due to how additional signatures are passed. Notes: - there's no `--target` for the CLI version so we have to pass in specific bundled signature names - the cli task already wires to `runtimeJavaHome` - no equivalent for `failOnUnsupportedJava = false` but doesn't seem to be a problem. Tested with Java 8 to 11 - there's no way to pass additional signatures as URL, these will have to be on the file system, and can't be resources on the cp unless we rely on how forbiddenapis is implemented and mimic them as boundled signatures. - the average of 3 runs is 4% slower using the CLI for :server. ( `./gradlew clean :server:forbiddenApis` vs `./gradlew clean :server:forbiddenApisCli`) - up-to-date checks don't work on the cli task yet, that will happen with the custom task. See also: #31715
1 parent 8114646 commit 02f2fad

File tree

5 files changed

+72
-12
lines changed

5 files changed

+72
-12
lines changed

buildSrc/src/main/groovy/org/elasticsearch/gradle/precommit/PrecommitTasks.groovy

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,14 @@ package org.elasticsearch.gradle.precommit
2020

2121
import de.thetaphi.forbiddenapis.gradle.CheckForbiddenApis
2222
import de.thetaphi.forbiddenapis.gradle.ForbiddenApisPlugin
23+
import org.elasticsearch.gradle.ExportElasticsearchBuildResourcesTask
2324
import org.gradle.api.Project
2425
import org.gradle.api.Task
26+
import org.gradle.api.file.FileCollection
2527
import org.gradle.api.plugins.JavaBasePlugin
2628
import org.gradle.api.plugins.quality.Checkstyle
29+
import org.gradle.api.tasks.JavaExec
30+
import org.gradle.api.tasks.StopExecutionException
2731

2832
/**
2933
* Validation tasks which should be run before committing. These run before tests.
@@ -40,7 +44,11 @@ class PrecommitTasks {
4044
project.tasks.create('licenseHeaders', LicenseHeadersTask.class),
4145
project.tasks.create('filepermissions', FilePermissionsTask.class),
4246
project.tasks.create('jarHell', JarHellTask.class),
43-
project.tasks.create('thirdPartyAudit', ThirdPartyAuditTask.class)]
47+
project.tasks.create('thirdPartyAudit', ThirdPartyAuditTask.class)
48+
]
49+
50+
// Configure it but don't add it as a dependency yet
51+
configureForbiddenApisCli(project)
4452

4553
// tasks with just tests don't need dependency licenses, so this flag makes adding
4654
// the task optional
@@ -96,9 +104,58 @@ class PrecommitTasks {
96104
}
97105
Task forbiddenApis = project.tasks.findByName('forbiddenApis')
98106
forbiddenApis.group = "" // clear group, so this does not show up under verification tasks
107+
99108
return forbiddenApis
100109
}
101110

111+
private static Task configureForbiddenApisCli(Project project) {
112+
project.configurations.create("forbiddenApisCliJar")
113+
project.dependencies {
114+
forbiddenApisCliJar 'de.thetaphi:forbiddenapis:2.5'
115+
}
116+
Task forbiddenApisCli = project.tasks.create('forbiddenApisCli')
117+
118+
project.sourceSets.forEach { sourceSet ->
119+
forbiddenApisCli.dependsOn(
120+
project.tasks.create(sourceSet.getTaskName('forbiddenApisCli', null), JavaExec) {
121+
ExportElasticsearchBuildResourcesTask buildResources = project.tasks.getByName('buildResources')
122+
dependsOn(buildResources)
123+
classpath = project.files(
124+
project.configurations.forbiddenApisCliJar,
125+
sourceSet.compileClasspath,
126+
sourceSet.runtimeClasspath
127+
)
128+
main = 'de.thetaphi.forbiddenapis.cli.CliMain'
129+
executable = "${project.runtimeJavaHome}/bin/java"
130+
args "-b", 'jdk-unsafe-1.8'
131+
args "-b", 'jdk-deprecated-1.8'
132+
args "-b", 'jdk-non-portable'
133+
args "-b", 'jdk-system-out'
134+
args "-f", buildResources.copy("forbidden/jdk-signatures.txt")
135+
args "-f", buildResources.copy("forbidden/es-all-signatures.txt")
136+
args "--suppressannotation", '**.SuppressForbidden'
137+
if (sourceSet.name == 'test') {
138+
args "-f", buildResources.copy("forbidden/es-test-signatures.txt")
139+
args "-f", buildResources.copy("forbidden/http-signatures.txt")
140+
} else {
141+
args "-f", buildResources.copy("forbidden/es-server-signatures.txt")
142+
}
143+
dependsOn sourceSet.classesTaskName
144+
doFirst {
145+
// Forbidden APIs expects only existing dirs, and requires at least one
146+
FileCollection existingOutputs = sourceSet.output.classesDirs
147+
.filter { it.exists() }
148+
if (existingOutputs.isEmpty()) {
149+
throw new StopExecutionException("${sourceSet.name} has no outputs")
150+
}
151+
existingOutputs.forEach { args "-d", it }
152+
}
153+
}
154+
)
155+
}
156+
return forbiddenApisCli
157+
}
158+
102159
private static Task configureCheckstyle(Project project) {
103160
// Always copy the checkstyle configuration files to 'buildDir/checkstyle' since the resources could be located in a jar
104161
// file. If the resources are located in a jar, Gradle will fail when it tries to turn the URL into a file

buildSrc/src/main/groovy/org/elasticsearch/gradle/test/StandaloneRestTestPlugin.groovy

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,14 @@ package org.elasticsearch.gradle.test
2222

2323
import com.carrotsearch.gradle.junit4.RandomizedTestingPlugin
2424
import org.elasticsearch.gradle.BuildPlugin
25+
import org.elasticsearch.gradle.ExportElasticsearchBuildResourcesTask
2526
import org.elasticsearch.gradle.VersionProperties
2627
import org.elasticsearch.gradle.precommit.PrecommitTasks
2728
import org.gradle.api.InvalidUserDataException
2829
import org.gradle.api.Plugin
2930
import org.gradle.api.Project
30-
import org.gradle.api.Task
3131
import org.gradle.api.plugins.JavaBasePlugin
3232
import org.gradle.api.tasks.compile.JavaCompile
33-
3433
/**
3534
* Configures the build to compile tests against Elasticsearch's test framework
3635
* and run REST tests. Use BuildPlugin if you want to build main code as well
@@ -48,6 +47,7 @@ public class StandaloneRestTestPlugin implements Plugin<Project> {
4847
project.pluginManager.apply(JavaBasePlugin)
4948
project.pluginManager.apply(RandomizedTestingPlugin)
5049

50+
project.getTasks().create("buildResources", ExportElasticsearchBuildResourcesTask)
5151
BuildPlugin.globalBuildInfo(project)
5252
BuildPlugin.configureRepositories(project)
5353

buildSrc/src/main/java/org/elasticsearch/gradle/ExportElasticsearchBuildResourcesTask.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import org.gradle.api.DefaultTask;
2222
import org.gradle.api.GradleException;
2323
import org.gradle.api.file.DirectoryProperty;
24-
import org.gradle.api.file.RegularFileProperty;
2524
import org.gradle.api.logging.Logger;
2625
import org.gradle.api.logging.Logging;
2726
import org.gradle.api.tasks.Classpath;
@@ -31,6 +30,7 @@
3130
import org.gradle.api.tasks.StopExecutionException;
3231
import org.gradle.api.tasks.TaskAction;
3332

33+
import java.io.File;
3434
import java.io.IOException;
3535
import java.io.InputStream;
3636
import java.nio.file.Files;
@@ -82,14 +82,14 @@ public void setOutputDir(DirectoryProperty outputDir) {
8282
this.outputDir = outputDir;
8383
}
8484

85-
public RegularFileProperty take(String resource) {
85+
public File copy(String resource) {
8686
if (getState().getExecuted() || getState().getExecuting()) {
8787
throw new GradleException("buildResources can't be configured after the task ran. " +
8888
"Make sure task is not used after configuration time"
8989
);
9090
}
9191
resources.add(resource);
92-
return getProject().getLayout().fileProperty(outputDir.file(resource));
92+
return outputDir.file(resource).get().getAsFile();
9393
}
9494

9595
@TaskAction
@@ -101,12 +101,13 @@ public void doExport() {
101101
.forEach(resourcePath -> {
102102
Path destination = outputDir.get().file(resourcePath).getAsFile().toPath();
103103
try (InputStream is = getClass().getClassLoader().getResourceAsStream(resourcePath)) {
104+
Files.createDirectories(destination.getParent());
104105
if (is == null) {
105106
throw new GradleException("Can't export `" + resourcePath + "` from build-tools: not found");
106107
}
107108
Files.copy(is, destination);
108109
} catch (IOException e) {
109-
throw new GradleException("Can't write resource `" + resourcePath + "` to " + destination);
110+
throw new GradleException("Can't write resource `" + resourcePath + "` to " + destination, e);
110111
}
111112
});
112113
}

buildSrc/src/test/java/org/elasticsearch/gradle/ExportElasticsearchBuildResourcesTaskIT.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.gradle.testkit.runner.BuildResult;
2424
import org.gradle.testkit.runner.GradleRunner;
2525

26+
2627
public class ExportElasticsearchBuildResourcesTaskIT extends GradleIntegrationTestCase {
2728

2829
public static final String PROJECT_NAME = "elasticsearch-build-resources";
@@ -59,6 +60,7 @@ public void testImplicitTaskDependencyCopy() {
5960
.withArguments("clean", "sampleCopyAll", "-s", "-i")
6061
.withPluginClasspath()
6162
.build();
63+
6264
assertTaskSuccessfull(result, ":buildResources");
6365
assertTaskSuccessfull(result, ":sampleCopyAll");
6466
assertBuildFileExists(result, PROJECT_NAME, "sampleCopyAll/checkstyle.xml");

buildSrc/src/testKit/elasticsearch-build-resources/build.gradle

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ ext.licenseFile = file("$buildDir/dummy/license")
66
ext.noticeFile = file("$buildDir/dummy/notice")
77

88
buildResources {
9-
take 'checkstyle.xml'
9+
copy 'checkstyle.xml'
1010
}
1111

1212
task sampleCopyAll(type: Sync) {
@@ -17,13 +17,13 @@ task sampleCopyAll(type: Sync) {
1717

1818
task sample {
1919
// This does not work, task dependencies can't be providers
20-
// dependsOn exportBuildResources.resource('minimumRuntimeVersion')
20+
// dependsOn buildResources.resource('minimumRuntimeVersion')
2121
// Nor does this, despite https://github.com/gradle/gradle/issues/3811
22-
// dependsOn exportBuildResources.outputDir
22+
// dependsOn buildResources.outputDir
2323
// for now it's just
2424
dependsOn buildResources
2525
// we have to refference it at configuration time in order to be picked up
26-
ext.checkstyle_suppressions = buildResources.take('checkstyle_suppressions.xml')
26+
ext.checkstyle_suppressions = buildResources.copy('checkstyle_suppressions.xml')
2727
doLast {
2828
println "This task is using ${file(checkstyle_suppressions)}"
2929
}
@@ -33,6 +33,6 @@ task noConfigAfterExecution {
3333
dependsOn buildResources
3434
doLast {
3535
println "This should cause an error because we are refferencing " +
36-
"${buildResources.take('checkstyle_suppressions.xml')} after the `buildResources` task has ran."
36+
"${buildResources.copy('checkstyle_suppressions.xml')} after the `buildResources` task has ran."
3737
}
3838
}

0 commit comments

Comments
 (0)