func (l *Loader) addTempGoFiles() (undo func(), _ error) {
	root := "."
	cmd := exec.Command("go", "list", "-m", "-f={{.Dir}}")
	cmd.Dir = l.Dir
	if out, err := cmd.Output(); err == nil {
		root = strings.TrimSpace(string(out))
	}
	if root == "" {
		return nil, fmt.Errorf("empty module root; missing module?")
	}
	var toDelete []string
	if err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if !info.IsDir() {
			return nil
		}
		if strings.Contains(path, "@v") {
			return filepath.SkipDir
		}
		infos, err := ioutil.ReadDir(path)
		if err != nil {
			return err
		}
		pkgName := info.Name()  
		anyGunk := false
		for _, info := range infos {
			name := info.Name()
			if strings.HasSuffix(name, ".go") {
				return nil
			}
			if strings.HasSuffix(name, ".gunk") {
				f, err := parser.ParseFile(token.NewFileSet(),
					filepath.Join(path, name), nil, parser.PackageClauseOnly)
				if err == nil {
					pkgName = f.Name.Name
				}
				anyGunk = true
				break
			}
		}
		if !anyGunk {
			return nil
		}
		tmpPath := filepath.Join(path, "gunkpkg-"+hexRand(8)+".go")
		if err := ioutil.WriteFile(tmpPath, []byte("package "+pkgName), 0o666); err != nil {
			return err
		}
		toDelete = append(toDelete, tmpPath)
		return nil
	}); err != nil {
		return nil, err
	}
	return func() {
		anyErr := false
		for _, path := range toDelete {
			if err := os.Remove(path); err != nil {
				anyErr = true
				fmt.Fprintf(os.Stderr, "could not delete gunkpkg file: %v", err)
			}
		}
		if anyErr {
			panic("could not delete some of the gunkpkg files")
		}
	}, nil
}
addTempGoFiles adds a temporary empty Go file with a random name to all Gunk
packages with no Go files, so that packages.Load can find them via patterns
like "./...". Check all directories within the current module, falling back
to all directories under the current directory.
package loader
import (
	"encoding/hex"
	"fmt"
	"go/ast"
	"go/constant"
	"go/parser"
	"go/scanner"
	"go/token"
	"go/types"
	"html/template"
	"io/ioutil"
	"math/rand"
	"os"
	"os/exec"
	"path/filepath"
	"reflect"
	"strconv"
	"strings"
	"time"
	"github.com/gunk/gunk/assets"
	"github.com/gunk/gunk/log"
	"golang.org/x/tools/go/packages"
	"google.golang.org/protobuf/proto"
	"google.golang.org/protobuf/types/descriptorpb"
)
type Loader struct {
	Dir  string
	Fset *token.FileSet
	Types bool
	cache map[string]*GunkPackage  
}
var seededRand = rand.New(rand.NewSource(time.Now().UnixNano()))
func hexRand(enclen int) string {
	p := make([]byte, hex.DecodedLen(enclen))
	_, _ = seededRand.Read(p)
	return hex.EncodeToString(p)
}