ref: 88972b0d5b62861128f101256027a52ab48eae86
parent: 640b8bed21eabfd6e256814eab4b3ab3ad2e3354
author: Bjørn Erik Pedersen <[email protected]>
date: Thu Nov 10 15:55:52 EST 2016
node to page: Refactor the build process To make it easier to follow and understand. Both building and rebuilding now follow a four step flow: 1. Init 2. Process 3. Assemble 4. Render And now there are only one Build method, used for both builds and rebuilds. Updates #2297
--- a/commands/hugo.go
+++ b/commands/hugo.go
@@ -670,7 +670,7 @@
if err := initSites(); err != nil {
return err
}
- return Hugo.Rebuild(hugolib.BuildCfg{PrintStats: !quiet, Watching: true}, events...)
+ return Hugo.Build(hugolib.BuildCfg{PrintStats: !quiet, Watching: true}, events...)
}
// NewWatcher creates a new watcher to watch filesystem events.
--- a/hugolib/hugo_sites.go
+++ b/hugolib/hugo_sites.go
@@ -14,7 +14,6 @@
package hugolib
import (
- "errors"
"fmt"
"html/template"
"os"
@@ -21,7 +20,6 @@
"path"
"strings"
"sync"
- "time"
"github.com/spf13/hugo/helpers"
@@ -28,7 +26,6 @@
"github.com/spf13/viper"
"github.com/bep/inflect"
- "github.com/fsnotify/fsnotify"
"github.com/spf13/hugo/source"
"github.com/spf13/hugo/tpl"
jww "github.com/spf13/jwalterweatherman"
@@ -109,7 +106,7 @@
tpl.ResetCaches()
}
-func (h *HugoSites) reCreateFromConfig() error {
+func (h *HugoSites) createSitesFromConfig() error {
sites, err := createSitesFromConfig()
@@ -158,191 +155,10 @@
// Use this to add templates to use for rendering.
// Useful for testing.
withTemplate func(templ tpl.Template) error
+ // Use this to indicate what changed (for rebuilds).
+ whatChanged *whatChanged
}
-// Build builds all sites.
-func (h *HugoSites) Build(config BuildCfg) error {
-
- t0 := time.Now()
-
- // TODO(bep) np init page collections
- for _, s := range h.Sites {
- if s.PageCollections == nil {
- s.PageCollections = newPageCollections()
- }
- }
-
- if config.ResetState {
- h.reset()
- }
-
- if config.CreateSitesFromConfig {
- if err := h.reCreateFromConfig(); err != nil {
- return err
- }
- }
-
- h.runMode.Watching = config.Watching
-
- // We should probably refactor the Site and pull up most of the logic from there to here,
- // but that seems like a daunting task.
- // So for now, if there are more than one site (language),
- // we pre-process the first one, then configure all the sites based on that.
- firstSite := h.Sites[0]
-
- if err := firstSite.preProcess(config); err != nil {
- return err
- }
-
- h.setupTranslations()
-
- if len(h.Sites) > 1 {
- // Initialize the rest
- for _, site := range h.Sites[1:] {
- site.initializeSiteInfo()
- }
- }
-
- // TODO(bep) make a more logical grouping of these.
- h.assembleGitInfo()
-
- for _, s := range h.Sites {
- if err := s.postProcess(); err != nil {
- return err
- }
- }
-
- // TODO(bep) np createMissingNodes needs taxonomies and sections
- if err := h.createMissingNodes(); err != nil {
- return err
- }
-
- for _, s := range h.Sites {
- // TODO(bep) np Needed by all who use .Pages, .AllPages, .indexPages
- s.refreshPageCaches()
- s.setupPrevNext()
- }
-
- if err := h.assignMissingTranslations(); err != nil {
- return err
- }
-
- if err := h.preRender(config, whatChanged{source: true, other: true}); err != nil {
- return err
- }
-
- if !config.SkipRender {
- for _, s := range h.Sites {
-
- if err := s.render(); err != nil {
- return err
- }
-
- if config.PrintStats {
- s.Stats()
- }
- }
-
- if err := h.render(); err != nil {
- return err
- }
- }
-
- if config.PrintStats {
- jww.FEEDBACK.Printf("total in %v ms\n", int(1000*time.Since(t0).Seconds()))
- }
-
- return nil
-
-}
-
-// Rebuild rebuilds all sites.
-func (h *HugoSites) Rebuild(config BuildCfg, events ...fsnotify.Event) error {
- t0 := time.Now()
-
- if config.CreateSitesFromConfig {
- return errors.New("Rebuild does not support 'CreateSitesFromConfig'. Use Build.")
- }
-
- if config.ResetState {
- return errors.New("Rebuild does not support 'ResetState'. Use Build.")
- }
-
- if !config.Watching {
- return errors.New("Rebuild called when not in watch mode")
- }
-
- h.runMode.Watching = config.Watching
-
- firstSite := h.Sites[0]
-
- for _, s := range h.Sites {
- s.resetBuildState()
- }
-
- helpers.InitLoggers()
-
- changed, err := firstSite.reBuild(events)
-
- if err != nil {
- return err
- }
-
- // Assign pages to sites per translation.
- h.setupTranslations()
-
- if changed.source {
- h.assembleGitInfo()
- for _, s := range h.Sites {
- if err := s.postProcess(); err != nil {
- return err
- }
- }
-
- }
-
- // TODO(bep) np consolidate the build lifecycle methods
- // See also the regular Build() method, and check vs. the changed.source
- if err := h.createMissingNodes(); err != nil {
- return err
- }
-
- for _, s := range h.Sites {
- s.refreshPageCaches()
- s.setupPrevNext()
- }
-
- if err := h.assignMissingTranslations(); err != nil {
- return err
- }
-
- if err := h.preRender(config, changed); err != nil {
- return err
- }
-
- if !config.SkipRender {
- for _, s := range h.Sites {
- if err := s.render(); err != nil {
- return err
- }
- if config.PrintStats {
- s.Stats()
- }
- }
-
- if err := h.render(); err != nil {
- return err
- }
- }
-
- if config.PrintStats {
- jww.FEEDBACK.Printf("total in %v ms\n", int(1000*time.Since(t0).Seconds()))
- }
-
- return nil
-
-}
-
// Analyze prints a build report to Stdout.
// Useful for debugging.
func (h *HugoSites) Analyze() error {
@@ -353,8 +169,7 @@
return s.ShowPlan(os.Stdout)
}
-// Render the cross-site artifacts.
-func (h *HugoSites) render() error {
+func (h *HugoSites) renderCrossSitesArtifacts() error {
if !h.multilingual.enabled() {
return nil
@@ -494,6 +309,7 @@
return nil
}
+// TODO(bep) np move
// Move the new* methods after cleanup in site.go
func (s *Site) newNodePage(typ NodeType) *Page {
return &Page{
--- /dev/null
+++ b/hugolib/hugo_sites_build.go
@@ -1,0 +1,197 @@
+// Copyright 2016-present The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package hugolib
+
+import (
+ "time"
+
+ "errors"
+
+ "github.com/fsnotify/fsnotify"
+ "github.com/spf13/hugo/helpers"
+ jww "github.com/spf13/jwalterweatherman"
+)
+
+// Build builds all sites. If filesystem events are provided,
+// this is considered to be a potential partial rebuild.
+func (h *HugoSites) Build(config BuildCfg, events ...fsnotify.Event) error {
+ t0 := time.Now()
+
+ // Need a pointer as this may be modified.
+ conf := &config
+
+ if conf.whatChanged == nil {
+ // Assume everything has changed
+ conf.whatChanged = &whatChanged{source: true, other: true}
+ }
+
+ if len(events) > 0 {
+ // Rebuild
+ if err := h.initRebuild(conf); err != nil {
+ return err
+ }
+ } else {
+ if err := h.init(conf); err != nil {
+ return err
+ }
+ }
+
+ if err := h.process(conf, events...); err != nil {
+ return err
+ }
+
+ if err := h.assemble(conf); err != nil {
+ return err
+ }
+
+ if err := h.render(conf); err != nil {
+ return err
+ }
+
+ if config.PrintStats {
+ jww.FEEDBACK.Printf("total in %v ms\n", int(1000*time.Since(t0).Seconds()))
+ }
+
+ return nil
+
+}
+
+// Build lifecycle methods below.
+// The order listed matches the order of execution.
+
+func (h *HugoSites) init(config *BuildCfg) error {
+
+ for _, s := range h.Sites {
+ if s.PageCollections == nil {
+ s.PageCollections = newPageCollections()
+ }
+ }
+
+ if config.ResetState {
+ h.reset()
+ }
+
+ if config.CreateSitesFromConfig {
+ if err := h.createSitesFromConfig(); err != nil {
+ return err
+ }
+ }
+
+ h.runMode.Watching = config.Watching
+
+ return nil
+}
+
+func (h *HugoSites) initRebuild(config *BuildCfg) error {
+ if config.CreateSitesFromConfig {
+ return errors.New("Rebuild does not support 'CreateSitesFromConfig'.")
+ }
+
+ if config.ResetState {
+ return errors.New("Rebuild does not support 'ResetState'.")
+ }
+
+ if !config.Watching {
+ return errors.New("Rebuild called when not in watch mode")
+ }
+
+ h.runMode.Watching = config.Watching
+
+ for _, s := range h.Sites {
+ s.resetBuildState()
+ }
+
+ helpers.InitLoggers()
+
+ return nil
+}
+
+func (h *HugoSites) process(config *BuildCfg, events ...fsnotify.Event) error {
+ // We should probably refactor the Site and pull up most of the logic from there to here,
+ // but that seems like a daunting task.
+ // So for now, if there are more than one site (language),
+ // we pre-process the first one, then configure all the sites based on that.
+ firstSite := h.Sites[0]
+
+ if len(events) > 0 {
+ // This is a rebuild
+ changed, err := firstSite.reProcess(events)
+ config.whatChanged = &changed
+ return err
+ }
+
+ return firstSite.process(*config)
+
+}
+
+func (h *HugoSites) assemble(config *BuildCfg) error {
+ // TODO(bep) np we could probably wait and do this in one go later
+ h.setupTranslations()
+
+ if len(h.Sites) > 1 {
+ // The first is initialized during process; initialize the rest
+ for _, site := range h.Sites[1:] {
+ site.initializeSiteInfo()
+ }
+ }
+
+ if config.whatChanged.source {
+ h.assembleGitInfo()
+
+ for _, s := range h.Sites {
+ if err := s.buildSiteMeta(); err != nil {
+ return err
+ }
+ }
+ }
+
+ if err := h.createMissingNodes(); err != nil {
+ return err
+ }
+
+ for _, s := range h.Sites {
+ s.refreshPageCaches()
+ s.setupPrevNext()
+ }
+
+ if err := h.assignMissingTranslations(); err != nil {
+ return err
+ }
+
+ if err := h.preRender(*config, whatChanged{source: true, other: true}); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (h *HugoSites) render(config *BuildCfg) error {
+ if !config.SkipRender {
+ for _, s := range h.Sites {
+ if err := s.render(); err != nil {
+ return err
+ }
+
+ if config.PrintStats {
+ s.Stats()
+ }
+ }
+
+ if err := h.renderCrossSitesArtifacts(); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
--- a/hugolib/hugo_sites_test.go
+++ b/hugolib/hugo_sites_test.go
@@ -543,7 +543,8 @@
if this.preFunc != nil {
this.preFunc(t)
}
- err = sites.Rebuild(cfg, this.events...)
+
+ err = sites.Build(cfg, this.events...)
if err != nil {
t.Fatalf("[%d] Failed to rebuild sites: %s", i, err)
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -482,7 +482,7 @@
// reBuild partially rebuilds a site given the filesystem events.
// It returns whetever the content source was changed.
-func (s *Site) reBuild(events []fsnotify.Event) (whatChanged, error) {
+func (s *Site) reProcess(events []fsnotify.Event) (whatChanged, error) {
jww.DEBUG.Printf("Rebuild for events %q", events)
@@ -763,7 +763,7 @@
return err
}
-func (s *Site) preProcess(config BuildCfg) (err error) {
+func (s *Site) process(config BuildCfg) (err error) {
s.timerStep("Go initialization")
if err = s.initialize(); err != nil {
return
@@ -785,16 +785,6 @@
}
-func (s *Site) postProcess() (err error) {
-
- if err = s.buildSiteMeta(); err != nil {
- return
- }
-
- s.timerStep("build taxonomies")
- return
-}
-
func (s *Site) setupPrevNext() {
for i, page := range s.Pages {
if i < len(s.Pages)-1 {
@@ -1333,6 +1323,7 @@
}
func (s *Site) buildSiteMeta() (err error) {
+ defer s.timerStep("build Site meta")
s.assembleMenus()