Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Strip build dependent comments in Play compilers generated files #183

Merged
merged 15 commits into from
Apr 24, 2023
Merged
4 changes: 3 additions & 1 deletion src/docs/asciidoc/10-plugin-conventions.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ include::13-tasks.adoc[]

include::14-source-sets.adoc[]

include::15-dependency-configurations.adoc[]
include::15-dependency-configurations.adoc[]

include::16-routes-comments.adoc[]
4 changes: 4 additions & 0 deletions src/docs/asciidoc/16-routes-comments.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
=== Generated routes files comments

Routes files generated by the compiler contain comments which are changing across builds (absolute path and date depending on the framework version), this prevents tasks using those files as inputs to benefit from build cache.
The plugin is post-processing those files to remove timestamp and convert absolute paths to relative paths.
4 changes: 4 additions & 0 deletions src/docs/asciidoc/50-changes.adoc
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
== Change Log

[discrete]
=== v0.14 (TBD)
* Add configuration to post-process routes comments ({uri-github-issues}/109[issue #109]).

[discrete]
=== v0.13 (2023-01-24)
* Remove usage of internal Gradle API org.gradle.api.internal.artifacts.dsl.LazyPublishArtifact
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ import org.gradle.testkit.runner.TaskOutcome
import org.gradle.util.VersionNumber
import org.junit.Assume

import java.nio.charset.StandardCharsets

import static org.gradle.playframework.fixtures.Repositories.playRepositories
import static org.gradle.playframework.fixtures.file.FileFixtures.assertContentsHaveChangedSince
import static org.gradle.playframework.fixtures.file.FileFixtures.assertModificationTimeHasChangedSince
import static org.gradle.playframework.fixtures.file.FileFixtures.snapshot
import static org.gradle.playframework.plugins.PlayRoutesPlugin.ROUTES_COMPILE_TASK_NAME

Expand Down Expand Up @@ -121,7 +124,7 @@ GET /newroute ${controllers()}.Application.index()

and:
assertContentsHaveChangedSince(scalaRoutesFileSnapshot, getScalaRoutesFile())
assertContentsHaveChangedSince(javaRoutesFileSnapshot, getJavaRoutesFile())
assertModificationTimeHasChangedSince(javaRoutesFileSnapshot, getJavaRoutesFile())
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing the @(DATE) with post-processing actually makes the content identical

assertContentsHaveChangedSince(reverseRoutesFileSnapshot, getReverseRoutesFile())

when:
Expand Down Expand Up @@ -259,4 +262,18 @@ $ROUTES_COMPILE_TASK_NAME {
new File(destinationDir, getReverseRoutesFileName('', '')).text.contains("extra.package")
new File(destinationDir, getScalaRoutesFileName('', '')).text.contains("extra.package")
}

def "post-process generated comments"() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def "post-process generated comments"() {
def "post-processed generated comments contain path and timestamp replacements"() {

given:
withRoutesTemplate()
when:
build(ROUTES_COMPILE_TASK_NAME)
then:
createRouteFileList().each {
def generatedFile = new File(destinationDir, it)
assert generatedFile.isFile()
assert generatedFile.getText(StandardCharsets.UTF_8.toString()).contains("// @(SOURCE):conf/routes")
assert !generatedFile.getText(StandardCharsets.UTF_8.toString()).contains("// @(DATE)")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ final class FileFixtures {
assertNotEquals(oldSnapshot.hash, now.hash)
}

static void assertModificationTimeHasChangedSince(Snapshot oldSnapshot, File file) {
Snapshot now = snapshot(file)
assertNotEquals(oldSnapshot.modTime, now.modTime)
}

private static File assertIsFile(File file) {
assertTrue(file.isFile())
}
Expand Down
17 changes: 15 additions & 2 deletions src/main/java/org/gradle/playframework/tasks/RoutesCompile.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import org.gradle.api.Action;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.file.Directory;
import org.gradle.api.file.DirectoryProperty;
import org.gradle.api.file.FileTree;
import org.gradle.api.provider.ListProperty;
Expand All @@ -29,6 +28,7 @@
import org.gradle.workers.WorkerExecutor;

import javax.inject.Inject;
import java.io.File;

/**
* Task for compiling routes templates into Scala code.
Expand All @@ -52,6 +52,7 @@ public class RoutesCompile extends SourceTask {
private final Property<PlayPlatform> platform;
private final Property<Boolean> injectedRoutesGenerator;
private final ConfigurableFileCollection routesCompilerClasspath;
private final File projectDir;

@Inject
public RoutesCompile(WorkerExecutor workerExecutor) {
Expand All @@ -66,6 +67,7 @@ public RoutesCompile(WorkerExecutor workerExecutor) {
this.injectedRoutesGenerator = getProject().getObjects().property(Boolean.class);
this.injectedRoutesGenerator.set(false);
this.routesCompilerClasspath = getProject().files();
this.projectDir = getProject().getProjectDir();
}

/**
Expand Down Expand Up @@ -105,7 +107,7 @@ public ConfigurableFileCollection getRoutesCompilerClasspath() {
@TaskAction
@SuppressWarnings("Convert2Lambda")
void compile() {
RoutesCompileSpec spec = new DefaultRoutesCompileSpec(getSource().getFiles(), getOutputDirectory().get().getAsFile(), isJavaProject(), getNamespaceReverseRouter().get(), getGenerateReverseRoutes().get(), getInjectedRoutesGenerator().get(), getAdditionalImports().get());
RoutesCompileSpec spec = new DefaultRoutesCompileSpec(getSource().getFiles(), getOutputDirectory().get().getAsFile(), isJavaProject(), getNamespaceReverseRouter().get(), getGenerateReverseRoutes().get(), getInjectedRoutesGenerator().get(), getAdditionalImports().get(), getProjectDir());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's OK to either call getProject().getProjectDir() here or instead inject ProjectLayout and use that here.


if (GradleVersion.current().compareTo(GradleVersion.version("5.6")) < 0) {
workerExecutor.submit(RoutesCompileRunnable.class, workerConfiguration -> {
Expand Down Expand Up @@ -177,4 +179,15 @@ public Property<Boolean> getGenerateReverseRoutes() {
public Property<Boolean> getInjectedRoutesGenerator() {
return injectedRoutesGenerator;
}

/**
* The project directory is used to relativize the route source folder
* when post-processing the generated routes files.
*
* @return The project directory.
*/
@Internal
public File getProjectDir() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of exposing a new property here, I would either call getProject().getProjectDir() above or change this to:

@Inject
protected abstract ProjectLayout getProjectLayout()

And use project layout above.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I directly called getProject().getProjectDir() 👍

return projectDir;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,17 @@ public class DefaultRoutesCompileSpec implements RoutesCompileSpec {
private final boolean generateReverseRoutes;
private final boolean injectedRoutesGenerator;
private final Collection<String> additionalImports;
private final File projectDir;

public DefaultRoutesCompileSpec(Iterable<File> sourceFiles, File outputDirectory, boolean javaProject, boolean namespaceReverseRouter, boolean generateReverseRoutes, boolean injectedRoutesGenerator, Collection<String> additionalImports) {
public DefaultRoutesCompileSpec(Iterable<File> sourceFiles, File outputDirectory, boolean javaProject, boolean namespaceReverseRouter, boolean generateReverseRoutes, boolean injectedRoutesGenerator, Collection<String> additionalImports, File projectDir) {
this.sourceFiles = sourceFiles;
this.outputDirectory = outputDirectory;
this.javaProject = javaProject;
this.namespaceReverseRouter = namespaceReverseRouter;
this.generateReverseRoutes = generateReverseRoutes;
this.injectedRoutesGenerator = injectedRoutesGenerator;
this.additionalImports = additionalImports;
this.projectDir = projectDir;
}

@Override
Expand Down Expand Up @@ -56,4 +58,9 @@ public boolean isInjectedRoutesGenerator() {
public Collection<String> getAdditionalImports() {
return additionalImports;
}

@Override
public File getProjectDir() {
return projectDir;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package org.gradle.playframework.tools.internal.routes;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;

class DefaultRoutesPostProcessor implements Serializable {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add a javadoc to clarify what exactly this class does and what it is processing?


private static final Logger LOGGER = LoggerFactory.getLogger(DefaultRoutesPostProcessor.class);

void execute(RoutesCompileSpec spec) {
String sourceReplacementString = getSourceReplacementString(spec.getSources(), spec.getProjectDir());

try (Stream<Path> stream = Files.find(spec.getDestinationDir().toPath(), Integer.MAX_VALUE, (filePath, fileAttr) -> fileAttr.isRegularFile())) {
stream.forEach(routeFile -> process(routeFile, sourceReplacementString));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the process function already catches the IOException. what else can throw it in this block?

Copy link
Member Author

@jprinet jprinet Apr 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Files.find does

} catch (IOException e) {
LOGGER.warn("Unable to post-process routes", e);
}
}

private String getSourceReplacementString(Iterable<File> sources, File projectDir) {
String sourceReplacementString = "";

if(sources.iterator().hasNext()) {
File sourceFile = sources.iterator().next();
sourceReplacementString = "// @(SOURCE):" +
projectDir.toURI().relativize(sourceFile.toURI())
.getPath()
.replace(File.separator, "/");
}

return sourceReplacementString;
}

private void process(Path routeFile, String sourceReplacementString) {
try {
String content = new String(Files.readAllBytes(routeFile), StandardCharsets.UTF_8);
content = content.replaceAll("(?m)^// @(SOURCE):.*", sourceReplacementString);
content = content.replaceAll("(?m)^// @(DATE):.*", "");
Files.write(routeFile, content.getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
LOGGER.warn(String.format("Unable to post-process route file %s", routeFile.getFileName()), e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,6 @@ public interface RoutesCompileSpec extends PlayCompileSpec, Serializable {
boolean isInjectedRoutesGenerator();

Collection<String> getAdditionalImports();

File getProjectDir();
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@
public class RoutesCompiler implements Compiler<RoutesCompileSpec>, Serializable {
private final VersionedRoutesCompilerAdapter adapter;

private final DefaultRoutesPostProcessor postProcessor;

public RoutesCompiler(VersionedRoutesCompilerAdapter adapter) {
this.adapter = adapter;
this.postProcessor = new DefaultRoutesPostProcessor();
}

@Override
Expand Down Expand Up @@ -42,6 +45,9 @@ public WorkResult execute(RoutesCompileSpec spec) {
didWork = ret || didWork;
}

// Post-process routes files
postProcessor.execute(spec);

return WorkResults.didWork(didWork);
}

Expand Down