diff --git a/commands/benchmark.go b/commands/benchmark.go index 53e11c3f6..56a50578b 100644 --- a/commands/benchmark.go +++ b/commands/benchmark.go @@ -57,8 +57,8 @@ func benchmark(cmd *cobra.Command, args []string) error { return err } for i := 0; i < benchmarkTimes; i++ { - Sites = nil - _ = buildSite() + _ = buildSites() + Hugo.Reset() } pprof.WriteHeapProfile(f) f.Close() @@ -76,8 +76,8 @@ func benchmark(cmd *cobra.Command, args []string) error { pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() for i := 0; i < benchmarkTimes; i++ { - Sites = nil - _ = buildSite() + _ = buildSites() + Hugo.Reset() } } diff --git a/commands/hugo.go b/commands/hugo.go index 4fd4dcb9d..6168d0a83 100644 --- a/commands/hugo.go +++ b/commands/hugo.go @@ -46,10 +46,20 @@ import ( "github.com/spf13/viper" ) -// Sites represents the Hugo sites to build. This variable is exported as it +type HugoSites []*hugolib.Site + +// Reset resets the sites, making it ready for a full rebuild. +// TODO(bep) multilingo +func (h HugoSites) Reset() { + for i, s := range h { + h[i] = s.Reset() + } +} + +// Hugo represents the Hugo sites to build. This variable is exported as it // is used by at least one external library (the Hugo caddy plugin). We should // provide a cleaner external API, but until then, this is it. -var Sites map[string]*hugolib.Site +var Hugo HugoSites // Reset resets Hugo ready for a new full build. This is mainly only useful // for benchmark testing etc. via the CLI commands. @@ -493,7 +503,15 @@ func InitializeConfig(subCmdVs ...*cobra.Command) error { helpers.HugoReleaseVersion(), minVersion) } - return readMultilingualConfiguration() + h, err := readMultilingualConfiguration() + + if err != nil { + return err + } + //TODO(bep) refactor ... + Hugo = h + + return nil } @@ -510,8 +528,8 @@ func watchConfig() { viper.OnConfigChange(func(e fsnotify.Event) { fmt.Println("Config file changed:", e.Name) // Force a full rebuild - Sites = nil - utils.CheckErr(buildSite(true)) + Hugo.Reset() + utils.CheckErr(buildSites(true)) if !viper.GetBool("DisableLiveReload") { // Will block forever trying to write to a channel that nobody is reading if livereload isn't initialized livereload.ForceRefresh() @@ -537,7 +555,7 @@ func build(watches ...bool) error { if len(watches) > 0 && watches[0] { watch = true } - if err := buildSite(buildWatch || watch); err != nil { + if err := buildSites(buildWatch || watch); err != nil { return fmt.Errorf("Error building site: %s", err) } @@ -704,32 +722,21 @@ func getDirList() []string { return a } -func buildSite(watching ...bool) (err error) { +func buildSites(watching ...bool) (err error) { fmt.Println("Started building site") t0 := time.Now() - if Sites == nil { - Sites = make(map[string]*hugolib.Site) - } - - for _, lang := range langConfigsList { + for _, site := range Hugo { t1 := time.Now() - mainSite, present := Sites[lang.Lang] - if !present { - mainSite = new(hugolib.Site) - Sites[lang.Lang] = mainSite - mainSite.SetMultilingualConfig(lang, langConfigsList) - } - if len(watching) > 0 && watching[0] { - mainSite.RunMode.Watching = true + site.RunMode.Watching = true } - if err := mainSite.Build(); err != nil { + if err := site.Build(); err != nil { return err } - mainSite.Stats(lang.Lang, t1) + site.Stats(t1) } jww.FEEDBACK.Printf("total in %v ms\n", int(1000*time.Since(t0).Seconds())) @@ -737,18 +744,17 @@ func buildSite(watching ...bool) (err error) { return nil } -func rebuildSite(events []fsnotify.Event) error { +func rebuildSites(events []fsnotify.Event) error { t0 := time.Now() - for _, lang := range langConfigsList { + for _, site := range Hugo { t1 := time.Now() - site := Sites[lang.Lang] if err := site.ReBuild(events); err != nil { return err } - site.Stats(lang.Lang, t1) + site.Stats(t1) } jww.FEEDBACK.Printf("total in %v ms\n", int(1000*time.Since(t0).Seconds())) @@ -969,7 +975,7 @@ func NewWatcher(port int) error { const layout = "2006-01-02 15:04 -0700" fmt.Println(time.Now().Format(layout)) - rebuildSite(dynamicEvents) + rebuildSites(dynamicEvents) if !buildWatch && !viper.GetBool("DisableLiveReload") { // Will block forever trying to write to a channel that nobody is reading if livereload isn't initialized diff --git a/commands/multilingual.go b/commands/multilingual.go index 3f813474d..9afb562ba 100644 --- a/commands/multilingual.go +++ b/commands/multilingual.go @@ -11,24 +11,30 @@ import ( "github.com/spf13/viper" ) -var langConfigsList hugolib.Languages - -func readMultilingualConfiguration() error { +func readMultilingualConfiguration() (HugoSites, error) { + h := make(HugoSites, 0) multilingual := viper.GetStringMap("Multilingual") if len(multilingual) == 0 { // TODO(bep) multilingo langConfigsList = append(langConfigsList, hugolib.NewLanguage("en")) - return nil + h = append(h, hugolib.NewSite(hugolib.NewLanguage("en"))) + return h, nil } var err error - langConfigsList, err = toSortedLanguages(multilingual) + langConfigsList, err := toSortedLanguages(multilingual) if err != nil { - return fmt.Errorf("Failed to parse multilingual config: %s", err) + return nil, fmt.Errorf("Failed to parse multilingual config: %s", err) } - return nil + for _, lang := range langConfigsList { + s := hugolib.NewSite(lang) + s.SetMultilingualConfig(lang, langConfigsList) + h = append(h, s) + } + + return h, nil } func toSortedLanguages(l map[string]interface{}) (hugolib.Languages, error) { diff --git a/hugolib/datafiles_test.go b/hugolib/datafiles_test.go index d57773579..36325dc61 100644 --- a/hugolib/datafiles_test.go +++ b/hugolib/datafiles_test.go @@ -87,7 +87,7 @@ func TestDataDirUnknownFormat(t *testing.T) { sources := []source.ByteSource{ {Name: filepath.FromSlash("test.roml"), Content: []byte("boo")}, } - s := &Site{} + s := newSiteDefaultLang() err := s.loadData([]source.Input{&source.InMemorySource{ByteSource: sources}}) if err != nil { t.Fatalf("Should not return an error") @@ -95,7 +95,7 @@ func TestDataDirUnknownFormat(t *testing.T) { } func doTestDataDir(t *testing.T, expected interface{}, sources []source.Input) { - s := &Site{} + s := newSiteDefaultLang() err := s.loadData(sources) if err != nil { t.Fatalf("Error loading data: %s", err) diff --git a/hugolib/handler_test.go b/hugolib/handler_test.go index 1b0a82418..a84d528cb 100644 --- a/hugolib/handler_test.go +++ b/hugolib/handler_test.go @@ -47,6 +47,7 @@ func TestDefaultHandler(t *testing.T) { s := &Site{ Source: &source.InMemorySource{ByteSource: sources}, targets: targetList{page: &target.PagePub{UglyURLs: true}}, + Lang: NewLanguage("en"), } s.initializeSiteInfo() diff --git a/hugolib/menu_test.go b/hugolib/menu_test.go index 602775a4b..03219a48a 100644 --- a/hugolib/menu_test.go +++ b/hugolib/menu_test.go @@ -683,6 +683,7 @@ func createTestSite(pageSources []source.ByteSource) *Site { s := &Site{ Source: &source.InMemorySource{ByteSource: pageSources}, + Lang: newDefaultLanguage(), } return s } diff --git a/hugolib/multilingual.go b/hugolib/multilingual.go index becdd5ba1..c75f504ef 100644 --- a/hugolib/multilingual.go +++ b/hugolib/multilingual.go @@ -22,6 +22,11 @@ func NewLanguage(lang string) *Language { return &Language{Lang: lang, params: make(map[string]interface{})} } +// TODO(bep) multilingo +func newDefaultLanguage() *Language { + return NewLanguage("en") +} + type Languages []*Language func NewLanguages(l ...*Language) Languages { @@ -93,10 +98,9 @@ func (l *Language) Get(key string) interface{} { return viper.Get(key) } +// TODO(bep) multilingo move this to a constructor. func (s *Site) SetMultilingualConfig(currentLang *Language, languages Languages) { - // TODO(bep) multilingo evaluate - viper.Set("CurrentLanguage", currentLang) ml := &Multilingual{ Languages: languages, } @@ -108,14 +112,11 @@ func (s *Site) multilingualEnabled() bool { return s.Multilingual != nil && s.Multilingual.enabled() } -func currentLanguageString() string { - return currentLanguage().Lang +// TODO(bep) multilingo remove these +func (s *Site) currentLanguageString() string { + return s.currentLanguage().Lang } -func currentLanguage() *Language { - l := viper.Get("CurrentLanguage") - if l == nil { - panic("CurrentLanguage not set") - } - return l.(*Language) +func (s *Site) currentLanguage() *Language { + return s.Lang } diff --git a/hugolib/pagination_test.go b/hugolib/pagination_test.go index 9b311cb34..080e6bee9 100644 --- a/hugolib/pagination_test.go +++ b/hugolib/pagination_test.go @@ -226,7 +226,7 @@ func doTestPaginator(t *testing.T, useViper bool) { viper.Set("paginate", -1) } pages := createTestPages(12) - s := &Site{} + s := newSiteDefaultLang() n1 := s.newHomeNode() n2 := s.newHomeNode() n1.Data["Pages"] = pages @@ -264,7 +264,7 @@ func TestPaginatorWithNegativePaginate(t *testing.T) { defer viper.Reset() viper.Set("paginate", -1) - s := &Site{} + s := newSiteDefaultLang() _, err := s.newHomeNode().Paginator() assert.NotNil(t, err) } @@ -287,7 +287,7 @@ func doTestPaginate(t *testing.T, useViper bool) { } pages := createTestPages(6) - s := &Site{} + s := newSiteDefaultLang() n1 := s.newHomeNode() n2 := s.newHomeNode() @@ -320,7 +320,7 @@ func doTestPaginate(t *testing.T, useViper bool) { } func TestInvalidOptions(t *testing.T) { - s := &Site{} + s := newSiteDefaultLang() n1 := s.newHomeNode() _, err := n1.Paginate(createTestPages(1), 1, 2) assert.NotNil(t, err) @@ -335,7 +335,7 @@ func TestPaginateWithNegativePaginate(t *testing.T) { defer viper.Reset() viper.Set("paginate", -1) - s := &Site{} + s := newSiteDefaultLang() _, err := s.newHomeNode().Paginate(createTestPages(2)) assert.NotNil(t, err) } @@ -358,7 +358,7 @@ func TestPaginatorFollowedByPaginateShouldFail(t *testing.T) { defer viper.Reset() viper.Set("paginate", 10) - s := &Site{} + s := newSiteDefaultLang() n1 := s.newHomeNode() n2 := s.newHomeNode() @@ -377,7 +377,7 @@ func TestPaginateFollowedByDifferentPaginateShouldFail(t *testing.T) { defer viper.Reset() viper.Set("paginate", 10) - s := &Site{} + s := newSiteDefaultLang() n1 := s.newHomeNode() n2 := s.newHomeNode() diff --git a/hugolib/robotstxt_test.go b/hugolib/robotstxt_test.go index 461d04e5d..8e4b13db6 100644 --- a/hugolib/robotstxt_test.go +++ b/hugolib/robotstxt_test.go @@ -40,6 +40,7 @@ func TestRobotsTXTOutput(t *testing.T) { s := &Site{ Source: &source.InMemorySource{ByteSource: weightedSources}, + Lang: newDefaultLanguage(), } s.initializeSiteInfo() diff --git a/hugolib/rss_test.go b/hugolib/rss_test.go index ec6ee87bd..72ec25fca 100644 --- a/hugolib/rss_test.go +++ b/hugolib/rss_test.go @@ -55,6 +55,7 @@ func TestRSSOutput(t *testing.T) { hugofs.InitMemFs() s := &Site{ Source: &source.InMemorySource{ByteSource: weightedSources}, + Lang: newDefaultLanguage(), } s.initializeSiteInfo() s.prepTemplates("rss.xml", rssTemplate) diff --git a/hugolib/shortcode_test.go b/hugolib/shortcode_test.go index 85cfdcbc2..d0832d2ea 100644 --- a/hugolib/shortcode_test.go +++ b/hugolib/shortcode_test.go @@ -499,6 +499,7 @@ e`, s := &Site{ Source: &source.InMemorySource{ByteSource: sources}, targets: targetList{page: &target.PagePub{UglyURLs: false}}, + Lang: newDefaultLanguage(), } s.initializeSiteInfo() diff --git a/hugolib/site.go b/hugolib/site.go index b2a9161f4..bbaa1e019 100644 --- a/hugolib/site.go +++ b/hugolib/site.go @@ -92,6 +92,21 @@ type Site struct { futureCount int expiredCount int Data map[string]interface{} + Lang *Language +} + +// TODO(bep) multilingo +// Reset returns a new Site prepared for rebuild. +func (s *Site) Reset() *Site { + return &Site{Lang: s.Lang, Multilingual: s.Multilingual} +} + +func NewSite(lang *Language) *Site { + return &Site{Lang: lang} +} + +func newSiteDefaultLang() *Site { + return NewSite(newDefaultLanguage()) } type targetList struct { @@ -705,7 +720,7 @@ func (s *Site) Process() (err error) { i18nSources = []source.Input{&source.Filesystem{Base: themeI18nDir}, i18nSources[0]} } - if err = loadI18n(i18nSources, currentLanguageString()); err != nil { + if err = loadI18n(i18nSources, s.currentLanguageString()); err != nil { return } s.timerStep("load i18n") @@ -742,7 +757,7 @@ func (s *Site) setupTranslations() { return } - currentLang := currentLanguageString() + currentLang := s.currentLanguageString() allTranslations := pagesToTranslationsMap(s.Multilingual, s.AllPages) assignTranslationsToPages(allTranslations, s.AllPages) @@ -819,20 +834,10 @@ func (s *Site) initialize() (err error) { func (s *Site) initializeSiteInfo() { var ( - lang *Language + lang *Language = s.Lang languages Languages ) - cl := viper.Get("CurrentLanguage") - if cl == nil { - // Set default to english - // TODO(bep) multilingo this looks clumsy - lang = NewLanguage("en") - viper.Set("CurrentLanguage", lang) - } else { - lang = cl.(*Language) - } - if s.Multilingual != nil { languages = s.Multilingual.Languages } @@ -1610,7 +1615,7 @@ func (s *Site) newTaxonomyNode(t taxRenderInfo) (*Node, string) { func (s *Site) addMultilingualPrefix(basePath string) string { hadPrefix := strings.HasPrefix(basePath, "/") if s.multilingualEnabled() { - basePath = path.Join(currentLanguageString(), basePath) + basePath = path.Join(s.currentLanguageString(), basePath) if hadPrefix { basePath = "/" + basePath } @@ -1961,7 +1966,7 @@ func (s *Site) renderRobotsTXT() error { // Stats prints Hugo builds stats to the console. // This is what you see after a successful hugo build. -func (s *Site) Stats(lang string, t0 time.Time) { +func (s *Site) Stats(t0 time.Time) { jww.FEEDBACK.Println(s.draftStats()) jww.FEEDBACK.Println(s.futureStats()) jww.FEEDBACK.Println(s.expiredStats()) @@ -1974,9 +1979,9 @@ func (s *Site) Stats(lang string, t0 time.Time) { jww.FEEDBACK.Printf("%d %s created\n", len(s.Taxonomies[pl]), pl) } - if lang != "" { - jww.FEEDBACK.Printf("rendered lang %q in %v ms\n", lang, int(1000*time.Since(t0).Seconds())) - } + // TODO(bep) will always have lang. Not sure this should always be printed. + jww.FEEDBACK.Printf("rendered lang %q in %v ms\n", s.Lang.Lang, int(1000*time.Since(t0).Seconds())) + } func (s *Site) setURLs(n *Node, in string) { diff --git a/hugolib/site_test.go b/hugolib/site_test.go index 78004aac4..ecf3d834d 100644 --- a/hugolib/site_test.go +++ b/hugolib/site_test.go @@ -215,7 +215,7 @@ func TestRenderThingOrDefault(t *testing.T) { for i, test := range tests { - s := &Site{} + s := newSiteDefaultLang() p, err := NewPageFrom(strings.NewReader(pageSimpleTitle), "content/a/file.md") if err != nil { @@ -262,6 +262,7 @@ func TestDraftAndFutureRender(t *testing.T) { siteSetup := func() *Site { s := &Site{ Source: &source.InMemorySource{ByteSource: sources}, + Lang: newDefaultLanguage(), } s.initializeSiteInfo() @@ -320,6 +321,7 @@ func TestFutureExpirationRender(t *testing.T) { siteSetup := func() *Site { s := &Site{ Source: &source.InMemorySource{ByteSource: sources}, + Lang: newDefaultLanguage(), } s.initializeSiteInfo() @@ -413,6 +415,7 @@ THE END.`, refShortcode)), s := &Site{ Source: &source.InMemorySource{ByteSource: sources}, targets: targetList{page: &target.PagePub{UglyURLs: uglyURLs}}, + Lang: newDefaultLanguage(), } s.initializeSiteInfo() @@ -479,6 +482,7 @@ func doTestShouldAlwaysHaveUglyURLs(t *testing.T, uglyURLs bool) { s := &Site{ Source: &source.InMemorySource{ByteSource: sources}, targets: targetList{page: &target.PagePub{UglyURLs: uglyURLs}}, + Lang: newDefaultLanguage(), } s.initializeSiteInfo() @@ -572,6 +576,7 @@ func doTestSectionNaming(t *testing.T, canonify, uglify, pluralize bool) { s := &Site{ Source: &source.InMemorySource{ByteSource: sources}, targets: targetList{page: &target.PagePub{UglyURLs: uglify}}, + Lang: newDefaultLanguage(), } s.initializeSiteInfo() @@ -636,6 +641,7 @@ func TestSkipRender(t *testing.T) { s := &Site{ Source: &source.InMemorySource{ByteSource: sources}, targets: targetList{page: &target.PagePub{UglyURLs: true}}, + Lang: newDefaultLanguage(), } s.initializeSiteInfo() @@ -693,6 +699,7 @@ func TestAbsURLify(t *testing.T) { s := &Site{ Source: &source.InMemorySource{ByteSource: sources}, targets: targetList{page: &target.PagePub{UglyURLs: true}}, + Lang: newDefaultLanguage(), } t.Logf("Rendering with BaseURL %q and CanonifyURLs set %v", viper.GetString("baseURL"), canonify) s.initializeSiteInfo() @@ -788,6 +795,7 @@ func TestOrderedPages(t *testing.T) { viper.Set("baseurl", "http://auth/bub") s := &Site{ Source: &source.InMemorySource{ByteSource: weightedSources}, + Lang: newDefaultLanguage(), } s.initializeSiteInfo() @@ -1040,6 +1048,7 @@ func TestWeightedTaxonomies(t *testing.T) { viper.Set("taxonomies", taxonomies) s := &Site{ Source: &source.InMemorySource{ByteSource: sources}, + Lang: newDefaultLanguage(), } s.initializeSiteInfo() @@ -1107,6 +1116,7 @@ func setupLinkingMockSite(t *testing.T) *Site { site := &Site{ Source: &source.InMemorySource{ByteSource: sources}, + Lang: newDefaultLanguage(), } site.initializeSiteInfo() @@ -1402,13 +1412,13 @@ NOTE: should use the "permalinks" configuration with :filename // Multilingual settings viper.Set("Multilingual", true) en := NewLanguage("en") - viper.Set("CurrentLanguage", en) viper.Set("DefaultContentLanguage", "fr") viper.Set("paginate", "2") languages := NewLanguages(en, NewLanguage("fr")) s := &Site{ Source: &source.InMemorySource{ByteSource: sources}, + Lang: en, Multilingual: &Multilingual{ Languages: languages, }, diff --git a/hugolib/site_url_test.go b/hugolib/site_url_test.go index 6e011a500..fc0203d4d 100644 --- a/hugolib/site_url_test.go +++ b/hugolib/site_url_test.go @@ -73,7 +73,7 @@ func TestShouldNotAddTrailingSlashToBaseURL(t *testing.T) { {"http://base.com", "http://base.com"}} { viper.Set("BaseURL", this.in) - s := &Site{} + s := newSiteDefaultLang() s.initializeSiteInfo() if s.Info.BaseURL != template.URL(this.expected) { @@ -93,6 +93,7 @@ func TestPageCount(t *testing.T) { viper.Set("paginate", 10) s := &Site{ Source: &source.InMemorySource{ByteSource: urlFakeSource}, + Lang: newDefaultLanguage(), } s.initializeSiteInfo() s.prepTemplates("indexes/blue.html", indexTemplate) diff --git a/hugolib/siteinfo_test.go b/hugolib/siteinfo_test.go index 8110dd932..362be2a46 100644 --- a/hugolib/siteinfo_test.go +++ b/hugolib/siteinfo_test.go @@ -27,7 +27,7 @@ func TestSiteInfoParams(t *testing.T) { defer viper.Reset() viper.Set("Params", map[string]interface{}{"MyGlobalParam": "FOOBAR_PARAM"}) - s := &Site{} + s := newSiteDefaultLang() s.initialize() if s.Info.Params["MyGlobalParam"] != "FOOBAR_PARAM" { @@ -53,7 +53,7 @@ func TestSiteInfoPermalinks(t *testing.T) { defer viper.Reset() viper.Set("Permalinks", map[string]interface{}{"section": "/:title"}) - s := &Site{} + s := newSiteDefaultLang() s.initialize() permalink := s.Info.Permalinks["section"] diff --git a/hugolib/sitemap_test.go b/hugolib/sitemap_test.go index b9270ba58..c508fbc31 100644 --- a/hugolib/sitemap_test.go +++ b/hugolib/sitemap_test.go @@ -17,11 +17,12 @@ import ( "bytes" "testing" + "reflect" + "github.com/spf13/hugo/helpers" "github.com/spf13/hugo/hugofs" "github.com/spf13/hugo/source" "github.com/spf13/viper" - "reflect" ) const SITEMAP_TEMPLATE = ` @@ -45,6 +46,7 @@ func TestSitemapOutput(t *testing.T) { s := &Site{ Source: &source.InMemorySource{ByteSource: weightedSources}, + Lang: newDefaultLanguage(), } s.initializeSiteInfo()