2024-10-03 23:57:22 +00:00
|
|
|
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 {
|
2024-11-01 00:12:04 +00:00
|
|
|
// 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"`
|
2024-10-03 23:57:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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"`
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-01 00:12:04 +00:00
|
|
|
type FileOrDir struct {
|
|
|
|
|
Directory []string
|
|
|
|
|
File string
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-03 23:57:22 +00:00
|
|
|
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)
|
|
|
|
|
}
|
2024-11-01 00:12:04 +00:00
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-03 23:57:22 +00:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-01 00:12:04 +00:00
|
|
|
dir = path.Clean(dir[5:])[1:]
|
2024-10-03 23:57:22 +00:00
|
|
|
|
2024-11-01 00:12:04 +00:00
|
|
|
contents, ok := files[dir]
|
2024-10-03 23:57:22 +00:00
|
|
|
|
|
|
|
|
if !ok {
|
|
|
|
|
return nil, fs.ErrNotExist
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-01 00:12:04 +00:00
|
|
|
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
|
2024-10-03 23:57:22 +00:00
|
|
|
}
|
|
|
|
|
|
2024-11-01 00:12:04 +00:00
|
|
|
infos[i] = &WrappedStat{FileInfo: stat, newName: path.Base(filename)}
|
|
|
|
|
} else {
|
|
|
|
|
infos[i] = &DirStat{path.Base(filename)}
|
2024-10-03 23:57:22 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-01 00:12:04 +00:00
|
|
|
pth = path.Clean(pth[5:])[1:]
|
|
|
|
|
fmt.Printf("-> OpenFile(%q)\n", pth)
|
|
|
|
|
fil := files[pth]
|
2024-10-03 23:57:22 +00:00
|
|
|
|
2024-11-01 00:12:04 +00:00
|
|
|
data, err := os.Open(fil.File)
|
2024-10-03 23:57:22 +00:00
|
|
|
if err != nil {
|
2024-11-01 00:12:04 +00:00
|
|
|
return data, fmt.Errorf("OpenFile(%q; %q): %w", opth, pth, err)
|
2024-10-03 23:57:22 +00:00
|
|
|
}
|
|
|
|
|
return data, err
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
IsDir: func(dir string) bool {
|
|
|
|
|
fmt.Printf("IsDir(%q)\n", dir)
|
|
|
|
|
if !strings.HasPrefix(dir, "/code") {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-01 00:12:04 +00:00
|
|
|
dir = path.Clean(dir[5:])[1:]
|
2024-10-03 23:57:22 +00:00
|
|
|
|
2024-11-01 00:12:04 +00:00
|
|
|
contents, ok := files[dir]
|
|
|
|
|
fmt.Printf("IsDir -> %q, %v, %v\n", dir, contents, ok)
|
|
|
|
|
return ok && contents.File == ""
|
2024-10-03 23:57:22 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
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
|
2024-11-01 00:12:04 +00:00
|
|
|
for _, filedata := range input.Files {
|
|
|
|
|
if filedata[2] != "" {
|
|
|
|
|
filenames = append(filenames, filepath.Join(filedata[0], filedata[1]))
|
2024-10-03 23:57:22 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-01 00:12:04 +00:00
|
|
|
outfiles := make(map[string]Output)
|
|
|
|
|
|
|
|
|
|
for dir, filelist := range files {
|
|
|
|
|
if filelist.File != "" {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2024-10-03 23:57:22 +00:00
|
|
|
|
|
|
|
|
isGo := false
|
2024-11-01 00:12:04 +00:00
|
|
|
for _, file := range filelist.Directory {
|
2024-10-03 23:57:22 +00:00
|
|
|
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)
|
2024-11-01 00:12:04 +00:00
|
|
|
if base == "." {
|
|
|
|
|
base = ""
|
|
|
|
|
}
|
2024-10-03 23:57:22 +00:00
|
|
|
|
|
|
|
|
if !isGo || strings.HasPrefix(base, "_") || strings.HasPrefix(base, ".") || base == "testdata" {
|
|
|
|
|
fmt.Printf(" skipping (not go)\n")
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-01 00:12:04 +00:00
|
|
|
pkg, err := ctx.Import(".", path.Clean("/code/"+dir), 0)
|
2024-10-03 23:57:22 +00:00
|
|
|
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{}
|
|
|
|
|
|
2024-11-17 18:24:00 +00:00
|
|
|
storePattern := pattern
|
|
|
|
|
|
2024-10-03 23:57:22 +00:00
|
|
|
// 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 {
|
2024-11-01 00:12:04 +00:00
|
|
|
dirname := filepath.Dir(match)
|
|
|
|
|
if dirname == "." {
|
|
|
|
|
dirname = ""
|
|
|
|
|
}
|
|
|
|
|
split = append(split, []string{match[len(dir)+1:], dirname, filepath.Base(match)})
|
2024-10-03 23:57:22 +00:00
|
|
|
}
|
|
|
|
|
|
2024-11-17 18:24:00 +00:00
|
|
|
out.Embeds[storePattern] = split
|
2024-10-03 23:57:22 +00:00
|
|
|
}
|
|
|
|
|
|
2024-11-01 00:12:04 +00:00
|
|
|
outfiles[dir] = out
|
2024-10-03 23:57:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out, err := os.OpenFile(os.Getenv("out"), os.O_CREATE|os.O_RDWR, 0666)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-01 00:12:04 +00:00
|
|
|
if err := json.NewEncoder(out).Encode(outfiles); err != nil {
|
2024-10-03 23:57:22 +00:00
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out.Close()
|
|
|
|
|
}
|