github.com/zachgersh/packr@v1.11.1/builder/builder.go (about)

     1  package builder
     2  
     3  import (
     4  	"context"
     5  	"os"
     6  	"path/filepath"
     7  	"regexp"
     8  	"strings"
     9  	"sync"
    10  	"text/template"
    11  
    12  	"github.com/pkg/errors"
    13  	"golang.org/x/sync/errgroup"
    14  )
    15  
    16  var DebugLog func(string, ...interface{})
    17  
    18  func init() {
    19  	DebugLog = func(string, ...interface{}) {}
    20  }
    21  
    22  var invalidFilePattern = regexp.MustCompile(`(_test|-packr).go$`)
    23  
    24  // Builder scans folders/files looking for `packr.NewBox` and then compiling
    25  // the required static files into `<package-name>-packr.go` files so they can
    26  // be built into Go binaries.
    27  type Builder struct {
    28  	context.Context
    29  	RootPath       string
    30  	IgnoredBoxes   []string
    31  	IgnoredFolders []string
    32  	pkgs           map[string]pkg
    33  	moot           *sync.Mutex
    34  	Compress       bool
    35  }
    36  
    37  // Run the builder.
    38  func (b *Builder) Run() error {
    39  	wg := &errgroup.Group{}
    40  	root, err := filepath.EvalSymlinks(b.RootPath)
    41  	if err != nil {
    42  		return errors.WithStack(err)
    43  	}
    44  	err = filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
    45  		if info == nil {
    46  			return filepath.SkipDir
    47  		}
    48  
    49  		base := strings.ToLower(filepath.Base(path))
    50  		for _, f := range b.IgnoredFolders {
    51  			if strings.ToLower(f) == base {
    52  				return filepath.SkipDir
    53  			}
    54  		}
    55  		if !info.IsDir() {
    56  			wg.Go(func() error {
    57  				return b.process(path)
    58  			})
    59  		}
    60  		return nil
    61  	})
    62  	if err != nil {
    63  		return errors.WithStack(err)
    64  	}
    65  	if err := wg.Wait(); err != nil {
    66  		return errors.WithStack(err)
    67  	}
    68  	return b.dump()
    69  }
    70  
    71  func (b *Builder) dump() error {
    72  	for _, p := range b.pkgs {
    73  		name := filepath.Join(p.Dir, p.Name+"-packr.go")
    74  		f, err := os.Create(name)
    75  		defer f.Close()
    76  		if err != nil {
    77  			return errors.WithStack(err)
    78  		}
    79  		t, err := template.New("").Parse(tmpl)
    80  
    81  		if err != nil {
    82  			return errors.WithStack(err)
    83  		}
    84  		err = t.Execute(f, p)
    85  		if err != nil {
    86  			return errors.WithStack(err)
    87  		}
    88  	}
    89  	return nil
    90  }
    91  
    92  func (b *Builder) process(path string) error {
    93  	ext := filepath.Ext(path)
    94  	if ext != ".go" || invalidFilePattern.MatchString(path) {
    95  		return nil
    96  	}
    97  
    98  	v := newVisitor(path)
    99  	if err := v.Run(); err != nil {
   100  		return errors.WithStack(err)
   101  	}
   102  
   103  	pk := pkg{
   104  		Dir:   filepath.Dir(path),
   105  		Boxes: []box{},
   106  		Name:  v.Package,
   107  	}
   108  
   109  	for _, n := range v.Boxes {
   110  		var ignored bool
   111  		for _, i := range b.IgnoredBoxes {
   112  			if n == i {
   113  				// this is an ignored box
   114  				ignored = true
   115  				break
   116  			}
   117  		}
   118  		if ignored {
   119  			continue
   120  		}
   121  		bx := &box{
   122  			Name:     n,
   123  			Files:    []file{},
   124  			compress: b.Compress,
   125  		}
   126  		DebugLog("building box %s\n", bx.Name)
   127  		p := filepath.Join(pk.Dir, bx.Name)
   128  		if err := bx.Walk(p); err != nil {
   129  			return errors.WithStack(err)
   130  		}
   131  		if len(bx.Files) > 0 {
   132  			pk.Boxes = append(pk.Boxes, *bx)
   133  		}
   134  		DebugLog("built box %s with %q\n", bx.Name, bx.Files)
   135  	}
   136  
   137  	if len(pk.Boxes) > 0 {
   138  		b.addPkg(pk)
   139  	}
   140  	return nil
   141  }
   142  
   143  func (b *Builder) addPkg(p pkg) {
   144  	b.moot.Lock()
   145  	defer b.moot.Unlock()
   146  	if _, ok := b.pkgs[p.Name]; !ok {
   147  		b.pkgs[p.Name] = p
   148  		return
   149  	}
   150  	pp := b.pkgs[p.Name]
   151  	pp.Boxes = append(pp.Boxes, p.Boxes...)
   152  	b.pkgs[p.Name] = pp
   153  }
   154  
   155  // New Builder with a given context and path
   156  func New(ctx context.Context, path string) *Builder {
   157  	return &Builder{
   158  		Context:        ctx,
   159  		RootPath:       path,
   160  		IgnoredBoxes:   []string{},
   161  		IgnoredFolders: []string{"vendor", ".git", "node_modules", ".idea"},
   162  		pkgs:           map[string]pkg{},
   163  		moot:           &sync.Mutex{},
   164  	}
   165  }