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

Use include_bytes macro to avoid the need for a build script #199

Merged
merged 2 commits into from Jan 11, 2019

Conversation

de-vri-es
Copy link
Contributor

This PR adds an otherwise unused include_bytes!(...); statement in the generated template code for each template in the context map and for each included template. This allows the compiler to detect file level dependencies without the need for a custom build script.

I believe I caught all the relevant file dependencies: extends, imports, includes and of course the template itself (if it is backed by a file, which requires some checks).

This gives more granular file-level dependency information than the build script, although I don't believe that actually gives any practical benefits right now.

Feedback welcome.

@djc
Copy link
Owner

djc commented Jan 10, 2019

Very nice, thanks for implementing this! I've thought about it myself a few times. Have you checked that this has the desired effect? (That is, touching a template file will trigger recompilation even without a build script.)

One issue here is that I think it should probably include bytes for all templates used, including inherited templates, imported templates and included templates. That might be a bit more work to get right, and will actually need something that I've so far resisted doing, which is walking the entire template AST during the Context creation phase.

Are you up for taking a look at that? It basically means adapting Context::new() to do a full traversal of the root template and any inherited templates to discover what templates are used, and then including bytes for all of them.

@djc
Copy link
Owner

djc commented Jan 10, 2019

Update: oh wait, you have handled all that stuff! More power to you!

So, that means only the includes probably don't work correctly right now.

@de-vri-es
Copy link
Contributor Author

de-vri-es commented Jan 10, 2019

I believe I handled includes properly too ;)

I did test locally, and the rebuilds worked as intended. I did not test multi-level includes or extends but from the code I believe it should work correctly recursively already.

/edit: To clarify, I did test extends, but not multi-level extends.

/edit2: Currently, all travis tests for nightly are failing because of this: rust-lang/rust#57488

@de-vri-es
Copy link
Contributor Author

Hmm, it looks like the path comparison to avoid include_bytes!() for the fake path of templates from rust source break on Windows. Or otherwise something else is going wrong on Windows =]

@de-vri-es
Copy link
Contributor Author

I also tested if a rebuild is properly triggered for nested includes, imports and extends now. On Linux, it works as expected. That still leaves the windows issues to fix.

@djc
Copy link
Owner

djc commented Jan 10, 2019

Did you test includes in a for or if block?

@de-vri-es
Copy link
Contributor Author

de-vri-es commented Jan 10, 2019

I did now. Regardless of the true/false value of the condition, changes to the included file trigger a rebuild (which makes sense, since the include_bytes!() is done compile time for the generated code, not runtime).

I think the windows issue is caused by backslashes being parsed into escape sequences somewhere. Not sure why that happens though.

@de-vri-es
Copy link
Contributor Author

Oh, of course. We're outputting source and there's backslashes in the string literal. Is there a proper way to escape string literals in the output of a procedural macro?

@djc
Copy link
Owner

djc commented Jan 10, 2019

Not sure, maybe look at the quote crate API?

@djc
Copy link
Owner

djc commented Jan 10, 2019

Also as follow-up (ideally in a separate commit) get rid of all the build script stuff? That includes in the README, the build script in the test crate and the helper code in the crates that supports the build script API.

This is meant to allow the compiler to understand the dependency between
the generated code and the template source. It removes the need for a
build script.
@de-vri-es
Copy link
Contributor Author

Right, I was meaning to ask if it should be deprecated, but if it can be removed, awesome :) Will do once the tests all pass.

In the mean time, to give you an idea of what the resulting generated code looks like, this was generated from a template with nested imports, nested extends and a conditional include with a nested include (now using quote!{} to fix the generated output if it has escape sequences):

impl < 'a > ::askama::Template for Hello< 'a > {
    fn render_into(&self, writer: &mut ::std::fmt::Write) -> ::askama::Result<()> {
        include_bytes ! ( "/home/maarten/dev/rust/lummel/templates/base2.html" ) ;
        include_bytes ! ( "/home/maarten/dev/rust/lummel/templates/base.html" ) ;
        include_bytes ! ( "/home/maarten/dev/rust/lummel/templates/import1.html" ) ;
        include_bytes ! ( "/home/maarten/dev/rust/lummel/templates/import2.html" ) ;
        include_bytes ! ( "/home/maarten/dev/rust/lummel/templates/hello.html" ) ;
        write!(
            writer,
            "<!DOCTYPE html>\n<html>\n<head>\n\t<title>{expr0}</title>\n\t\n\t<link rel=\"stylesheet\" type=\"text/css\" href=\"/css/main.css\">\n\t\n</head>\n<body>\n<p>Hello world!</p>\n",
            expr0 = &::askama::MarkupDisplay::from(&self.title),
        )?;
        if self.include_foo {
            writer.write_str("\n")?;
            include_bytes ! ( "/home/maarten/dev/rust/lummel/templates/foo.html" ) ;
            writer.write_str("FOO\n")?;
            include_bytes ! ( "/home/maarten/dev/rust/lummel/templates/foo2.html" ) ;
            writer.write_str("FOO2")?;
            writer.write_str("\n")?;
        }
        writer.write_str("\n</body>\n</html>")?;
        Ok(())
    }
    fn extension() -> Option<&'static str> {
        Some("html")
    }
}
impl < 'a > ::std::fmt::Display for Hello< 'a > {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        ::askama::Template::render_into(self, f).map_err(|_| ::std::fmt::Error {})
    }
}

@de-vri-es
Copy link
Contributor Author

The tests are now passing, except for nightly still because of rust-lang/rust#57488.

I added a commit to remove the rerun_if_templates_changed() function and updated the README. I added a note that the build script is no longer needed, but maybe that note shouldn't be there. Let me know if you want me to reword or remove it.

It would probably also be prudent to update the version number in the README just before the next release, since 0.8.0 still needs a build script.

@de-vri-es de-vri-es force-pushed the include-bytes branch 2 times, most recently from bb5e071 to 74fc90c Compare January 11, 2019 09:13
@de-vri-es
Copy link
Contributor Author

Force pushed to re-trigger CI, it now passed =)

@djc
Copy link
Owner

djc commented Jan 11, 2019

Yeah, can you remove the note from the README again? Also I changed my mind, so maybe bring back rerun_if_templates_changed with an empty implementation and a #[deprecated] attribute?

After that I'm done, promise.

@de-vri-es
Copy link
Contributor Author

Righty, removed the note from the readme and re-added the old function, but deprecated and with updated doc.

@djc djc merged commit b3c1fc1 into djc:master Jan 11, 2019
@djc
Copy link
Owner

djc commented Jan 11, 2019

Thanks, great work!

@de-vri-es
Copy link
Contributor Author

Thanks for the review :)

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.

None yet

2 participants