85 lines
2.1 KiB
Go
85 lines
2.1 KiB
Go
// Package assets is the crucible `assets` builder: NWN:EE model/texture tools
|
|
// that operate in place on a target directory. Mirrors internal/depot's
|
|
// Run(args, stdout, stderr, getenv) shape and bypasses the legacy internal/app
|
|
// surface entirely.
|
|
package assets
|
|
|
|
import (
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
// runner is the single external-command indirection so tests can observe and
|
|
// stub every engine / ImageMagick / upscaler invocation. dir is the working
|
|
// directory ("" = inherit); env replaces the child environment when non-nil.
|
|
var runner = func(dir string, env []string, name string, args ...string) ([]byte, error) {
|
|
cmd := exec.Command(name, args...)
|
|
cmd.Dir = dir
|
|
if env != nil {
|
|
cmd.Env = env
|
|
}
|
|
return cmd.CombinedOutput()
|
|
}
|
|
|
|
// walk collects files under each root whose lower-cased extension is in exts. A
|
|
// root that is itself a matching file is included. recursive controls descent
|
|
// into subdirectories. Results are sorted and deduplicated.
|
|
func walk(roots []string, exts map[string]bool, recursive bool) ([]string, error) {
|
|
seen := map[string]bool{}
|
|
var out []string
|
|
add := func(p string) {
|
|
if exts[strings.ToLower(filepath.Ext(p))] && !seen[p] {
|
|
seen[p] = true
|
|
out = append(out, p)
|
|
}
|
|
}
|
|
for _, root := range roots {
|
|
fi, err := os.Stat(root)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !fi.IsDir() {
|
|
add(root)
|
|
continue
|
|
}
|
|
err = filepath.WalkDir(root, func(p string, d os.DirEntry, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if d.IsDir() {
|
|
if !recursive && p != root {
|
|
return filepath.SkipDir
|
|
}
|
|
return nil
|
|
}
|
|
add(p)
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
sort.Strings(out)
|
|
return out, nil
|
|
}
|
|
|
|
// look returns the first bare name found on PATH, or the first candidate that
|
|
// is an existing path. Returns "" if none resolve.
|
|
func look(candidates ...string) string {
|
|
for _, c := range candidates {
|
|
if strings.ContainsRune(c, filepath.Separator) {
|
|
if fi, err := os.Stat(c); err == nil && !fi.IsDir() {
|
|
return c
|
|
}
|
|
continue
|
|
}
|
|
if p, err := exec.LookPath(c); err == nil {
|
|
return p
|
|
}
|
|
}
|
|
return ""
|
|
}
|