aboutsummaryrefslogtreecommitdiffhomepage
path: root/hugolib/hugo_sites_build.go
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <[email protected]>2024-06-08 11:52:22 +0200
committerBjørn Erik Pedersen <[email protected]>2024-06-23 11:25:47 +0200
commit6cd0784e447f18e009cbbf30de471e486f7cf356 (patch)
tree0684d05d7e487ebe93463636ed8b5a1bb78e4704 /hugolib/hugo_sites_build.go
parent8731d8822216dd3c7587769e3cf5d98690717b0c (diff)
downloadhugo-6cd0784e447f18e009cbbf30de471e486f7cf356.tar.gz
hugo-6cd0784e447f18e009cbbf30de471e486f7cf356.zip
Implement defer
Closes #8086 Closes #12589
Diffstat (limited to 'hugolib/hugo_sites_build.go')
-rw-r--r--hugolib/hugo_sites_build.go204
1 files changed, 169 insertions, 35 deletions
diff --git a/hugolib/hugo_sites_build.go b/hugolib/hugo_sites_build.go
index 12eb6a5f8..65ce946e9 100644
--- a/hugolib/hugo_sites_build.go
+++ b/hugolib/hugo_sites_build.go
@@ -26,6 +26,7 @@ import (
"time"
"github.com/bep/logg"
+ "github.com/gohugoio/hugo/bufferpool"
"github.com/gohugoio/hugo/deps"
"github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/hugofs/files"
@@ -173,6 +174,16 @@ func (h *HugoSites) Build(config BuildCfg, events ...fsnotify.Event) error {
h.SendError(fmt.Errorf("postRenderOnce: %w", err))
}
+ // Make sure to write any build stats to disk first so it's available
+ // to the post processors.
+ if err := h.writeBuildStats(); err != nil {
+ return err
+ }
+
+ if err := h.renderDeferred(infol); err != nil {
+ h.SendError(fmt.Errorf("renderDeferred: %w", err))
+ }
+
if err := h.postProcess(infol); err != nil {
h.SendError(fmt.Errorf("postProcess: %w", err))
}
@@ -352,47 +363,172 @@ func (h *HugoSites) render(l logg.LevelLogger, config *BuildCfg) error {
continue
}
- siteRenderContext.outIdx = siteOutIdx
- siteRenderContext.sitesOutIdx = i
- i++
-
- select {
- case <-h.Done():
- return nil
- default:
- for _, s2 := range h.Sites {
- // We render site by site, but since the content is lazily rendered
- // and a site can "borrow" content from other sites, every site
- // needs this set.
- s2.rc = &siteRenderingContext{Format: renderFormat}
-
- if err := s2.preparePagesForRender(s == s2, siteRenderContext.sitesOutIdx); err != nil {
- return err
+ if err := func() error {
+ rc := tpl.RenderingContext{Site: s, SiteOutIdx: siteOutIdx}
+ h.BuildState.StartStageRender(rc)
+ defer h.BuildState.StopStageRender(rc)
+
+ siteRenderContext.outIdx = siteOutIdx
+ siteRenderContext.sitesOutIdx = i
+ i++
+
+ select {
+ case <-h.Done():
+ return nil
+ default:
+ for _, s2 := range h.Sites {
+ if err := s2.preparePagesForRender(s == s2, siteRenderContext.sitesOutIdx); err != nil {
+ return err
+ }
+ }
+ if !config.SkipRender {
+ ll := l.WithField("substep", "pages").
+ WithField("site", s.language.Lang).
+ WithField("outputFormat", renderFormat.Name)
+
+ start := time.Now()
+
+ if config.PartialReRender {
+ if err := s.renderPages(siteRenderContext); err != nil {
+ return err
+ }
+ } else {
+ if err := s.render(siteRenderContext); err != nil {
+ return err
+ }
+ }
+ loggers.TimeTrackf(ll, start, nil, "")
}
}
- if !config.SkipRender {
- ll := l.WithField("substep", "pages").
- WithField("site", s.language.Lang).
- WithField("outputFormat", renderFormat.Name)
+ return nil
+ }(); err != nil {
+ return err
+ }
- start := time.Now()
+ }
+ }
- if config.PartialReRender {
- if err := s.renderPages(siteRenderContext); err != nil {
- return err
- }
- } else {
- if err := s.render(siteRenderContext); err != nil {
+ return nil
+}
+
+func (h *HugoSites) renderDeferred(l logg.LevelLogger) error {
+ l = l.WithField("step", "render deferred")
+ start := time.Now()
+
+ var deferredCount int
+
+ for rc, de := range h.Deps.BuildState.DeferredExecutionsGroupedByRenderingContext {
+ if de.FilenamesWithPostPrefix.Len() == 0 {
+ continue
+ }
+
+ deferredCount += de.FilenamesWithPostPrefix.Len()
+
+ s := rc.Site.(*Site)
+ for _, s2 := range h.Sites {
+ if err := s2.preparePagesForRender(s == s2, rc.SiteOutIdx); err != nil {
+ return err
+ }
+ }
+ if err := s.executeDeferredTemplates(de); err != nil {
+ return herrors.ImproveRenderErr(err)
+ }
+ }
+
+ loggers.TimeTrackf(l, start, logg.Fields{
+ logg.Field{Name: "count", Value: deferredCount},
+ }, "")
+
+ return nil
+}
+
+func (s *Site) executeDeferredTemplates(de *deps.DeferredExecutions) error {
+ handleFile := func(filename string) error {
+ content, err := afero.ReadFile(s.BaseFs.PublishFs, filename)
+ if err != nil {
+ return err
+ }
+
+ k := 0
+ changed := false
+
+ for {
+ if k >= len(content) {
+ break
+ }
+ l := bytes.Index(content[k:], []byte(tpl.HugoDeferredTemplatePrefix))
+ if l == -1 {
+ break
+ }
+ m := bytes.Index(content[k+l:], []byte(tpl.HugoDeferredTemplateSuffix)) + len(tpl.HugoDeferredTemplateSuffix)
+
+ low, high := k+l, k+l+m
+
+ forward := l + m
+ id := string(content[low:high])
+
+ if err := func() error {
+ deferred, found := de.Executions.Get(id)
+ if !found {
+ panic(fmt.Sprintf("deferred execution with id %q not found", id))
+ }
+ deferred.Mu.Lock()
+ defer deferred.Mu.Unlock()
+
+ if !deferred.Executed {
+ tmpl := s.Deps.Tmpl()
+ templ, found := tmpl.Lookup(deferred.TemplateName)
+ if !found {
+ panic(fmt.Sprintf("template %q not found", deferred.TemplateName))
+ }
+
+ if err := func() error {
+ buf := bufferpool.GetBuffer()
+ defer bufferpool.PutBuffer(buf)
+
+ err = tmpl.ExecuteWithContext(deferred.Ctx, templ, buf, deferred.Data)
+ if err != nil {
return err
}
+ deferred.Result = buf.String()
+ deferred.Executed = true
+
+ return nil
+ }(); err != nil {
+ return err
}
- loggers.TimeTrackf(ll, start, nil, "")
}
+
+ content = append(content[:low], append([]byte(deferred.Result), content[high:]...)...)
+ changed = true
+
+ return nil
+ }(); err != nil {
+ return err
}
+
+ k += forward
}
+
+ if changed {
+ return afero.WriteFile(s.BaseFs.PublishFs, filename, content, 0o666)
+ }
+
+ return nil
}
- return nil
+ g := rungroup.Run[string](context.Background(), rungroup.Config[string]{
+ NumWorkers: s.h.numWorkers,
+ Handle: func(ctx context.Context, filename string) error {
+ return handleFile(filename)
+ },
+ })
+
+ de.FilenamesWithPostPrefix.ForEeach(func(filename string, _ bool) {
+ g.Enqueue(filename)
+ })
+
+ return g.Wait()
}
// / postRenderOnce runs some post processing that only needs to be done once, e.g. printing of unused templates.
@@ -428,12 +564,6 @@ func (h *HugoSites) postProcess(l logg.LevelLogger) error {
l = l.WithField("step", "postProcess")
defer loggers.TimeTrackf(l, time.Now(), nil, "")
- // Make sure to write any build stats to disk first so it's available
- // to the post processors.
- if err := h.writeBuildStats(); err != nil {
- return err
- }
-
// This will only be set when js.Build have been triggered with
// imports that resolves to the project or a module.
// Write a jsconfig.json file to the project's /asset directory
@@ -600,6 +730,10 @@ func (h *HugoSites) writeBuildStats() error {
}
}
+ // This step may be followed by a post process step that may
+ // rebuild e.g. CSS, so clear any cache that's defined for the hugo_stats.json.
+ h.dynacacheGCFilenameIfNotWatchedAndDrainMatching(filename)
+
return nil
}