Skip to content

Commit 885a82c

Browse files
committed
Access build tools resources (#32201)
Add a buildResources buildResources.take('resource') writes the resources out to the filesystem so the paths can be sent int other tools.
1 parent 7ac0f25 commit 885a82c

File tree

5 files changed

+293
-1
lines changed

5 files changed

+293
-1
lines changed

buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ class BuildPlugin implements Plugin<Project> {
8787
project.pluginManager.apply('nebula.info-scm')
8888
project.pluginManager.apply('nebula.info-jar')
8989

90+
project.getTasks().create("buildResources", ExportElasticsearchBuildResourcesTask)
91+
9092
globalBuildInfo(project)
9193
configureRepositories(project)
9294
configureConfigurations(project)
@@ -101,6 +103,7 @@ class BuildPlugin implements Plugin<Project> {
101103
configureDependenciesInfo(project)
102104
}
103105

106+
104107
/** Performs checks on the build environment and prints information about the build environment. */
105108
static void globalBuildInfo(Project project) {
106109
if (project.rootProject.ext.has('buildChecksDone') == false) {
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.elasticsearch.gradle;
20+
21+
import org.gradle.api.DefaultTask;
22+
import org.gradle.api.GradleException;
23+
import org.gradle.api.file.DirectoryProperty;
24+
import org.gradle.api.file.RegularFileProperty;
25+
import org.gradle.api.logging.Logger;
26+
import org.gradle.api.logging.Logging;
27+
import org.gradle.api.tasks.Classpath;
28+
import org.gradle.api.tasks.Input;
29+
import org.gradle.api.tasks.OutputDirectory;
30+
import org.gradle.api.tasks.SkipWhenEmpty;
31+
import org.gradle.api.tasks.StopExecutionException;
32+
import org.gradle.api.tasks.TaskAction;
33+
34+
import java.io.IOException;
35+
import java.io.InputStream;
36+
import java.nio.file.Files;
37+
import java.nio.file.Path;
38+
import java.util.Collections;
39+
import java.util.HashSet;
40+
import java.util.Set;
41+
42+
/**
43+
* Export Elasticsearch build resources to configurable paths
44+
* <p>
45+
* Wil overwrite existing files and create missing directories.
46+
* Useful for resources that that need to be passed to other processes trough the filesystem or otherwise can't be
47+
* consumed from the classpath.
48+
*/
49+
public class ExportElasticsearchBuildResourcesTask extends DefaultTask {
50+
51+
private final Logger logger = Logging.getLogger(ExportElasticsearchBuildResourcesTask.class);
52+
53+
private final Set<String> resources = new HashSet<>();
54+
55+
private DirectoryProperty outputDir;
56+
57+
public ExportElasticsearchBuildResourcesTask() {
58+
outputDir = getProject().getLayout().directoryProperty(
59+
getProject().getLayout().getBuildDirectory().dir("build-tools-exported")
60+
);
61+
}
62+
63+
@OutputDirectory
64+
public DirectoryProperty getOutputDir() {
65+
return outputDir;
66+
}
67+
68+
@Input
69+
@SkipWhenEmpty
70+
public Set<String> getResources() {
71+
return Collections.unmodifiableSet(resources);
72+
}
73+
74+
@Classpath
75+
public String getResourcesClasspath() {
76+
// This will make sure the task is not considered up to date if the resources are changed.
77+
logger.info("Classpath: {}", System.getProperty("java.class.path"));
78+
return System.getProperty("java.class.path");
79+
}
80+
81+
public void setOutputDir(DirectoryProperty outputDir) {
82+
this.outputDir = outputDir;
83+
}
84+
85+
public RegularFileProperty take(String resource) {
86+
if (getState().getExecuted() || getState().getExecuting()) {
87+
throw new GradleException("buildResources can't be configured after the task ran. " +
88+
"Make sure task is not used after configuration time"
89+
);
90+
}
91+
resources.add(resource);
92+
return getProject().getLayout().fileProperty(outputDir.file(resource));
93+
}
94+
95+
@TaskAction
96+
public void doExport() {
97+
if (resources.isEmpty()) {
98+
throw new StopExecutionException();
99+
}
100+
resources.stream().parallel()
101+
.forEach(resourcePath -> {
102+
Path destination = outputDir.get().file(resourcePath).getAsFile().toPath();
103+
try (InputStream is = getClass().getClassLoader().getResourceAsStream(resourcePath)) {
104+
if (is == null) {
105+
throw new GradleException("Can't export `" + resourcePath + "` from build-tools: not found");
106+
}
107+
Files.copy(is, destination);
108+
} catch (IOException e) {
109+
throw new GradleException("Can't write resource `" + resourcePath + "` to " + destination);
110+
}
111+
});
112+
}
113+
114+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package org.elasticsearch.gradle;
2+
3+
/*
4+
* Licensed to Elasticsearch under one or more contributor
5+
* license agreements. See the NOTICE file distributed with
6+
* this work for additional information regarding copyright
7+
* ownership. Elasticsearch licenses this file to you under
8+
* the Apache License, Version 2.0 (the "License"); you may
9+
* not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing,
15+
* software distributed under the License is distributed on an
16+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17+
* KIND, either express or implied. See the License for the
18+
* specific language governing permissions and limitations
19+
* under the License.
20+
*/
21+
22+
import org.elasticsearch.gradle.test.GradleIntegrationTestCase;
23+
import org.gradle.testkit.runner.BuildResult;
24+
import org.gradle.testkit.runner.GradleRunner;
25+
26+
public class ExportElasticsearchBuildResourcesTaskIT extends GradleIntegrationTestCase {
27+
28+
public static final String PROJECT_NAME = "elasticsearch-build-resources";
29+
30+
public void testUpToDateWithSourcesConfigured() {
31+
GradleRunner.create()
32+
.withProjectDir(getProjectDir(PROJECT_NAME))
33+
.withArguments("clean", "-s")
34+
.withPluginClasspath()
35+
.build();
36+
37+
BuildResult result = GradleRunner.create()
38+
.withProjectDir(getProjectDir(PROJECT_NAME))
39+
.withArguments("buildResources", "-s", "-i")
40+
.withPluginClasspath()
41+
.build();
42+
assertTaskSuccessfull(result, ":buildResources");
43+
assertBuildFileExists(result, PROJECT_NAME, "build-tools-exported/checkstyle.xml");
44+
assertBuildFileExists(result, PROJECT_NAME, "build-tools-exported/checkstyle_suppressions.xml");
45+
46+
result = GradleRunner.create()
47+
.withProjectDir(getProjectDir(PROJECT_NAME))
48+
.withArguments("buildResources", "-s", "-i")
49+
.withPluginClasspath()
50+
.build();
51+
assertTaskUpToDate(result, ":buildResources");
52+
assertBuildFileExists(result, PROJECT_NAME, "build-tools-exported/checkstyle.xml");
53+
assertBuildFileExists(result, PROJECT_NAME, "build-tools-exported/checkstyle_suppressions.xml");
54+
}
55+
56+
public void testImplicitTaskDependencyCopy() {
57+
BuildResult result = GradleRunner.create()
58+
.withProjectDir(getProjectDir(PROJECT_NAME))
59+
.withArguments("clean", "sampleCopyAll", "-s", "-i")
60+
.withPluginClasspath()
61+
.build();
62+
assertTaskSuccessfull(result, ":buildResources");
63+
assertTaskSuccessfull(result, ":sampleCopyAll");
64+
assertBuildFileExists(result, PROJECT_NAME, "sampleCopyAll/checkstyle.xml");
65+
// This is a side effect of compile time reference
66+
assertBuildFileExists(result, PROJECT_NAME, "sampleCopyAll/checkstyle_suppressions.xml");
67+
}
68+
69+
public void testImplicitTaskDependencyInputFileOfOther() {
70+
BuildResult result = GradleRunner.create()
71+
.withProjectDir(getProjectDir(PROJECT_NAME))
72+
.withArguments("clean", "sample", "-s", "-i")
73+
.withPluginClasspath()
74+
.build();
75+
76+
assertTaskSuccessfull(result, ":sample");
77+
assertBuildFileExists(result, PROJECT_NAME, "build-tools-exported/checkstyle.xml");
78+
assertBuildFileExists(result, PROJECT_NAME, "build-tools-exported/checkstyle_suppressions.xml");
79+
}
80+
81+
public void testIncorrectUsage() {
82+
BuildResult result = GradleRunner.create()
83+
.withProjectDir(getProjectDir(PROJECT_NAME))
84+
.withArguments("noConfigAfterExecution", "-s", "-i")
85+
.withPluginClasspath()
86+
.buildAndFail();
87+
assertOutputContains("buildResources can't be configured after the task ran");
88+
}
89+
}

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

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
package org.elasticsearch.gradle.test;
22

3+
import org.gradle.testkit.runner.BuildResult;
4+
import org.gradle.testkit.runner.BuildTask;
35
import org.gradle.testkit.runner.GradleRunner;
6+
import org.gradle.testkit.runner.TaskOutcome;
47

58
import java.io.File;
9+
import java.nio.file.Files;
10+
import java.nio.file.Path;
611
import java.util.List;
712
import java.util.stream.Collectors;
813
import java.util.stream.Stream;
@@ -15,7 +20,7 @@ protected File getProjectDir(String name) {
1520
throw new RuntimeException("Could not find resources dir for integration tests. " +
1621
"Note that these tests can only be ran by Gradle and are not currently supported by the IDE");
1722
}
18-
return new File(root, name);
23+
return new File(root, name).getAbsoluteFile();
1924
}
2025

2126
protected GradleRunner getGradleRunner(String sampleProject) {
@@ -61,4 +66,47 @@ protected void assertOutputDoesNotContain(String output, String... lines) {
6166
}
6267
}
6368

69+
protected void assertTaskSuccessfull(BuildResult result, String taskName) {
70+
BuildTask task = result.task(taskName);
71+
if (task == null) {
72+
fail("Expected task `" + taskName + "` to be successful, but it did not run");
73+
}
74+
assertEquals(
75+
"Expected task to be successful but it was: " + task.getOutcome() +
76+
"\n\nOutput is:\n" + result.getOutput() ,
77+
TaskOutcome.SUCCESS,
78+
task.getOutcome()
79+
);
80+
}
81+
82+
protected void assertTaskUpToDate(BuildResult result, String taskName) {
83+
BuildTask task = result.task(taskName);
84+
if (task == null) {
85+
fail("Expected task `" + taskName + "` to be up-to-date, but it did not run");
86+
}
87+
assertEquals(
88+
"Expected task to be up to date but it was: " + task.getOutcome() +
89+
"\n\nOutput is:\n" + result.getOutput() ,
90+
TaskOutcome.UP_TO_DATE,
91+
task.getOutcome()
92+
);
93+
}
94+
95+
protected void assertBuildFileExists(BuildResult result, String projectName, String path) {
96+
Path absPath = getBuildDir(projectName).toPath().resolve(path);
97+
assertTrue(
98+
result.getOutput() + "\n\nExpected `" + absPath + "` to exists but it did not" +
99+
"\n\nOutput is:\n" + result.getOutput(),
100+
Files.exists(absPath)
101+
);
102+
}
103+
104+
protected void assertBuildFileDoesNotExists(BuildResult result, String projectName, String path) {
105+
Path absPath = getBuildDir(projectName).toPath().resolve(path);
106+
assertFalse(
107+
result.getOutput() + "\n\nExpected `" + absPath + "` bo to exists but it did" +
108+
"\n\nOutput is:\n" + result.getOutput(),
109+
Files.exists(absPath)
110+
);
111+
}
64112
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
plugins {
2+
id 'elasticsearch.build'
3+
}
4+
5+
ext.licenseFile = file("$buildDir/dummy/license")
6+
ext.noticeFile = file("$buildDir/dummy/notice")
7+
8+
buildResources {
9+
take 'checkstyle.xml'
10+
}
11+
12+
task sampleCopyAll(type: Sync) {
13+
/** Note: no explicit dependency. This works with tasks that use the Provider API a.k.a "Lazy Configuration" **/
14+
from buildResources
15+
into "$buildDir/sampleCopyAll"
16+
}
17+
18+
task sample {
19+
// This does not work, task dependencies can't be providers
20+
// dependsOn exportBuildResources.resource('minimumRuntimeVersion')
21+
// Nor does this, despite https://github.com/gradle/gradle/issues/3811
22+
// dependsOn exportBuildResources.outputDir
23+
// for now it's just
24+
dependsOn buildResources
25+
// we have to refference it at configuration time in order to be picked up
26+
ext.checkstyle_suppressions = buildResources.take('checkstyle_suppressions.xml')
27+
doLast {
28+
println "This task is using ${file(checkstyle_suppressions)}"
29+
}
30+
}
31+
32+
task noConfigAfterExecution {
33+
dependsOn buildResources
34+
doLast {
35+
println "This should cause an error because we are refferencing " +
36+
"${buildResources.take('checkstyle_suppressions.xml')} after the `buildResources` task has ran."
37+
}
38+
}

0 commit comments

Comments
 (0)