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