github.com/olliephillips/hugo@v0.42.2/hugolib/hugo_sites_build.go (about)

     1  // Copyright 2016-present The Hugo Authors. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package hugolib
    15  
    16  import (
    17  	"bytes"
    18  	"fmt"
    19  
    20  	"errors"
    21  
    22  	jww "github.com/spf13/jwalterweatherman"
    23  
    24  	"github.com/fsnotify/fsnotify"
    25  	"github.com/gohugoio/hugo/helpers"
    26  )
    27  
    28  // Build builds all sites. If filesystem events are provided,
    29  // this is considered to be a potential partial rebuild.
    30  func (h *HugoSites) Build(config BuildCfg, events ...fsnotify.Event) error {
    31  	if h.Metrics != nil {
    32  		h.Metrics.Reset()
    33  	}
    34  
    35  	//t0 := time.Now()
    36  
    37  	// Need a pointer as this may be modified.
    38  	conf := &config
    39  
    40  	if conf.whatChanged == nil {
    41  		// Assume everything has changed
    42  		conf.whatChanged = &whatChanged{source: true, other: true}
    43  	}
    44  
    45  	if len(events) > 0 {
    46  		// Rebuild
    47  		if err := h.initRebuild(conf); err != nil {
    48  			return err
    49  		}
    50  	} else {
    51  		if err := h.init(conf); err != nil {
    52  			return err
    53  		}
    54  	}
    55  
    56  	if err := h.process(conf, events...); err != nil {
    57  		return err
    58  	}
    59  
    60  	if err := h.assemble(conf); err != nil {
    61  		return err
    62  	}
    63  
    64  	if err := h.render(conf); err != nil {
    65  		return err
    66  	}
    67  
    68  	if h.Metrics != nil {
    69  		var b bytes.Buffer
    70  		h.Metrics.WriteMetrics(&b)
    71  
    72  		h.Log.FEEDBACK.Printf("\nTemplate Metrics:\n\n")
    73  		h.Log.FEEDBACK.Print(b.String())
    74  		h.Log.FEEDBACK.Println()
    75  	}
    76  
    77  	errorCount := h.Log.LogCountForLevel(jww.LevelError)
    78  	if errorCount > 0 {
    79  		return fmt.Errorf("logged %d error(s)", errorCount)
    80  	}
    81  
    82  	return nil
    83  
    84  }
    85  
    86  // Build lifecycle methods below.
    87  // The order listed matches the order of execution.
    88  
    89  func (h *HugoSites) init(config *BuildCfg) error {
    90  
    91  	for _, s := range h.Sites {
    92  		if s.PageCollections == nil {
    93  			s.PageCollections = newPageCollections()
    94  		}
    95  	}
    96  
    97  	if config.ResetState {
    98  		h.reset()
    99  	}
   100  
   101  	if config.CreateSitesFromConfig {
   102  		if err := h.createSitesFromConfig(); err != nil {
   103  			return err
   104  		}
   105  	}
   106  
   107  	return nil
   108  }
   109  
   110  func (h *HugoSites) initRebuild(config *BuildCfg) error {
   111  	if config.CreateSitesFromConfig {
   112  		return errors.New("Rebuild does not support 'CreateSitesFromConfig'.")
   113  	}
   114  
   115  	if config.ResetState {
   116  		return errors.New("Rebuild does not support 'ResetState'.")
   117  	}
   118  
   119  	if !h.running {
   120  		return errors.New("Rebuild called when not in watch mode")
   121  	}
   122  
   123  	if config.whatChanged.source {
   124  		// This is for the non-renderable content pages (rarely used, I guess).
   125  		// We could maybe detect if this is really needed, but it should be
   126  		// pretty fast.
   127  		h.TemplateHandler().RebuildClone()
   128  	}
   129  
   130  	for _, s := range h.Sites {
   131  		s.resetBuildState()
   132  	}
   133  
   134  	h.resetLogs()
   135  	helpers.InitLoggers()
   136  
   137  	return nil
   138  }
   139  
   140  func (h *HugoSites) process(config *BuildCfg, events ...fsnotify.Event) error {
   141  	// We should probably refactor the Site and pull up most of the logic from there to here,
   142  	// but that seems like a daunting task.
   143  	// So for now, if there are more than one site (language),
   144  	// we pre-process the first one, then configure all the sites based on that.
   145  
   146  	firstSite := h.Sites[0]
   147  
   148  	if len(events) > 0 {
   149  		// This is a rebuild
   150  		changed, err := firstSite.processPartial(events)
   151  		config.whatChanged = &changed
   152  		return err
   153  	}
   154  
   155  	return firstSite.process(*config)
   156  
   157  }
   158  
   159  func (h *HugoSites) assemble(config *BuildCfg) error {
   160  	if config.whatChanged.source {
   161  		for _, s := range h.Sites {
   162  			s.createTaxonomiesEntries()
   163  		}
   164  	}
   165  
   166  	// TODO(bep) we could probably wait and do this in one go later
   167  	h.setupTranslations()
   168  
   169  	if len(h.Sites) > 1 {
   170  		// The first is initialized during process; initialize the rest
   171  		for _, site := range h.Sites[1:] {
   172  			if err := site.initializeSiteInfo(); err != nil {
   173  				return err
   174  			}
   175  		}
   176  	}
   177  
   178  	if config.whatChanged.source {
   179  		for _, s := range h.Sites {
   180  			if err := s.buildSiteMeta(); err != nil {
   181  				return err
   182  			}
   183  		}
   184  	}
   185  
   186  	if err := h.createMissingPages(); err != nil {
   187  		return err
   188  	}
   189  
   190  	for _, s := range h.Sites {
   191  		for _, pages := range []Pages{s.Pages, s.headlessPages} {
   192  			for _, p := range pages {
   193  				// May have been set in front matter
   194  				if len(p.outputFormats) == 0 {
   195  					p.outputFormats = s.outputFormats[p.Kind]
   196  				}
   197  
   198  				if p.headless {
   199  					// headless = 1 output format only
   200  					p.outputFormats = p.outputFormats[:1]
   201  				}
   202  				for _, r := range p.Resources.ByType(pageResourceType) {
   203  					r.(*Page).outputFormats = p.outputFormats
   204  				}
   205  
   206  				if err := p.initPaths(); err != nil {
   207  					return err
   208  				}
   209  
   210  			}
   211  		}
   212  		s.assembleMenus()
   213  		s.refreshPageCaches()
   214  		s.setupSitePages()
   215  	}
   216  
   217  	if err := h.assignMissingTranslations(); err != nil {
   218  		return err
   219  	}
   220  
   221  	return nil
   222  
   223  }
   224  
   225  func (h *HugoSites) render(config *BuildCfg) error {
   226  	for _, s := range h.Sites {
   227  		s.initRenderFormats()
   228  	}
   229  
   230  	for _, s := range h.Sites {
   231  		for i, rf := range s.renderFormats {
   232  			for _, s2 := range h.Sites {
   233  				// We render site by site, but since the content is lazily rendered
   234  				// and a site can "borrow" content from other sites, every site
   235  				// needs this set.
   236  				s2.rc = &siteRenderingContext{Format: rf}
   237  
   238  				isRenderingSite := s == s2
   239  
   240  				if err := s2.preparePagesForRender(isRenderingSite && i == 0); err != nil {
   241  					return err
   242  				}
   243  
   244  			}
   245  
   246  			if !config.SkipRender {
   247  				if err := s.render(config, i); err != nil {
   248  					return err
   249  				}
   250  			}
   251  		}
   252  	}
   253  
   254  	if !config.SkipRender {
   255  		if err := h.renderCrossSitesArtifacts(); err != nil {
   256  			return err
   257  		}
   258  	}
   259  
   260  	return nil
   261  }