Technically this isn't quite right, as the full list of files is not available to the go parser (some files are filtered out too early in the process), but it should do the job for now.
314 lines
6.2 KiB
Go
314 lines
6.2 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"go/build"
|
|
"io"
|
|
"io/fs"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type WrappedStat struct {
|
|
fs.FileInfo
|
|
newName string // meow
|
|
}
|
|
|
|
func (s *WrappedStat) Name() string {
|
|
return s.newName
|
|
}
|
|
|
|
type DirStat struct {
|
|
name string
|
|
}
|
|
|
|
func (s *DirStat) Name() string {
|
|
return s.name
|
|
}
|
|
|
|
func (s *DirStat) Size() int64 {
|
|
return 1
|
|
}
|
|
|
|
func (s *DirStat) Mode() fs.FileMode {
|
|
return fs.FileMode(fs.ModeDir | 0777)
|
|
}
|
|
|
|
func (s *DirStat) ModTime() time.Time {
|
|
return time.Time{}
|
|
}
|
|
|
|
func (s *DirStat) IsDir() bool {
|
|
return true
|
|
}
|
|
|
|
func (s *DirStat) Sys() any {
|
|
return "zilch"
|
|
}
|
|
|
|
type Input struct {
|
|
// list of [directory, filename, target path] (target path is empty, if directory marker)
|
|
Files [][3]string `json:"files"`
|
|
GOARCH string `json:"GOARCH"`
|
|
GOOS string `json:"GOOS"`
|
|
}
|
|
|
|
type Output struct {
|
|
Name string `json:"name"`
|
|
GoFiles []string `json:"goFiles"`
|
|
SFiles []string `json:"sFiles"`
|
|
Imports []string `json:"imports"`
|
|
Embeds map[string][][]string `json:"embeds"`
|
|
}
|
|
|
|
type FileOrDir struct {
|
|
Directory []string
|
|
File string
|
|
}
|
|
|
|
func main() {
|
|
inputFile, err := os.Open(os.Args[1])
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
var input Input
|
|
err = json.NewDecoder(inputFile).Decode(&input)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
files := make(map[string]*FileOrDir)
|
|
for _, file := range input.Files {
|
|
directory := file[0]
|
|
filename := file[1]
|
|
contents := file[2]
|
|
|
|
dirslash := directory
|
|
|
|
if directory != "" {
|
|
dirslash = directory + "/"
|
|
}
|
|
|
|
if contents == "" {
|
|
_, ok := files[dirslash+filename]
|
|
if !ok {
|
|
files[dirslash+filename] = &FileOrDir{}
|
|
}
|
|
} else {
|
|
files[dirslash+filename] = &FileOrDir{File: contents}
|
|
}
|
|
|
|
parent, ok := files[directory]
|
|
if !ok {
|
|
parent = &FileOrDir{}
|
|
files[directory] = parent
|
|
}
|
|
|
|
parent.Directory = append(parent.Directory, dirslash+filename)
|
|
}
|
|
|
|
ctx := build.Context{
|
|
GOARCH: input.GOARCH,
|
|
GOOS: input.GOOS,
|
|
Compiler: "gc",
|
|
ToolTags: build.Default.ToolTags,
|
|
ReleaseTags: build.Default.ReleaseTags,
|
|
ReadDir: func(dir string) ([]fs.FileInfo, error) {
|
|
fmt.Printf("ReadDir(%q)\n", dir)
|
|
if !strings.HasPrefix(dir, "/code") {
|
|
return nil, fs.ErrNotExist
|
|
}
|
|
|
|
dir = path.Clean(dir[5:])[1:]
|
|
|
|
contents, ok := files[dir]
|
|
|
|
if !ok {
|
|
return nil, fs.ErrNotExist
|
|
}
|
|
|
|
infos := make([]fs.FileInfo, len(contents.Directory))
|
|
for i, filename := range contents.Directory {
|
|
file := files[filename]
|
|
if file.File != "" {
|
|
stat, err := os.Stat(file.File)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
infos[i] = &WrappedStat{FileInfo: stat, newName: path.Base(filename)}
|
|
} else {
|
|
infos[i] = &DirStat{path.Base(filename)}
|
|
}
|
|
}
|
|
|
|
return infos, nil
|
|
},
|
|
|
|
OpenFile: func(pth string) (io.ReadCloser, error) {
|
|
opth := pth
|
|
fmt.Printf("OpenFile(%q)\n", pth)
|
|
if !strings.HasPrefix(pth, "/code") {
|
|
return nil, fs.ErrNotExist
|
|
}
|
|
|
|
pth = path.Clean(pth[5:])[1:]
|
|
fmt.Printf("-> OpenFile(%q)\n", pth)
|
|
fil := files[pth]
|
|
|
|
data, err := os.Open(fil.File)
|
|
if err != nil {
|
|
return data, fmt.Errorf("OpenFile(%q; %q): %w", opth, pth, err)
|
|
}
|
|
return data, err
|
|
},
|
|
|
|
IsDir: func(dir string) bool {
|
|
fmt.Printf("IsDir(%q)\n", dir)
|
|
if !strings.HasPrefix(dir, "/code") {
|
|
return false
|
|
}
|
|
|
|
dir = path.Clean(dir[5:])[1:]
|
|
|
|
contents, ok := files[dir]
|
|
fmt.Printf("IsDir -> %q, %v, %v\n", dir, contents, ok)
|
|
return ok && contents.File == ""
|
|
},
|
|
|
|
HasSubdir: func(root, dir string) (rel string, ok bool) {
|
|
root = path.Clean(root)
|
|
dir = path.Clean(dir)
|
|
|
|
return strings.CutPrefix(dir, root)
|
|
},
|
|
}
|
|
|
|
var filenames []string
|
|
for _, filedata := range input.Files {
|
|
if filedata[2] != "" {
|
|
filenames = append(filenames, filepath.Join(filedata[0], filedata[1]))
|
|
}
|
|
}
|
|
|
|
outfiles := make(map[string]Output)
|
|
|
|
for dir, filelist := range files {
|
|
if filelist.File != "" {
|
|
continue
|
|
}
|
|
|
|
isGo := false
|
|
for _, file := range filelist.Directory {
|
|
if strings.HasSuffix(file, ".go") {
|
|
isGo = true
|
|
break
|
|
}
|
|
}
|
|
|
|
fmt.Printf("Checking %q..\n", dir)
|
|
if strings.Contains(dir, "/.") || strings.Contains(dir, "/_") || strings.Contains(dir, "/testdata/") {
|
|
fmt.Printf(" skipping; \n")
|
|
continue
|
|
}
|
|
|
|
base := path.Base(dir)
|
|
if base == "." {
|
|
base = ""
|
|
}
|
|
|
|
if !isGo || strings.HasPrefix(base, "_") || strings.HasPrefix(base, ".") || base == "testdata" {
|
|
fmt.Printf(" skipping (not go)\n")
|
|
continue
|
|
}
|
|
|
|
pkg, err := ctx.Import(".", path.Clean("/code/"+dir), 0)
|
|
if err != nil {
|
|
if _, ok := err.(*build.NoGoError); ok {
|
|
continue
|
|
}
|
|
|
|
panic(err)
|
|
}
|
|
|
|
out := Output{
|
|
Name: pkg.Name,
|
|
Imports: pkg.Imports,
|
|
GoFiles: pkg.GoFiles,
|
|
SFiles: pkg.SFiles,
|
|
Embeds: make(map[string][][]string),
|
|
}
|
|
|
|
// _test only, or so
|
|
if len(pkg.GoFiles) == 0 {
|
|
continue
|
|
}
|
|
|
|
sort.Strings(out.Imports)
|
|
sort.Strings(out.GoFiles)
|
|
sort.Strings(out.SFiles)
|
|
sort.Strings(pkg.EmbedPatterns)
|
|
|
|
for _, pattern := range pkg.EmbedPatterns {
|
|
matchedFiles := []string{}
|
|
|
|
storePattern := pattern
|
|
|
|
// TODO: proper matching
|
|
if strings.HasPrefix(pattern, "all:") {
|
|
pattern = pattern[4:]
|
|
}
|
|
|
|
fullPattern := filepath.Join(dir, pattern)
|
|
for _, file := range filenames {
|
|
if ok, _ := filepath.Match(fullPattern, file); ok {
|
|
matchedFiles = append(matchedFiles, file)
|
|
}
|
|
}
|
|
|
|
if len(matchedFiles) == 0 {
|
|
dirpattern := fullPattern + "/"
|
|
for _, file := range filenames {
|
|
if strings.HasPrefix(file, dirpattern) {
|
|
matchedFiles = append(matchedFiles, file)
|
|
}
|
|
|
|
if file == fullPattern {
|
|
matchedFiles = append(matchedFiles, file)
|
|
}
|
|
}
|
|
}
|
|
|
|
sort.Strings(matchedFiles)
|
|
var split [][]string
|
|
for _, match := range matchedFiles {
|
|
dirname := filepath.Dir(match)
|
|
if dirname == "." {
|
|
dirname = ""
|
|
}
|
|
split = append(split, []string{match[len(dir)+1:], dirname, filepath.Base(match)})
|
|
}
|
|
|
|
out.Embeds[storePattern] = split
|
|
}
|
|
|
|
outfiles[dir] = out
|
|
}
|
|
|
|
out, err := os.OpenFile(os.Getenv("out"), os.O_CREATE|os.O_RDWR, 0666)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if err := json.NewEncoder(out).Encode(outfiles); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
out.Close()
|
|
}
|