From c6d650c8c8b22fdc7ddedc1e42a3ca698e1390d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Wed, 15 Jan 2020 15:59:56 +0100 Subject: tpl/tplimpl: Rework template management to get rid of concurrency issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This more or less completes the simplification of the template handling code in Hugo started in v0.62. The main motivation was to fix a long lasting issue about a crash in HTML content files without front matter. But this commit also comes with a big functional improvement. As we now have moved the base template evaluation to the build stage we now use the same lookup rules for `baseof` as for `list` etc. type of templates. This means that in this simple example you can have a `baseof` template for the `blog` section without having to duplicate the others: ``` layouts ├── _default │   ├── baseof.html │   ├── list.html │   └── single.html └── blog └── baseof.html ``` Also, when simplifying code, you often get rid of some double work, as shown in the "site building" benchmarks below. These benchmarks looks suspiciously good, but I have repeated the below with ca. the same result. Compared to master: ``` name old time/op new time/op delta SiteNew/Bundle_with_image-16 13.1ms ± 1% 10.5ms ± 1% -19.34% (p=0.029 n=4+4) SiteNew/Bundle_with_JSON_file-16 13.0ms ± 0% 10.7ms ± 1% -18.05% (p=0.029 n=4+4) SiteNew/Tags_and_categories-16 46.4ms ± 2% 43.1ms ± 1% -7.15% (p=0.029 n=4+4) SiteNew/Canonify_URLs-16 52.2ms ± 2% 47.8ms ± 1% -8.30% (p=0.029 n=4+4) SiteNew/Deep_content_tree-16 77.9ms ± 1% 70.9ms ± 1% -9.01% (p=0.029 n=4+4) SiteNew/Many_HTML_templates-16 43.0ms ± 0% 37.2ms ± 1% -13.54% (p=0.029 n=4+4) SiteNew/Page_collections-16 58.2ms ± 1% 52.4ms ± 1% -9.95% (p=0.029 n=4+4) name old alloc/op new alloc/op delta SiteNew/Bundle_with_image-16 3.81MB ± 0% 2.22MB ± 0% -41.70% (p=0.029 n=4+4) SiteNew/Bundle_with_JSON_file-16 3.60MB ± 0% 2.01MB ± 0% -44.20% (p=0.029 n=4+4) SiteNew/Tags_and_categories-16 19.3MB ± 1% 14.1MB ± 0% -26.91% (p=0.029 n=4+4) SiteNew/Canonify_URLs-16 70.7MB ± 0% 69.0MB ± 0% -2.40% (p=0.029 n=4+4) SiteNew/Deep_content_tree-16 37.1MB ± 0% 31.2MB ± 0% -15.94% (p=0.029 n=4+4) SiteNew/Many_HTML_templates-16 17.6MB ± 0% 10.6MB ± 0% -39.92% (p=0.029 n=4+4) SiteNew/Page_collections-16 25.9MB ± 0% 21.2MB ± 0% -17.99% (p=0.029 n=4+4) name old allocs/op new allocs/op delta SiteNew/Bundle_with_image-16 52.3k ± 0% 26.1k ± 0% -50.18% (p=0.029 n=4+4) SiteNew/Bundle_with_JSON_file-16 52.3k ± 0% 26.1k ± 0% -50.16% (p=0.029 n=4+4) SiteNew/Tags_and_categories-16 336k ± 1% 269k ± 0% -19.90% (p=0.029 n=4+4) SiteNew/Canonify_URLs-16 422k ± 0% 395k ± 0% -6.43% (p=0.029 n=4+4) SiteNew/Deep_content_tree-16 401k ± 0% 313k ± 0% -21.79% (p=0.029 n=4+4) SiteNew/Many_HTML_templates-16 247k ± 0% 143k ± 0% -42.17% (p=0.029 n=4+4) SiteNew/Page_collections-16 282k ± 0% 207k ± 0% -26.55% (p=0.029 n=4+4) ``` Fixes #6716 Fixes #6760 Fixes #6768 Fixes #6778 --- hugolib/site.go | 77 +++++++++++++++++++-------------------------------------- 1 file changed, 26 insertions(+), 51 deletions(-) (limited to 'hugolib/site.go') diff --git a/hugolib/site.go b/hugolib/site.go index eb232c629..bbcbcd27a 100644 --- a/hugolib/site.go +++ b/hugolib/site.go @@ -105,8 +105,6 @@ type Site struct { Sections Taxonomy Info SiteInfo - layoutHandler *output.LayoutHandler - language *langs.Language siteCfg siteConfigHolder @@ -324,7 +322,6 @@ func (s *Site) isEnabled(kind string) bool { // reset returns a new Site prepared for rebuild. func (s *Site) reset() *Site { return &Site{Deps: s.Deps, - layoutHandler: output.NewLayoutHandler(), disabledKinds: s.disabledKinds, titleFunc: s.titleFunc, relatedDocsHandler: s.relatedDocsHandler.Clone(), @@ -439,7 +436,6 @@ func newSite(cfg deps.DepsCfg) (*Site, error) { s := &Site{ PageCollections: c, - layoutHandler: output.NewLayoutHandler(), language: cfg.Language, disabledKinds: disabledKinds, titleFunc: titleFunc, @@ -936,7 +932,7 @@ func (s *Site) processPartial(config *BuildCfg, init func(config *BuildCfg) erro sourceChanged = append(sourceChanged, ev) case files.ComponentFolderLayouts: tmplChanged = true - if _, found := s.Tmpl.Lookup(id.Path); !found { + if !s.Tmpl().HasTemplate(id.Path) { tmplAdded = true } if tmplAdded { @@ -1030,7 +1026,7 @@ func (s *Site) processPartial(config *BuildCfg, init func(config *BuildCfg) erro sourceFilesChanged[ev.Name] = true } - if config.ErrRecovery || tmplAdded { + if config.ErrRecovery || tmplAdded || dataChanged { h.resetPageState() } else { h.resetPageStateFromEvents(changeIdentities) @@ -1226,10 +1222,9 @@ func (s *Site) initializeSiteInfo() error { func (s *Site) eventToIdentity(e fsnotify.Event) (identity.PathIdentity, bool) { for _, fs := range s.BaseFs.SourceFilesystems.FileSystems() { if p := fs.Path(e.Name); p != "" { - return identity.NewPathIdentity(fs.Name, p), true + return identity.NewPathIdentity(fs.Name, filepath.ToSlash(p)), true } } - return identity.PathIdentity{}, false } @@ -1464,12 +1459,22 @@ func (s *Site) permalink(link string) string { } -func (s *Site) renderAndWriteXML(statCounter *uint64, name string, targetPath string, d interface{}, layouts ...string) error { +func (s *Site) lookupLayouts(layouts ...string) tpl.Template { + for _, l := range layouts { + if templ, found := s.Tmpl().Lookup(l); found { + return templ + } + } + + return nil +} + +func (s *Site) renderAndWriteXML(statCounter *uint64, name string, targetPath string, d interface{}, templ tpl.Template) error { s.Log.DEBUG.Printf("Render XML for %q to %q", name, targetPath) renderBuffer := bp.GetBuffer() defer bp.PutBuffer(renderBuffer) - if err := s.renderForLayouts(name, "", d, renderBuffer, layouts...); err != nil { + if err := s.renderForTemplate(name, "", d, renderBuffer, templ); err != nil { return err } @@ -1498,13 +1503,13 @@ func (s *Site) renderAndWriteXML(statCounter *uint64, name string, targetPath st } -func (s *Site) renderAndWritePage(statCounter *uint64, name string, targetPath string, p *pageState, layouts ...string) error { +func (s *Site) renderAndWritePage(statCounter *uint64, name string, targetPath string, p *pageState, templ tpl.Template) error { renderBuffer := bp.GetBuffer() defer bp.PutBuffer(renderBuffer) of := p.outputFormat() - if err := s.renderForLayouts(p.Kind(), of.Name, p, renderBuffer, layouts...); err != nil { + if err := s.renderForTemplate(p.Kind(), of.Name, p, renderBuffer, templ); err != nil { return err } @@ -1571,56 +1576,26 @@ func (r contentLinkRenderer) Render(w io.Writer, ctx hooks.LinkContext) error { return r.templateHandler.Execute(r.templ, w, ctx) } -func (s *Site) lookupTemplate(layouts ...string) (tpl.Template, bool) { - for _, l := range layouts { - if templ, found := s.Tmpl.Lookup(l); found { - return templ, true - } - } - - return nil, false -} - -func (s *Site) renderForLayouts(name, outputFormat string, d interface{}, w io.Writer, layouts ...string) (err error) { - templ := s.findFirstTemplate(layouts...) +func (s *Site) renderForTemplate(name, outputFormat string, d interface{}, w io.Writer, templ tpl.Template) (err error) { if templ == nil { - log := s.Log.WARN - if infoOnMissingLayout[name] { - log = s.Log.INFO - } - - errMsg := "You should create a template file which matches Hugo Layouts Lookup Rules for this combination." - var args []interface{} - msg := "found no layout file for" - if outputFormat != "" { - msg += " %q" - args = append(args, outputFormat) - } - if name != "" { - msg += " for %q" - args = append(args, name) - } - - msg += ": " + errMsg - - log.Printf(msg, args...) - + s.logMissingLayout(name, "", outputFormat) return nil } - if err = s.Tmpl.Execute(templ, w, d); err != nil { + if err = s.Tmpl().Execute(templ, w, d); err != nil { return _errors.Wrapf(err, "render of %q failed", name) } return } -func (s *Site) findFirstTemplate(layouts ...string) tpl.Template { - for _, layout := range layouts { - if templ, found := s.Tmpl.Lookup(layout); found { - return templ +func (s *Site) lookupTemplate(layouts ...string) (tpl.Template, bool) { + for _, l := range layouts { + if templ, found := s.Tmpl().Lookup(l); found { + return templ, true } } - return nil + + return nil, false } func (s *Site) publish(statCounter *uint64, path string, r io.Reader) (err error) { -- cgit v1.2.3