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

Conversation

jprinet
Copy link
Member

@jprinet jprinet commented Mar 24, 2023

fixes #109

The generated files by the Route and Twirl compilers contain an absolute path (and a timestamp with older playframework versions) which leads to build cache misses.
The relativization in the framework assumes that the current working directory is the project directory which is not the case when using the Worker API ($HOME/.gradle/workers).

This PR removes the DATE comment and relativize the absolute path after SOURCE comment by post-processing the generated files.

@jprinet jprinet force-pushed the feature/strip-routes-comments branch 5 times, most recently from 7036a02 to 0a0728a Compare March 30, 2023 10:58
@jprinet jprinet force-pushed the feature/strip-routes-comments branch from 0a0728a to 0b4e761 Compare March 30, 2023 12:46
@jprinet jprinet marked this pull request as ready for review March 30, 2023 15:07
@jprinet jprinet requested a review from big-guy March 30, 2023 15:07
@@ -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

@jprinet
Copy link
Member Author

jprinet commented Mar 30, 2023

@mkurz you were suggesting here to apply the same post-processing to the TwirlCompiler.
I couldn't find any evidence of an absolute path similar issue with the TwirlCompiler, could you confirm if this is required or not?

@mkurz
Copy link

mkurz commented Mar 30, 2023

Based on the reproducer at the bottom of #109 (comment) but running gradle compilePlayTwirlTemplates and then diffing:

diff ./target/scala-2.13/twirl/main/views/html/ ./build/src/play/twirl/views/html/

I can see that the gradle Play plugin still adds the date and the source is absolute:

/*
    -- GENERATED --
    DATE: 2023-03-30T17:31:29.276683
    SOURCE: /tmp/gradle-test/play-scala-seed/app/views/main.scala.html
    HASH: 71c20c7304dce5c18dc291baf1465b5039e08938
    MATRIX: 987->260|1111->291|1138->292|1218->397|1254->406|1289->414|1315->419|1404->481|1419->487|1482->528|1570->589|1585->595|1646->634|1709->759|1746->769|1774->776|1809->784|1850->798|1865->804|1926->844
    LINES: 26->7|31->8|32->9|35->12|36->13|36->13|36->13|37->14|37->14|37->14|38->15|38->15|38->15|42->20|43->21|43->21|45->23|45->23|45->23|45->23
    -- GENERATED --
*/

Using sbt the date is removed and the path is relative:

/*
    -- GENERATED --
    SOURCE: app/views/main.scala.html
    HASH: 52352176f1a784d96e62964e439264b508b87d6f
    MATRIX: 987->260|1111->291|1138->292|1218->397|1254->406|1289->414|1315->419|1404->481|1419->487|1482->528|1570->589|1585->595|1646->634|1709->759|1746->769|1774->776|1809->784|1850->798|1865->804|1926->844
    LINES: 26->7|31->8|32->9|35->12|36->13|36->13|36->13|37->14|37->14|37->14|38->15|38->15|38->15|42->20|43->21|43->21|45->23|45->23|45->23|45->23
    -- GENERATED --
*/

So yeah I guess you have to apply the same post processing to twirl templates like you do for the routes files.

@mkurz
Copy link

mkurz commented Mar 30, 2023

Hmm. I was wondering why the DATE is still present. You use an outdated twirl version:

return new TwirlCompilerAdapterV13X("1.4.2", scalaCompatibilityVersion, playTwirlAdapter);

You should upgrade to 1.5.1, I am pretty sure at least the DATE is gone, and then we could see if that also fixes the path problem.

@jprinet
Copy link
Member Author

jprinet commented Mar 30, 2023

Alright, the impact is different in that case as an HTML file wouldn't be used as an input of a compilation task. Is it a realistic use case to have the TwirlCompiler producing some java / scala files as well?

@mkurz
Copy link

mkurz commented Mar 30, 2023

Is it a realistic use case to have the TwirlCompiler producing some java / scala files as well?

The twirl compiler is producing scala files. It works more or less the same like the routescompiler. The routescompiler generates mulitiple scala and java files from a foo.routes file. The twirl compiler generates scala files from a foo.scala.html file.
For caching, it will be necessary to also remove the DATE and remove or relativize the path from the generated twirl scala file.s
Exact same problem.

You should upgrade to twirl 1.5.1, independent of this problem btw.

@mkurz
Copy link

mkurz commented Mar 30, 2023

The above snippets are not from html files but from scala files.

@jprinet
Copy link
Member Author

jprinet commented Mar 30, 2023

That makes sense, I'll update the PR ASAP then. Thanks @mkurz 👍

@@ -22,7 +22,7 @@ public static VersionedTwirlCompilerAdapter createAdapter(PlayPlatform playPlatf
return new TwirlCompilerAdapterV13X("1.3.13", scalaCompatibilityVersion, playTwirlAdapter);
case PLAY_2_7_X:
default:
return new TwirlCompilerAdapterV13X("1.4.2", scalaCompatibilityVersion, playTwirlAdapter);
return new TwirlCompilerAdapterV13X("1.5.1", scalaCompatibilityVersion, playTwirlAdapter);
Copy link
Member Author

Choose a reason for hiding this comment

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

My understanding is that there is no breaking change to address
playframework/twirl@1.5.1...1.4.2

Copy link

Choose a reason for hiding this comment

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

I don't think there were breaking changes.

@jprinet jprinet changed the title Add configuration to strip build dependent comments in generated routes Strip build dependent comments in Play compilers generated files Apr 6, 2023
Copy link

@runningcode runningcode left a comment

Choose a reason for hiding this comment

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

just some nits, but looks great!

@@ -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"() {

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

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?

Copy link
Member

@big-guy big-guy left a comment

Choose a reason for hiding this comment

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

Thanks for doing this

@@ -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.

* @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() 👍

// /*
// -- GENERATED --
// DATE: Mon Apr 03 10:27:51 CEST 2023
// SOURCE: /private/var/folders/79/xmc9yr493y75ptry2_nrx3r00000gn/T/junit4995996226044083355/app/views/test.scala.html
Copy link
Member

Choose a reason for hiding this comment

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

What does this look like on Windows?

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point, let me check this

Copy link
Member Author

Choose a reason for hiding this comment

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

The only usage I'm aware of is this one, which is still safe to use after the PR.

private void process(Path generatedFile, String sourceReplacementString) {
try {
String content = new String(Files.readAllBytes(generatedFile), StandardCharsets.UTF_8);
String regexSource = String.format(PATTERN_MATCHER, "SOURCE");
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 using a regex here, would it be simpler to do something like:
For each line...

  • Look for GENERATED_TAG, we're in a generated section.
  • If we're in a generated section, does the line start with DATE or SOURCE?
  • If DATE, throw away the line.
  • If SOURCE, throw away the line and replace it with a new line
  • else, keep the line
  • Look for the GENERATED_TAG again, keep all lines after this once we see it.

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 adjusted the implementation in that sense 👍

// This post processor fixes build / project dependent comments (DATE and SOURCE) from the RoutesCompiler generated files:
// @GENERATOR:play-routes-compiler
// @(DATE): Mon Apr 03 10:27:51 CEST 2023
// @(SOURCE):/private/var/folders/79/xmc9yr493y75ptry2_nrx3r00000gn/T/junit4995996226044083355/conf/routes

Choose a reason for hiding this comment

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

nit: multi-line comments usually use the /** syntax.
Is 16-18 an example?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes

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 adjusted the Javadoc comments 👍

@big-guy big-guy merged commit 5f6a3d0 into gradle:master Apr 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

compileScala failed to get from cache due to compilePlayRoutes Task
4 participants