Skip to content

Commit 30d8d66

Browse files
committed
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 c2e8828 commit 30d8d66

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
@@ -21,10 +21,14 @@ package org.elasticsearch.gradle.precommit
2121
import de.thetaphi.forbiddenapis.gradle.CheckForbiddenApis
2222
import de.thetaphi.forbiddenapis.gradle.ForbiddenApisPlugin
2323
import org.gradle.api.JavaVersion
24+
import org.elasticsearch.gradle.ExportElasticsearchBuildResourcesTask
2425
import org.gradle.api.Project
2526
import org.gradle.api.Task
27+
import org.gradle.api.file.FileCollection
2628
import org.gradle.api.plugins.JavaBasePlugin
2729
import org.gradle.api.plugins.quality.Checkstyle
30+
import org.gradle.api.tasks.JavaExec
31+
import org.gradle.api.tasks.StopExecutionException
2832

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

4654
// tasks with just tests don't need dependency licenses, so this flag makes adding
4755
// the task optional
@@ -102,9 +110,58 @@ class PrecommitTasks {
102110
}
103111
Task forbiddenApis = project.tasks.findByName('forbiddenApis')
104112
forbiddenApis.group = "" // clear group, so this does not show up under verification tasks
113+
105114
return forbiddenApis
106115
}
107116

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