mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-21 20:46:30 -05:00
7ff0a8ee9f
This is preparation for #6041. For historic reasons, the code for bulding the section tree and the taxonomies were very much separate. This works, but makes it hard to extend, maintain, and possibly not so fast as it could be. This simplification also introduces 3 slightly breaking changes, which I suspect most people will be pleased about. See referenced issues: This commit also switches the radix tree dependency to a mutable implementation: github.com/armon/go-radix. Fixes #6154 Fixes #6153 Fixes #6152
329 lines
6.8 KiB
Go
329 lines
6.8 KiB
Go
// Copyright 2019 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 (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"runtime/trace"
|
|
|
|
"github.com/gohugoio/hugo/output"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/fsnotify/fsnotify"
|
|
"github.com/gohugoio/hugo/helpers"
|
|
)
|
|
|
|
// 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 {
|
|
|
|
if h.running {
|
|
// Make sure we don't trigger rebuilds in parallel.
|
|
h.runningMu.Lock()
|
|
defer h.runningMu.Unlock()
|
|
}
|
|
|
|
ctx, task := trace.NewTask(context.Background(), "Build")
|
|
defer task.End()
|
|
|
|
errCollector := h.StartErrorCollector()
|
|
errs := make(chan error)
|
|
|
|
go func(from, to chan error) {
|
|
var errors []error
|
|
i := 0
|
|
for e := range from {
|
|
i++
|
|
if i > 50 {
|
|
break
|
|
}
|
|
errors = append(errors, e)
|
|
}
|
|
to <- h.pickOneAndLogTheRest(errors)
|
|
|
|
close(to)
|
|
|
|
}(errCollector, errs)
|
|
|
|
if h.Metrics != nil {
|
|
h.Metrics.Reset()
|
|
}
|
|
|
|
// 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}
|
|
}
|
|
|
|
var prepareErr error
|
|
|
|
if !config.PartialReRender {
|
|
prepare := func() error {
|
|
init := func(conf *BuildCfg) error {
|
|
for _, s := range h.Sites {
|
|
s.Deps.BuildStartListeners.Notify()
|
|
}
|
|
|
|
if len(events) > 0 {
|
|
// Rebuild
|
|
if err := h.initRebuild(conf); err != nil {
|
|
return errors.Wrap(err, "initRebuild")
|
|
}
|
|
} else {
|
|
if err := h.initSites(conf); err != nil {
|
|
return errors.Wrap(err, "initSites")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
var err error
|
|
|
|
f := func() {
|
|
err = h.process(conf, init, events...)
|
|
}
|
|
trace.WithRegion(ctx, "process", f)
|
|
if err != nil {
|
|
return errors.Wrap(err, "process")
|
|
}
|
|
|
|
f = func() {
|
|
err = h.assemble(conf)
|
|
}
|
|
trace.WithRegion(ctx, "assemble", f)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
f := func() {
|
|
prepareErr = prepare()
|
|
}
|
|
trace.WithRegion(ctx, "prepare", f)
|
|
if prepareErr != nil {
|
|
h.SendError(prepareErr)
|
|
}
|
|
|
|
}
|
|
|
|
if prepareErr == nil {
|
|
var err error
|
|
f := func() {
|
|
err = h.render(conf)
|
|
}
|
|
trace.WithRegion(ctx, "render", f)
|
|
if err != nil {
|
|
h.SendError(err)
|
|
}
|
|
}
|
|
|
|
if h.Metrics != nil {
|
|
var b bytes.Buffer
|
|
h.Metrics.WriteMetrics(&b)
|
|
|
|
h.Log.FEEDBACK.Printf("\nTemplate Metrics:\n\n")
|
|
h.Log.FEEDBACK.Print(b.String())
|
|
h.Log.FEEDBACK.Println()
|
|
}
|
|
|
|
select {
|
|
// Make sure the channel always gets something.
|
|
case errCollector <- nil:
|
|
default:
|
|
}
|
|
close(errCollector)
|
|
|
|
err := <-errs
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := h.fatalErrorHandler.getErr(); err != nil {
|
|
return err
|
|
}
|
|
|
|
errorCount := h.Log.ErrorCounter.Count()
|
|
if errorCount > 0 {
|
|
return fmt.Errorf("logged %d error(s)", errorCount)
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
// Build lifecycle methods below.
|
|
// The order listed matches the order of execution.
|
|
|
|
func (h *HugoSites) initSites(config *BuildCfg) error {
|
|
h.reset(config)
|
|
|
|
if config.NewConfig != nil {
|
|
if err := h.createSitesFromConfig(config.NewConfig); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (h *HugoSites) initRebuild(config *BuildCfg) error {
|
|
if config.NewConfig != nil {
|
|
return errors.New("rebuild does not support 'NewConfig'")
|
|
}
|
|
|
|
if config.ResetState {
|
|
return errors.New("rebuild does not support 'ResetState'")
|
|
}
|
|
|
|
if !h.running {
|
|
return errors.New("rebuild called when not in watch mode")
|
|
}
|
|
|
|
for _, s := range h.Sites {
|
|
s.resetBuildState(config.whatChanged.source)
|
|
}
|
|
|
|
h.reset(config)
|
|
h.resetLogs()
|
|
helpers.InitLoggers()
|
|
|
|
return nil
|
|
}
|
|
|
|
func (h *HugoSites) process(config *BuildCfg, init func(config *BuildCfg) error, 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
|
|
return firstSite.processPartial(config, init, events)
|
|
}
|
|
|
|
return firstSite.process(*config)
|
|
|
|
}
|
|
|
|
func (h *HugoSites) assemble(config *BuildCfg) error {
|
|
|
|
if len(h.Sites) > 1 {
|
|
// The first is initialized during process; initialize the rest
|
|
for _, site := range h.Sites[1:] {
|
|
if err := site.initializeSiteInfo(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
if !config.whatChanged.source {
|
|
return nil
|
|
}
|
|
|
|
for _, s := range h.Sites {
|
|
if err := s.assemblePagesMap(s); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := s.pagesMap.assembleTaxonomies(s); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := s.createWorkAllPages(); err != nil {
|
|
return err
|
|
}
|
|
|
|
}
|
|
|
|
if err := h.createPageCollections(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
func (h *HugoSites) render(config *BuildCfg) error {
|
|
siteRenderContext := &siteRenderContext{cfg: config, multihost: h.multihost}
|
|
|
|
if !config.PartialReRender {
|
|
h.renderFormats = output.Formats{}
|
|
for _, s := range h.Sites {
|
|
s.initRenderFormats()
|
|
h.renderFormats = append(h.renderFormats, s.renderFormats...)
|
|
}
|
|
}
|
|
|
|
i := 0
|
|
for _, s := range h.Sites {
|
|
for siteOutIdx, renderFormat := range s.renderFormats {
|
|
siteRenderContext.outIdx = siteOutIdx
|
|
siteRenderContext.sitesOutIdx = i
|
|
i++
|
|
|
|
select {
|
|
case <-h.Done():
|
|
return nil
|
|
default:
|
|
// For the non-renderable pages, we use the content iself as
|
|
// template and we may have to re-parse and execute it for
|
|
// each output format.
|
|
h.TemplateHandler().RebuildClone()
|
|
|
|
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 !config.SkipRender {
|
|
if config.PartialReRender {
|
|
if err := s.renderPages(siteRenderContext); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
if err := s.render(siteRenderContext); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !config.SkipRender {
|
|
if err := h.renderCrossSitesArtifacts(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|