feat: remove NWScript compiler support
This commit is contained in:
@@ -6,11 +6,9 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -544,228 +542,10 @@ func collectModuleResources(p *project.Project, moduleHakOrder []string) ([]erf.
|
||||
moduleResources = append(moduleResources, resource)
|
||||
}
|
||||
|
||||
compiledScripts, err := compileReferencedScripts(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
moduleResources = append(moduleResources, compiledScripts...)
|
||||
|
||||
sortResources(moduleResources)
|
||||
return moduleResources, nil
|
||||
}
|
||||
|
||||
func compileReferencedScripts(p *project.Project) ([]erf.Resource, error) {
|
||||
if skipNSSCompilation() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
referenced, err := referencedScriptResrefs(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(referenced) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
scriptSources := make(map[string]string, len(p.Inventory.ScriptFiles))
|
||||
for _, rel := range p.Inventory.ScriptFiles {
|
||||
resref := strings.ToLower(strings.TrimSuffix(filepath.Base(rel), filepath.Ext(rel)))
|
||||
scriptSources[resref] = filepath.Join(p.SourceDir(), filepath.FromSlash(rel))
|
||||
}
|
||||
|
||||
compiler, err := resolveScriptCompiler(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
compilerEnv, err := resolveScriptCompilerEnvironment(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cacheDir := compiledScriptCacheDir(p)
|
||||
if err := os.RemoveAll(cacheDir); err != nil {
|
||||
return nil, fmt.Errorf("clear script compiler cache dir: %w", err)
|
||||
}
|
||||
if err := os.MkdirAll(cacheDir, 0o755); err != nil {
|
||||
return nil, fmt.Errorf("create script compiler cache dir: %w", err)
|
||||
}
|
||||
|
||||
resources := make([]erf.Resource, 0, len(referenced))
|
||||
scriptsDir := p.ScriptSourceDir()
|
||||
for _, resref := range referenced {
|
||||
sourcePath, ok := scriptSources[resref]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
outputPath := filepath.Join(cacheDir, resref+".ncs")
|
||||
cmd := exec.Command(compiler, "--dirs", scriptsDir, "-o", outputPath, sourcePath)
|
||||
cmd.Env = compilerEnv
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
message := strings.TrimSpace(string(output))
|
||||
if message == "" {
|
||||
message = err.Error()
|
||||
}
|
||||
return nil, fmt.Errorf("compile script %s: %s", filepath.Base(sourcePath), message)
|
||||
}
|
||||
resource, err := rawResource(outputPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load compiled script %s: %w", filepath.Base(outputPath), err)
|
||||
}
|
||||
resources = append(resources, resource)
|
||||
}
|
||||
|
||||
return resources, nil
|
||||
}
|
||||
|
||||
func skipNSSCompilation() bool {
|
||||
switch strings.ToLower(strings.TrimSpace(os.Getenv("SOW_MODULE_SKIP_NSS_COMPILATION"))) {
|
||||
case "1", "true", "yes", "on":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func compiledScriptCacheDir(p *project.Project) string {
|
||||
return p.ScriptCacheDir()
|
||||
}
|
||||
|
||||
func referencedScriptResrefs(p *project.Project) ([]string, error) {
|
||||
referenced := map[string]struct{}{}
|
||||
for _, rel := range p.Inventory.SourceFiles {
|
||||
abs := filepath.Join(p.SourceDir(), filepath.FromSlash(rel))
|
||||
document, _, _, err := validatorLoadDocument(abs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
validatorWalkFields(document.Root, func(fieldLabel, value string) {
|
||||
if !validatorIsScriptField(fieldLabel) {
|
||||
return
|
||||
}
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" {
|
||||
return
|
||||
}
|
||||
referenced[strings.ToLower(value)] = struct{}{}
|
||||
})
|
||||
}
|
||||
|
||||
resrefs := make([]string, 0, len(referenced))
|
||||
for resref := range referenced {
|
||||
resrefs = append(resrefs, resref)
|
||||
}
|
||||
slices.Sort(resrefs)
|
||||
return resrefs, nil
|
||||
}
|
||||
|
||||
func resolveScriptCompiler(p *project.Project) (string, error) {
|
||||
candidates := make([]string, 0, 2+len(p.ScriptCompilerSearchPaths()))
|
||||
if configured := p.ScriptCompilerPath(); configured != "" {
|
||||
candidates = append(candidates, configured)
|
||||
}
|
||||
if configured := strings.TrimSpace(os.Getenv(p.ScriptCompilerPathEnv())); configured != "" {
|
||||
candidates = append(candidates, configured)
|
||||
}
|
||||
candidates = append(candidates, p.ScriptCompilerSearchPaths()...)
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
slices.SortStableFunc(candidates, func(a, b string) int {
|
||||
aExe := strings.HasSuffix(strings.ToLower(a), ".exe")
|
||||
bExe := strings.HasSuffix(strings.ToLower(b), ".exe")
|
||||
if aExe == bExe {
|
||||
return strings.Compare(a, b)
|
||||
}
|
||||
if aExe {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
})
|
||||
}
|
||||
for _, candidate := range candidates {
|
||||
if binary, ok := strings.CutPrefix(candidate, "PATH:"); ok {
|
||||
if compiler, err := exec.LookPath(binary); err == nil && scriptCompilerRunnable(compiler) {
|
||||
return compiler, nil
|
||||
}
|
||||
continue
|
||||
}
|
||||
if info, err := os.Stat(candidate); err == nil && !info.IsDir() && scriptCompilerRunnable(candidate) {
|
||||
return candidate, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("script compiler not found; configure scripts.compiler.path or set %s", p.ScriptCompilerPathEnv())
|
||||
}
|
||||
|
||||
func scriptCompilerRunnable(path string) bool {
|
||||
if strings.TrimSpace(path) == "" {
|
||||
return false
|
||||
}
|
||||
info, err := os.Stat(path)
|
||||
if err != nil || info.IsDir() {
|
||||
return false
|
||||
}
|
||||
cmd := exec.Command(path, "--help")
|
||||
cmd.Stdout = io.Discard
|
||||
cmd.Stderr = io.Discard
|
||||
return cmd.Run() == nil
|
||||
}
|
||||
|
||||
func validatorLoadDocument(path string) (gff.Document, string, string, error) {
|
||||
raw, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return gff.Document{}, "", "", fmt.Errorf("read %s: %w", path, err)
|
||||
}
|
||||
|
||||
stem := strings.TrimSuffix(filepath.Base(path), ".json")
|
||||
extension := strings.TrimPrefix(strings.ToLower(filepath.Ext(stem)), ".")
|
||||
if extension == "" {
|
||||
return gff.Document{}, "", "", fmt.Errorf("missing resource extension before .json: %s", path)
|
||||
}
|
||||
resref := strings.ToLower(strings.TrimSuffix(stem, "."+extension))
|
||||
|
||||
var document gff.Document
|
||||
if err := json.Unmarshal(raw, &document); err != nil {
|
||||
return gff.Document{}, "", "", fmt.Errorf("parse %s: %w", path, err)
|
||||
}
|
||||
return document, resref, extension, nil
|
||||
}
|
||||
|
||||
func validatorWalkFields(root gff.Struct, visit func(string, string)) {
|
||||
walkScriptFields(root, visit)
|
||||
}
|
||||
|
||||
func validatorIsScriptField(label string) bool {
|
||||
return strings.HasPrefix(label, "On") || strings.HasSuffix(label, "Script") || strings.Contains(label, "Script")
|
||||
}
|
||||
|
||||
func walkScriptFields(s gff.Struct, visit func(string, string)) {
|
||||
for _, field := range s.Fields {
|
||||
if value, ok := fieldStringValueForScripts(field.Value); ok {
|
||||
visit(field.Label, value)
|
||||
}
|
||||
switch typed := field.Value.(type) {
|
||||
case gff.Struct:
|
||||
walkScriptFields(typed, visit)
|
||||
case gff.ListValue:
|
||||
for _, item := range typed {
|
||||
walkScriptFields(item, visit)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fieldStringValueForScripts(value gff.Value) (string, bool) {
|
||||
switch typed := value.(type) {
|
||||
case gff.StringValue:
|
||||
return string(typed), true
|
||||
case gff.ResRefValue:
|
||||
return string(typed), true
|
||||
default:
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
|
||||
func collectAssetResources(p *project.Project, requireContent bool, allowed map[string]struct{}, musicAssets *preparedMusicAssets, generated2DAAssets []topdata.Generated2DAAsset) ([]assetResource, error) {
|
||||
var hakResources []assetResource
|
||||
assetCreatedAt, err := collectGitAssetCreationTimes(p)
|
||||
|
||||
Reference in New Issue
Block a user