diff options
Diffstat (limited to 'hugolib/page_bundler_capture.go')
-rw-r--r-- | hugolib/page_bundler_capture.go | 185 |
1 files changed, 115 insertions, 70 deletions
diff --git a/hugolib/page_bundler_capture.go b/hugolib/page_bundler_capture.go index 4d8f39fb7..255a8efda 100644 --- a/hugolib/page_bundler_capture.go +++ b/hugolib/page_bundler_capture.go @@ -17,17 +17,21 @@ import ( "errors" "fmt" "os" + "path" "path/filepath" "runtime" "strings" "sync" + "github.com/spf13/afero" + + "github.com/gohugoio/hugo/hugofs" + "github.com/gohugoio/hugo/helpers" "golang.org/x/sync/errgroup" "github.com/gohugoio/hugo/source" - "github.com/spf13/afero" jww "github.com/spf13/jwalterweatherman" ) @@ -44,8 +48,6 @@ type capturer struct { fs afero.Fs logger *jww.Notepad - baseDir string - // Filenames limits the content to process to a list of filenames/directories. // This is used for partial building in server mode. filenames []string @@ -62,7 +64,7 @@ func newCapturer( sourceSpec *source.SourceSpec, handler captureResultHandler, contentChanges *contentChangeMap, - baseDir string, filenames ...string) *capturer { + filenames ...string) *capturer { numWorkers := 4 if n := runtime.NumCPU(); n > numWorkers { @@ -73,10 +75,11 @@ func newCapturer( sem: make(chan bool, numWorkers), handler: handler, sourceSpec: sourceSpec, + fs: sourceSpec.Fs, logger: logger, contentChanges: contentChanges, - fs: sourceSpec.Fs.Source, baseDir: baseDir, seen: make(map[string]bool), - filenames: filenames} + seen: make(map[string]bool), + filenames: filenames} return c } @@ -85,7 +88,7 @@ func newCapturer( // these channels. type captureResultHandler interface { handleSingles(fis ...*fileInfo) - handleCopyFiles(filenames ...string) + handleCopyFiles(fis ...pathLangFile) captureBundlesHandler } @@ -110,10 +113,10 @@ func (c *captureResultHandlerChain) handleBundles(b *bundleDirs) { } } -func (c *captureResultHandlerChain) handleCopyFiles(filenames ...string) { +func (c *captureResultHandlerChain) handleCopyFiles(files ...pathLangFile) { for _, h := range c.handlers { if hh, ok := h.(captureResultHandler); ok { - hh.handleCopyFiles(filenames...) + hh.handleCopyFiles(files...) } } } @@ -139,7 +142,7 @@ func (c *capturer) capturePartial(filenames ...string) error { return err } default: - fi, _, err := c.getRealFileInfo(resolvedFilename) + fi, err := c.resolveRealPath(resolvedFilename) if os.IsNotExist(err) { // File has been deleted. continue @@ -147,9 +150,9 @@ func (c *capturer) capturePartial(filenames ...string) error { // Just in case the owning dir is a new symlink -- this will // create the proper mapping for it. - c.getRealFileInfo(dir) + c.resolveRealPath(dir) - f, active := c.newFileInfo(resolvedFilename, fi, tp) + f, active := c.newFileInfo(fi, tp) if active { c.copyOrHandleSingle(f) } @@ -164,7 +167,7 @@ func (c *capturer) capture() error { return c.capturePartial(c.filenames...) } - err := c.handleDir(c.baseDir) + err := c.handleDir(helpers.FilePathSeparator) if err != nil { return err } @@ -196,6 +199,7 @@ func (c *capturer) handleNestedDir(dirname string) error { func (c *capturer) handleBranchDir(dirname string) error { files, err := c.readDir(dirname) if err != nil { + return err } @@ -205,7 +209,7 @@ func (c *capturer) handleBranchDir(dirname string) error { for _, fi := range files { if !fi.IsDir() { - tp, _ := classifyBundledFile(fi.Name()) + tp, _ := classifyBundledFile(fi.RealName()) if dirType == bundleNot { dirType = tp } @@ -222,25 +226,35 @@ func (c *capturer) handleBranchDir(dirname string) error { dirs := newBundleDirs(bundleBranch, c) - for _, fi := range files { + var secondPass []*fileInfo + // Handle potential bundle headers first. + for _, fi := range files { if fi.IsDir() { continue } - tp, isContent := classifyBundledFile(fi.Name()) + tp, isContent := classifyBundledFile(fi.RealName()) + + f, active := c.newFileInfo(fi, tp) - f, active := c.newFileInfo(fi.filename, fi.FileInfo, tp) if !active { continue } - if f.isOwner() { - dirs.addBundleHeader(f) - } else if !isContent { - // This is a partial update -- we only care about the files that - // is in this bundle. - dirs.addBundleFiles(f) + + if !f.isOwner() { + if !isContent { + // This is a partial update -- we only care about the files that + // is in this bundle. + secondPass = append(secondPass, f) + } + continue } + dirs.addBundleHeader(f) + } + + for _, f := range secondPass { + dirs.addBundleFiles(f) } c.handler.handleBundles(dirs) @@ -250,6 +264,7 @@ func (c *capturer) handleBranchDir(dirname string) error { } func (c *capturer) handleDir(dirname string) error { + files, err := c.readDir(dirname) if err != nil { return err @@ -290,7 +305,8 @@ func (c *capturer) handleDir(dirname string) error { for i, fi := range files { if !fi.IsDir() { - tp, isContent := classifyBundledFile(fi.Name()) + tp, isContent := classifyBundledFile(fi.RealName()) + fileBundleTypes[i] = tp if !isBranch { isBranch = tp == bundleBranch @@ -317,6 +333,7 @@ func (c *capturer) handleDir(dirname string) error { var fileInfos = make([]*fileInfo, 0, len(files)) for i, fi := range files { + currentType := bundleNot if !fi.IsDir() { @@ -329,7 +346,9 @@ func (c *capturer) handleDir(dirname string) error { if bundleType == bundleNot && currentType != bundleNot { bundleType = currentType } - f, active := c.newFileInfo(fi.filename, fi.FileInfo, currentType) + + f, active := c.newFileInfo(fi, currentType) + if !active { continue } @@ -343,8 +362,7 @@ func (c *capturer) handleDir(dirname string) error { for _, fi := range fileInfos { if fi.FileInfo().IsDir() { // Handle potential nested bundles. - filename := fi.Filename() - if err := c.handleNestedDir(filename); err != nil { + if err := c.handleNestedDir(fi.Path()); err != nil { return err } } else if bundleType == bundleNot || (!fi.isOwner() && fi.isContentFile()) { @@ -376,23 +394,23 @@ func (c *capturer) handleDir(dirname string) error { func (c *capturer) handleNonBundle( dirname string, - fileInfos []fileInfoName, + fileInfos pathLangFileFis, singlesOnly bool) error { for _, fi := range fileInfos { if fi.IsDir() { - if err := c.handleNestedDir(fi.filename); err != nil { + if err := c.handleNestedDir(fi.Filename()); err != nil { return err } } else { if singlesOnly { - f, active := c.newFileInfo(fi.filename, fi, bundleNot) + f, active := c.newFileInfo(fi, bundleNot) if !active { continue } c.handler.handleSingles(f) } else { - c.handler.handleCopyFiles(fi.filename) + c.handler.handleCopyFiles(fi) } } } @@ -405,7 +423,7 @@ func (c *capturer) copyOrHandleSingle(fi *fileInfo) { c.handler.handleSingles(fi) } else { // These do not currently need any further processing. - c.handler.handleCopyFiles(fi.Filename()) + c.handler.handleCopyFiles(fi) } } @@ -430,7 +448,7 @@ func (c *capturer) createBundleDirs(fileInfos []*fileInfo, bundleType bundleDirT fileInfos = append(fileInfos, fis...) } } - err := c.collectFiles(fi.Filename(), collector) + err := c.collectFiles(fi.Path(), collector) if err != nil { return nil, err } @@ -462,6 +480,7 @@ func (c *capturer) createBundleDirs(fileInfos []*fileInfo, bundleType bundleDirT } func (c *capturer) collectFiles(dirname string, handleFiles func(fis ...*fileInfo)) error { + filesInDir, err := c.readDir(dirname) if err != nil { return err @@ -469,12 +488,12 @@ func (c *capturer) collectFiles(dirname string, handleFiles func(fis ...*fileInf for _, fi := range filesInDir { if fi.IsDir() { - err := c.collectFiles(fi.filename, handleFiles) + err := c.collectFiles(fi.Filename(), handleFiles) if err != nil { return err } } else { - f, active := c.newFileInfo(fi.filename, fi.FileInfo, bundleNot) + f, active := c.newFileInfo(fi, bundleNot) if active { handleFiles(f) } @@ -484,27 +503,29 @@ func (c *capturer) collectFiles(dirname string, handleFiles func(fis ...*fileInf return nil } -func (c *capturer) readDir(dirname string) ([]fileInfoName, error) { +func (c *capturer) readDir(dirname string) (pathLangFileFis, error) { if c.sourceSpec.IgnoreFile(dirname) { return nil, nil } dir, err := c.fs.Open(dirname) if err != nil { - return nil, err + return nil, fmt.Errorf("readDir: %s", err) } defer dir.Close() - names, err := dir.Readdirnames(-1) + fis, err := dir.Readdir(-1) if err != nil { return nil, err } - fis := make([]fileInfoName, 0, len(names)) + pfis := make(pathLangFileFis, 0, len(fis)) - for _, name := range names { - filename := filepath.Join(dirname, name) - if !c.sourceSpec.IgnoreFile(filename) { - fi, _, err := c.getRealFileInfo(filename) + for _, fi := range fis { + fip := fi.(pathLangFileFi) + + if !c.sourceSpec.IgnoreFile(fip.Filename()) { + + err := c.resolveRealPathIn(fip) if err != nil { // It may have been deleted in the meantime. @@ -514,23 +535,30 @@ func (c *capturer) readDir(dirname string) ([]fileInfoName, error) { return nil, err } - fis = append(fis, fileInfoName{filename: filename, FileInfo: fi}) + pfis = append(pfis, fip) } } - return fis, nil + return pfis, nil } -func (c *capturer) newFileInfo(filename string, fi os.FileInfo, tp bundleDirType) (*fileInfo, bool) { - f := newFileInfo(c.sourceSpec, c.baseDir, filename, fi, tp) +func (c *capturer) newFileInfo(fi pathLangFileFi, tp bundleDirType) (*fileInfo, bool) { + f := newFileInfo(c.sourceSpec, "", "", fi, tp) return f, !f.disabled } -type fileInfoName struct { +type pathLangFile interface { + hugofs.LanguageAnnouncer + hugofs.FilePather +} + +type pathLangFileFi interface { os.FileInfo - filename string + pathLangFile } +type pathLangFileFis []pathLangFileFi + type bundleDirs struct { tp bundleDirType // Maps languages to bundles. @@ -589,16 +617,17 @@ func (b *bundleDirs) addBundleContentFile(fi *fileInfo) { b.bundles[fi.Lang()] = dir } - dir.resources[fi.Filename()] = fi + dir.resources[fi.Path()] = fi } func (b *bundleDirs) addBundleFiles(fi *fileInfo) { dir := filepath.ToSlash(fi.Dir()) p := dir + fi.TranslationBaseName() + "." + fi.Ext() for lang, bdir := range b.bundles { - key := lang + p + key := path.Join(lang, p) + // Given mypage.de.md (German translation) and mypage.md we pick the most - // the specific for that language. + // specific for that language. if fi.Lang() == lang || !b.langOverrides[key] { bdir.resources[key] = fi } @@ -623,40 +652,53 @@ func (c *capturer) isSeen(dirname string) bool { return false } -func (c *capturer) getRealFileInfo(path string) (os.FileInfo, string, error) { - fileInfo, err := c.lstatIfOs(path) - realPath := path - +func (c *capturer) resolveRealPath(path string) (pathLangFileFi, error) { + fileInfo, err := c.lstatIfPossible(path) if err != nil { - return nil, "", err + return nil, err } + return fileInfo, c.resolveRealPathIn(fileInfo) +} + +func (c *capturer) resolveRealPathIn(fileInfo pathLangFileFi) error { + + basePath := fileInfo.BaseDir() + path := fileInfo.Filename() + + realPath := path if fileInfo.Mode()&os.ModeSymlink == os.ModeSymlink { link, err := filepath.EvalSymlinks(path) if err != nil { - return nil, "", fmt.Errorf("Cannot read symbolic link %q, error was: %s", path, err) + return fmt.Errorf("Cannot read symbolic link %q, error was: %s", path, err) } - fileInfo, err = c.lstatIfOs(link) + // This is a file on the outside of any base fs, so we have to use the os package. + sfi, err := os.Stat(link) if err != nil { - return nil, "", fmt.Errorf("Cannot stat %q, error was: %s", link, err) + return fmt.Errorf("Cannot stat %q, error was: %s", link, err) + } + + // TODO(bep) improve all of this. + if a, ok := fileInfo.(*hugofs.LanguageFileInfo); ok { + a.FileInfo = sfi } realPath = link - if realPath != path && fileInfo.IsDir() && c.isSeen(realPath) { + if realPath != path && sfi.IsDir() && c.isSeen(realPath) { // Avoid cyclic symlinks. // Note that this may prevent some uses that isn't cyclic and also // potential useful, but this implementation is both robust and simple: // We stop at the first directory that we have seen before, e.g. // /content/blog will only be processed once. - return nil, realPath, errSkipCyclicDir + return errSkipCyclicDir } if c.contentChanges != nil { // Keep track of symbolic links in watch mode. var from, to string - if fileInfo.IsDir() { + if sfi.IsDir() { from = realPath to = path @@ -667,12 +709,11 @@ func (c *capturer) getRealFileInfo(path string) (os.FileInfo, string, error) { from = from + helpers.FilePathSeparator } - baseDir := c.baseDir - if !strings.HasSuffix(baseDir, helpers.FilePathSeparator) { - baseDir = baseDir + helpers.FilePathSeparator + if !strings.HasSuffix(basePath, helpers.FilePathSeparator) { + basePath = basePath + helpers.FilePathSeparator } - if strings.HasPrefix(from, baseDir) { + if strings.HasPrefix(from, basePath) { // With symbolic links inside /content we need to keep // a reference to both. This may be confusing with --navigateToChanged // but the user has chosen this him or herself. @@ -688,9 +729,13 @@ func (c *capturer) getRealFileInfo(path string) (os.FileInfo, string, error) { } } - return fileInfo, realPath, nil + return nil } -func (c *capturer) lstatIfOs(path string) (os.FileInfo, error) { - return helpers.LstatIfOs(c.fs, path) +func (c *capturer) lstatIfPossible(path string) (pathLangFileFi, error) { + fi, err := helpers.LstatIfPossible(c.fs, path) + if err != nil { + return nil, err + } + return fi.(pathLangFileFi), nil } |