Skip to content

Commit

Permalink
Merge pull request apache#132 from davidmc24/config-cache
Browse files Browse the repository at this point in the history
Add support for configuration cache and update kotlin testing
  • Loading branch information
davidmc24 authored Dec 17, 2020
2 parents 842c328 + 0b45c89 commit c4393b8
Show file tree
Hide file tree
Showing 16 changed files with 116 additions and 36 deletions.
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
# Change Log

## Unreleased
* Add [Configuration Cache](https://docs.gradle.org/6.6/userguide/configuration_cache.html) support (#129; thanks to [dcabasson](https://github.com/dcabasson) and [eskatos](https://github.com/eskatos))
* Add coverage reporting via JaCoco/Codecov to the plugin's build pipeline
* Add support for multiple IDL files with the same name in different directories (#123)
* The `.avpr` file generated by `GenerateAvroProtocolTask` is now based on the namespace and name of the protocol, rather than the name of the `.avdl` file.
* Built using Avro 1.10.1
* Built using Gradle 6.7.1
* Updated compatibility testing to include Java 15
* Updated compatibility testing through Gradle 6.7.1
* Updated compatibility testing through Kotlin 1.4.20

## 0.21.0
* Built using Avro 1.10.0
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ This is a [Gradle](http://www.gradle.org/) plugin to allow easily performing Jav
* If you need support for Avro 1.7.7, try plugin version 0.8.1 (updated for Gradle 5.6)
* Versions of Avro prior to 1.7.x are unlikely to work
* Support for Kotlin
* Currently tested against Kotlin plugin versions 1.3.20-1.3.61 using the latest supported version of Gradle
* Currently tested against Kotlin plugin versions 1.3.20-1.3.72 and 1.4.0-1.4.20 using the latest compatible version of Gradle
* Currently tested against Kotlin plugin versions 1.2.20-1.2.71 and 1.3.0-1.3.11 using Gradle 5.1
* Kotlin plugin versions starting with 1.4.0 require Gradle 5.3+
* Kotlin plugin versions prior to 1.2.30 do not support Java 10+
* Version of the Kotlin plugin prior to 1.2.20 are unlikely to work
* Support for Gradle Kotlin DSL
Expand Down
14 changes: 10 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import org.gradle.util.GradleVersion

plugins {
id "groovy"
id "checkstyle"
Expand Down Expand Up @@ -221,7 +223,7 @@ if (JavaVersion.current().java9Compatible) {
def avroVersions = ["1.10.0", "1.10.1"]

def keyAvroVersions = avroVersions.last()
def gradle5KotlinVersions =[]
def gradle5KotlinVersions = []
if (!JavaVersion.current().java10Compatible) {
// Java 10 support was added in Kotlin 1.2.30
gradle5KotlinVersions.addAll([
Expand All @@ -233,10 +235,12 @@ gradle5KotlinVersions.addAll([
"1.3.0", "1.3.10", "1.3.11",
])
def latestGradleKotlinVersions = [
"1.3.20", "1.3.21", "1.3.30", "1.3.31", "1.3.40", "1.3.41", "1.3.50", "1.3.60", "1.3.61",
"1.3.20", "1.3.21", "1.3.30", "1.3.31", "1.3.40", "1.3.41", "1.3.50", "1.3.60", "1.3.61", "1.3.70", "1.3.71", "1.3.72",
"1.4.0", "1.4.10", "1.4.20", "1.4.21"
]
def kotlinVersions = gradle5KotlinVersions + latestGradleKotlinVersions
def keyKotlinVersions = [] // First and last version for each supported line
// Determine first supported version
if (!JavaVersion.current().java10Compatible) {
// Java 10 support was added in Kotlin 1.2.30
keyKotlinVersions.add("1.2.20")
Expand All @@ -245,8 +249,10 @@ if (!JavaVersion.current().java10Compatible) {
}
keyKotlinVersions.addAll([
"1.2.71",
"1.3.0", "1.3.61",
"1.3.0", "1.3.72", // First and last version for 1.3.x line
"1.4.0", "1.4.21", // First and last version for 1.4.x line
])
def pre5_3KotlinVersion = "1.3.72" // Kotlin plugin 1.4.x appears to require Gradle 5.3+
def keyGradleVersions = [] // First and last version for each supported line
def firstSupportedGradleVersion = null
def gradleVersions = []
Expand Down Expand Up @@ -297,7 +303,7 @@ jacocoTestReport {

avroVersions.each { def avroVersion ->
gradleVersions.each { def gradleVersion ->
def kotlinVersion = latestKotlinVersion
def kotlinVersion = GradleVersion.version(gradleVersion) < GradleVersion.version("5.3") ? pre5_3KotlinVersion : latestKotlinVersion
def newTask = tasks.create(name: "testAvro${avroVersion}Gradle${gradleVersion}", type: Test) {
description = "Test cross-compatibility of the plugin with Avro ${avroVersion} and Gradle ${gradleVersion}"
systemProperties = [
Expand Down
1 change: 1 addition & 0 deletions config/checkstyle/import-control.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<allow pkg="java.lang"/>
<allow pkg="java.net"/>
<allow pkg="java.nio.charset"/>
<allow pkg="java.nio.file"/>
<allow pkg="java.util"/>
<allow pkg="java.util.concurrent"/>
<allow pkg="javax.annotation"/>
Expand Down
17 changes: 17 additions & 0 deletions src/main/java/com/commercehub/gradle/plugin/avro/FileUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Path;
import org.gradle.api.file.ProjectLayout;

/**
* General file manipulation utilities.
Expand Down Expand Up @@ -132,4 +134,19 @@ private static void writeStringToFile(File file, String data, String encoding) t
static void writeJsonFile(File file, String data) throws IOException {
writeStringToFile(file, data, Constants.UTF8_ENCODING);
}

/**
* Acts as a replacement for {@link org.gradle.api.Project#relativePath(Object)}, as Configuration Cache support doesn't allow
* maintaining references to the {@link org.gradle.api.Project}.
*/
static String projectRelativePath(ProjectLayout projectLayout, File file) {
Path path = file.toPath();
if (path.isAbsolute()) {
Path projectDirectoryPath = projectLayout.getProjectDirectory().getAsFile().toPath();
path = projectDirectoryPath.relativize(path);
} else {
path = file.toPath();
}
return path.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.apache.avro.generic.GenericData.StringType;
import org.gradle.api.GradleException;
import org.gradle.api.file.FileCollection;
import org.gradle.api.file.ProjectLayout;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.provider.ListProperty;
import org.gradle.api.provider.MapProperty;
Expand Down Expand Up @@ -81,6 +82,7 @@ public class GenerateAvroJavaTask extends OutputDirTask {
private final Provider<StringType> stringTypeProvider;
private final Provider<FieldVisibility> fieldVisibilityProvider;

private final ProjectLayout projectLayout;
private final SchemaResolver resolver;

@Inject
Expand All @@ -103,7 +105,8 @@ public GenerateAvroJavaTask(ObjectFactory objects) {
this.logicalTypeFactories = objects.mapProperty(String.class, Constants.LOGICAL_TYPE_FACTORY_TYPE.getConcreteClass())
.convention(DEFAULT_LOGICAL_TYPE_FACTORIES);
this.customConversions = objects.listProperty(Constants.CONVERSION_TYPE.getConcreteClass()).convention(DEFAULT_CUSTOM_CONVERSIONS);
this.resolver = new SchemaResolver(getProject(), getLogger());
this.projectLayout = getProject().getLayout();
this.resolver = new SchemaResolver(projectLayout, getLogger());
}

@Optional
Expand Down Expand Up @@ -312,7 +315,7 @@ private int processSchemaFiles() {
Set<File> files = filterSources(new FileExtensionSpec(SCHEMA_EXTENSION)).getFiles();
ProcessingState processingState = resolver.resolve(files);
for (File file : files) {
String path = getProject().relativePath(file);
String path = FileUtils.projectRelativePath(projectLayout, file);
for (Schema schema : processingState.getSchemasForLocation(path)) {
try {
compile(new SpecificCompiler(schema), file);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,25 @@
enum GradleFeatures {
extensionInjection() {
@Override
boolean isSupported() {
return GradleVersion.current().compareTo(GradleVersions.v5_2) >= 0;
boolean isSupportedBy(GradleVersion version) {
return version.compareTo(GradleVersions.v5_2) >= 0;
}
},
objectFactoryFileCollection() {
@Override
boolean isSupported() {
return GradleVersion.current().compareTo(GradleVersions.v5_3) >= 0;
boolean isSupportedBy(GradleVersion version) {
return version.compareTo(GradleVersions.v5_3) >= 0;
}
},
configCache() {
@Override
boolean isSupportedBy(GradleVersion version) {
return version.compareTo(GradleVersions.v6_6) >= 0;
}
};

abstract boolean isSupported();
abstract boolean isSupportedBy(GradleVersion version);
boolean isSupported() {
return isSupportedBy(GradleVersion.current());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@
class GradleVersions {
static final GradleVersion v5_2 = GradleVersion.version("5.2");
static final GradleVersion v5_3 = GradleVersion.version("5.3");
static final GradleVersion v6_6 = GradleVersion.version("6.6");
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,17 @@
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.avro.Schema;
import org.gradle.api.Project;
import org.gradle.api.file.ProjectLayout;

class ProcessingState {
private final Map<String, TypeState> typeStates = new HashMap<>();
private final Set<FileState> delayedFiles = new LinkedHashSet<>();
private final Queue<FileState> filesToProcess = new LinkedList<>();
private int processedTotal;

ProcessingState(Iterable<File> files, Project project) {
ProcessingState(Iterable<File> files, ProjectLayout projectLayout) {
for (File file : files) {
filesToProcess.add(new FileState(file, project.relativePath(file)));
filesToProcess.add(new FileState(file, FileUtils.projectRelativePath(projectLayout, file)));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
*/
@CacheableTask
public class ResolveAvroDependenciesTask extends OutputDirTask {
private final SchemaResolver resolver = new SchemaResolver(getProject(), getLogger());
private final SchemaResolver resolver = new SchemaResolver(getProject().getLayout(), getLogger());

@TaskAction
protected void process() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import org.apache.avro.Schema;
import org.apache.avro.SchemaParseException;
import org.gradle.api.GradleException;
import org.gradle.api.Project;
import org.gradle.api.file.ProjectLayout;
import org.gradle.api.logging.Logger;

import static com.commercehub.gradle.plugin.avro.MapUtils.asymmetricDifference;
Expand All @@ -18,16 +18,16 @@ class SchemaResolver {
private static Pattern ERROR_UNKNOWN_TYPE = Pattern.compile("(?i).*(undefined name|not a defined name|type not supported).*");
private static Pattern ERROR_DUPLICATE_TYPE = Pattern.compile("Can't redefine: (.*)");

private final Project project;
private final ProjectLayout projectLayout;
private final Logger logger;

SchemaResolver(Project project, Logger logger) {
this.project = project;
SchemaResolver(ProjectLayout projectLayout, Logger logger) {
this.projectLayout = projectLayout;
this.logger = logger;
}

ProcessingState resolve(Iterable<File> files) {
ProcessingState processingState = new ProcessingState(files, project);
ProcessingState processingState = new ProcessingState(files, projectLayout);
while (processingState.isWorkRemaining()) {
processSchemaFile(processingState, processingState.nextFileState());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,19 +159,22 @@ class AvroPluginFunctionalSpec extends FunctionalSpec {
buildFile << """
|def configuredTasks = []
|tasks.configureEach {
| configuredTasks << it
|}
|gradle.buildFinished {
| println "Configured tasks: \${configuredTasks*.path}"
| println "Configured task: \${it.path}"
|}
|""".stripMargin()

when:
def result = run("help")

then:
def taskMatcher = result.output =~ /(?m)^Configured tasks: (.*)$/
taskMatcher.find()
def configuredTasks = taskMatcher.group(1)
configuredTasks == "[:help]"
def expectedConfiguredTasks = [":help"]
if (GradleFeatures.configCache.isSupportedBy(gradleVersion)) {
// Not sure why, but when configuration caching was introduced, the base plugin started configuring the
// clean task even if it wasn't called.
expectedConfiguredTasks << ":clean"
}
def actualConfiguredTasks = []
result.output.findAll(/(?m)^Configured task: (.*)$/) { match, taskPath -> actualConfiguredTasks << taskPath }
actualConfiguredTasks == expectedConfiguredTasks
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,23 @@ abstract class FunctionalSpec extends Specification {
}

protected BuildResult run(String... args = ["build"]) {
return createGradleRunner().withArguments(Arrays.asList(args) + "--stacktrace").build()
return createGradleRunner().withArguments(determineGradleArguments(args)).build()
}

protected BuildResult runAndFail(String... args = ["build"]) {
return createGradleRunner().withArguments(Arrays.asList(args) + "--stacktrace").buildAndFail()
return createGradleRunner().withArguments(determineGradleArguments(args)).buildAndFail()
}

protected String buildOutputClassPath(String suffix) {
return "build/classes/java/main/${suffix}"
}

private List<String> determineGradleArguments(String... args) {
def arguments = ["--stacktrace"]
arguments.addAll(Arrays.asList(args))
if (GradleFeatures.configCache.isSupportedBy(gradleVersion) && !arguments.contains("--no-configuration-cache")) {
arguments << "--configuration-cache"
}
return arguments
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package com.commercehub.gradle.plugin.avro

import org.gradle.testkit.runner.BuildResult

import static org.gradle.testkit.runner.TaskOutcome.SUCCESS

class IntellijFunctionalSpec extends FunctionalSpec {
Expand All @@ -31,7 +33,7 @@ class IntellijFunctionalSpec extends FunctionalSpec {
testProjectDir.newFolder("src", "test", "avro")

when:
run("idea")
runIdea()

then:
def moduleFile = new File(testProjectDir.root, "${testProjectDir.root.name}.iml")
Expand All @@ -48,7 +50,7 @@ class IntellijFunctionalSpec extends FunctionalSpec {

def "generated output directories are created by default"() {
when:
def result = run("idea")
def result = runIdea()

then:
result.task(":idea").outcome == SUCCESS
Expand All @@ -68,7 +70,7 @@ class IntellijFunctionalSpec extends FunctionalSpec {
|""".stripMargin()

when:
def result = run("idea")
def result = runIdea()

then:
result.task(":idea").outcome == SUCCESS
Expand All @@ -77,4 +79,14 @@ class IntellijFunctionalSpec extends FunctionalSpec {
projectFile("build/generatedMainAvro").directory
projectFile("build/generatedTestAvro").directory
}

private BuildResult runIdea() {
def args = ["idea"]
if (GradleFeatures.configCache.isSupportedBy(gradleVersion)) {
// As of Gradle 6.7.1, idea plugin doesn't support configuration cache yet.
// Thus, don't try to use it in this spec.
args << "--no-configuration-cache"
}
return run(args as String[])
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
*/
package com.commercehub.gradle.plugin.avro

import org.gradle.testkit.runner.BuildResult
import org.gradle.util.GradleVersion

@SuppressWarnings(["Println"])
class KotlinCompatibilityFunctionalSpec extends FunctionalSpec {
@SuppressWarnings(["FieldName"])
Expand All @@ -39,9 +42,22 @@ class KotlinCompatibilityFunctionalSpec extends FunctionalSpec {
copyResource("helloWorld.kt", kotlinDir)

when:
def result = run("run")
def result = runBuild()

then:
result.output.contains("Hello, David")
}

private BuildResult runBuild() {
def args = ["run"]
if (GradleFeatures.configCache.isSupportedBy(gradleVersion)) {
// The kotlin plugin prior to 1.4.20 doesn't support the configuration cache, so we need to disable it.
// This is a bit of a mis-use of the GradleVersion class, but it's way easier than writing our own
// version comparison logic.
if (GradleVersion.version(kotlinVersion) < GradleVersion.version("1.4.20")) {
args << "--no-configuration-cache"
}
}
return run(args as String[])
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class SchemaResolverSpec extends Specification {

def setup() {
project = ProjectBuilder.builder().build()
resolver = new SchemaResolver(project, project.logger)
resolver = new SchemaResolver(project.layout, project.logger)
}

@Unroll
Expand Down

0 comments on commit c4393b8

Please sign in to comment.