git.wit.org/jcarr/packr@v1.10.8/builder/builder.go (about)

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