173 lines
3.4 KiB
Go
173 lines
3.4 KiB
Go
package builder
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
"sync"
|
|
"text/template"
|
|
|
|
"github.com/pkg/errors"
|
|
"golang.org/x/sync/errgroup"
|
|
)
|
|
|
|
var DebugLog func(string, ...interface{})
|
|
|
|
func init() {
|
|
DebugLog = func(string, ...interface{}) {}
|
|
}
|
|
|
|
var invalidFilePattern = regexp.MustCompile(`(_test|-packr).go$`)
|
|
|
|
// Builder scans folders/files looking for `packr.NewBox` and then compiling
|
|
// the required static files into `<package-name>-packr.go` files so they can
|
|
// be built into Go binaries.
|
|
type Builder struct {
|
|
context.Context
|
|
RootPath string
|
|
IgnoredBoxes []string
|
|
IgnoredFolders []string
|
|
pkgs map[string]pkg
|
|
moot *sync.Mutex
|
|
Compress bool
|
|
}
|
|
|
|
// Run the builder.
|
|
func (b *Builder) Run() error {
|
|
wg := &errgroup.Group{}
|
|
root, err := filepath.EvalSymlinks(b.RootPath)
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
err = filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
|
|
if info == nil {
|
|
return filepath.SkipDir
|
|
}
|
|
|
|
base := strings.ToLower(filepath.Base(path))
|
|
if strings.HasPrefix(base, "_") {
|
|
return filepath.SkipDir
|
|
}
|
|
for _, f := range b.IgnoredFolders {
|
|
if strings.ToLower(f) == base {
|
|
if info.IsDir() {
|
|
return filepath.SkipDir
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
if !info.IsDir() {
|
|
wg.Go(func() error {
|
|
return b.process(path)
|
|
})
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
if err := wg.Wait(); err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
return b.dump()
|
|
}
|
|
|
|
func (b *Builder) dump() error {
|
|
for _, p := range b.pkgs {
|
|
name := filepath.Join(p.Dir, "a_"+p.Name+"-packr.go")
|
|
f, err := os.Create(name)
|
|
defer f.Close()
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
t, err := template.New("").Parse(tmpl)
|
|
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
err = t.Execute(f, p)
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (b *Builder) process(path string) error {
|
|
ext := filepath.Ext(path)
|
|
if ext != ".go" || invalidFilePattern.MatchString(path) {
|
|
return nil
|
|
}
|
|
|
|
v := newVisitor(path)
|
|
if err := v.Run(); err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
pk := pkg{
|
|
Dir: filepath.Dir(path),
|
|
Boxes: []box{},
|
|
Name: v.Package,
|
|
}
|
|
|
|
for _, n := range v.Boxes {
|
|
var ignored bool
|
|
for _, i := range b.IgnoredBoxes {
|
|
if n == i {
|
|
// this is an ignored box
|
|
ignored = true
|
|
break
|
|
}
|
|
}
|
|
if ignored {
|
|
continue
|
|
}
|
|
bx := &box{
|
|
Name: n,
|
|
Files: []file{},
|
|
compress: b.Compress,
|
|
}
|
|
DebugLog("building box %s\n", bx.Name)
|
|
p := filepath.Join(pk.Dir, bx.Name)
|
|
if err := bx.Walk(p); err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
if len(bx.Files) > 0 {
|
|
pk.Boxes = append(pk.Boxes, *bx)
|
|
}
|
|
DebugLog("built box %s with %q\n", bx.Name, bx.Files)
|
|
}
|
|
|
|
if len(pk.Boxes) > 0 {
|
|
b.addPkg(pk)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (b *Builder) addPkg(p pkg) {
|
|
b.moot.Lock()
|
|
defer b.moot.Unlock()
|
|
if _, ok := b.pkgs[p.Name]; !ok {
|
|
b.pkgs[p.Name] = p
|
|
return
|
|
}
|
|
pp := b.pkgs[p.Name]
|
|
pp.Boxes = append(pp.Boxes, p.Boxes...)
|
|
b.pkgs[p.Name] = pp
|
|
}
|
|
|
|
// New Builder with a given context and path
|
|
func New(ctx context.Context, path string) *Builder {
|
|
return &Builder{
|
|
Context: ctx,
|
|
RootPath: path,
|
|
IgnoredBoxes: []string{},
|
|
IgnoredFolders: []string{"vendor", ".git", "node_modules", ".idea"},
|
|
pkgs: map[string]pkg{},
|
|
moot: &sync.Mutex{},
|
|
}
|
|
}
|