feat: remove NWScript compiler support

This commit is contained in:
2026-06-09 17:57:57 +02:00
parent de7e99c55c
commit f3ed13ce19
10 changed files with 4 additions and 1297 deletions
+2 -129
View File
@@ -355,9 +355,8 @@ func runBuildModule(ctx context) error {
}
type buildModulePreflightResult struct {
ManifestStatus string
ManifestPath string
ScriptCompilationStatus string
ManifestStatus string
ManifestPath string
}
func runBuildModulePreflight(ctx context, p *project.Project, progress func(string)) (buildModulePreflightResult, error) {
@@ -371,11 +370,6 @@ func runBuildModulePreflight(ctx context, p *project.Project, progress func(stri
}
result.ManifestStatus = manifestStatus
result.ManifestPath = manifestPath
scriptStatus, err := prepareBuildModuleCompiler(ctx, p, progress)
if err != nil {
return result, err
}
result.ScriptCompilationStatus = scriptStatus
return result, nil
}
@@ -406,117 +400,6 @@ func refreshBuildModuleManifest(ctx context, p *project.Project, progress func(s
return "refreshed", manifestPath, nil
}
func prepareBuildModuleCompiler(ctx context, p *project.Project, progress func(string)) (string, error) {
if envEnabled("SOW_MODULE_SKIP_NSS_COMPILATION") {
progress("SOW_MODULE_SKIP_NSS_COMPILATION is set; skipping NWScript compiler resolution and NSS compilation for this run.")
return "skipped", nil
}
if !projectHasScriptSources(p) {
return "not-needed", nil
}
compilerPathEnv := p.ScriptCompilerPathEnv()
explicitCompiler := strings.TrimSpace(os.Getenv(compilerPathEnv))
if explicitCompiler != "" {
if scriptCompilerRunnable(explicitCompiler) {
return "enabled", nil
}
if fileExists(explicitCompiler) {
return disableBuildModuleCompiler(compilerPathEnv, progress, fmt.Sprintf("Configured NWScript compiler is present but cannot start: %s", explicitCompiler))
}
}
if _, ok := findUsableBuildModuleCompiler(p); ok {
return "enabled", nil
}
installScript := projectScriptPath(p.Root, "scripts", "install-script-compiler")
if !fileExists(installScript) {
return disableBuildModuleCompiler(compilerPathEnv, progress, "NWScript compiler not found locally and automatic installer script is unavailable.")
}
progress("NWScript compiler not found locally. Installing it now...")
if err := runProjectScript(ctx, p, []string{"scripts", "install-script-compiler"}); err != nil {
return disableBuildModuleCompiler(compilerPathEnv, progress, "Automatic NWScript compiler install failed.")
}
if _, ok := findUsableBuildModuleCompiler(p); ok {
return "installed", nil
}
return disableBuildModuleCompiler(compilerPathEnv, progress, "Installed NWScript compiler but it still cannot start.")
}
func disableBuildModuleCompiler(compilerPathEnv string, progress func(string), reason string) (string, error) {
if err := os.Setenv("SOW_MODULE_SKIP_NSS_COMPILATION", "1"); err != nil {
return "", fmt.Errorf("set SOW_MODULE_SKIP_NSS_COMPILATION: %w", err)
}
progress("Skipping NWScript compilation for this build. " + reason)
if compilerPathEnv != "" {
_ = os.Unsetenv(compilerPathEnv)
}
return "skipped", nil
}
func projectHasScriptSources(p *project.Project) bool {
for _, rel := range p.Inventory.ScriptFiles {
if strings.EqualFold(filepath.Ext(rel), ".nss") {
return true
}
}
return false
}
func findUsableBuildModuleCompiler(p *project.Project) (string, bool) {
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()...)
for _, candidate := range candidates {
if compiler, ok := resolveRunnableCompilerCandidate(candidate); ok {
return compiler, true
}
}
return "", false
}
func resolveRunnableCompilerCandidate(candidate string) (string, bool) {
if candidate == "" {
return "", false
}
if binary, ok := strings.CutPrefix(candidate, "PATH:"); ok {
compiler, err := exec.LookPath(binary)
if err != nil {
return "", false
}
if scriptCompilerRunnable(compiler) {
return compiler, true
}
return "", false
}
if !fileExists(candidate) {
return "", false
}
if scriptCompilerRunnable(candidate) {
return candidate, true
}
return "", false
}
func scriptCompilerRunnable(path string) bool {
if !fileExists(path) {
return false
}
cmd := exec.Command(path, "--help")
cmd.Stdout = io.Discard
cmd.Stderr = io.Discard
return cmd.Run() == nil
}
func runProjectScript(ctx context, p *project.Project, scriptParts []string, args ...string) error {
if len(scriptParts) == 0 {
return errors.New("project script path is required")
@@ -659,10 +542,6 @@ func (c *projectConsole) phaseLabel(message string) string {
return "refreshing HAK manifest"
case strings.HasPrefix(message, "Using existing hak manifest at "):
return "reusing HAK manifest"
case strings.HasPrefix(message, "NWScript compiler not found locally. Installing it now"):
return "installing script compiler"
case strings.HasPrefix(message, "Skipping NWScript compilation for this build."):
return "skipping script compilation"
case strings.HasPrefix(message, "Validating project"):
return "validating project"
case strings.HasPrefix(message, "Resolving module HAK order"):
@@ -689,9 +568,6 @@ func (c *projectConsole) emitBuildResult(result pipeline.BuildResult, preflight
}
fmt.Fprintln(c.stdout)
}
if preflight.ScriptCompilationStatus != "" && preflight.ScriptCompilationStatus != "not-needed" {
fmt.Fprintf(c.stdout, "script compilation: %s\n", preflight.ScriptCompilationStatus)
}
if result.TopPackageHAK != "" {
fmt.Fprintf(c.stdout, "top package hak: %s\n", c.relPath(result.TopPackageHAK))
fmt.Fprintf(c.stdout, "top package tlk: %s\n", c.relPath(result.TopPackageTLK))
@@ -716,9 +592,6 @@ func (c *projectConsole) emitBuildModuleResult(result pipeline.BuildResult, pref
}
fmt.Fprintln(c.stdout)
}
if preflight.ScriptCompilationStatus != "" && preflight.ScriptCompilationStatus != "not-needed" {
fmt.Fprintf(c.stdout, "script compilation: %s\n", preflight.ScriptCompilationStatus)
}
}
func (c *projectConsole) emitExtractResult(result pipeline.ExtractResult) {
-171
View File
@@ -322,169 +322,6 @@ func TestProjectConsoleEmitsRelativePaths(t *testing.T) {
}
}
func TestRunBuildModuleToolkitPreflightRefreshesManifestAndSkipsBrokenCompiler(t *testing.T) {
root := t.TempDir()
mkdirAll(t, filepath.Join(root, "src", "module"))
mkdirAll(t, filepath.Join(root, "src", "placeables"))
mkdirAll(t, filepath.Join(root, "src", "scripts"))
mkdirAll(t, filepath.Join(root, "build"))
mkdirAll(t, filepath.Join(root, "scripts"))
writeFile(t, filepath.Join(root, "nwn-tool.yaml"), `
module:
name: Test Module
resref: testmod
paths:
source: src
build: build
scripts:
compiler:
search:
- tools/script-compiler/nwn_script_comp
`)
writeFile(t, filepath.Join(root, "src", "module", "module.ifo.json"), `{
"file_type": "IFO ",
"file_version": "V3.2",
"root": {
"struct_type": 0,
"fields": [
{"label": "Mod_Name", "type": "CExoString", "value": "Test Module"}
]
}
}
`)
writeFile(t, filepath.Join(root, "src", "placeables", "thing.utp.json"), `{
"file_type": "UTP ",
"file_version": "V3.2",
"root": {
"struct_type": 0,
"fields": [
{"label": "OnUsed", "type": "ResRef", "value": "use_thing"}
]
}
}
`)
writeFile(t, filepath.Join(root, "src", "scripts", "use_thing.nss"), "void main() {}\n")
writeFile(t, filepath.Join(root, "scripts", "fetch-hak-manifest.sh"), "#!/usr/bin/env sh\nset -eu\nmkdir -p \"$(dirname \"$1\")\"\nprintf '{\"module_haks\":[\"sow_core\"],\"haks\":[]}\n' >\"$1\"\n")
if err := os.Chmod(filepath.Join(root, "scripts", "fetch-hak-manifest.sh"), 0o755); err != nil {
t.Fatalf("chmod fetch-hak-manifest.sh: %v", err)
}
badCompiler := filepath.Join(root, "bad-compiler.sh")
writeFile(t, badCompiler, "#!/usr/bin/env sh\nexit 1\n")
if err := os.Chmod(badCompiler, 0o755); err != nil {
t.Fatalf("chmod bad compiler: %v", err)
}
t.Setenv("SOW_NWN_SCRIPT_COMPILER", badCompiler)
t.Setenv("SOW_MODULE_SKIP_NSS_COMPILATION", "")
var stdout bytes.Buffer
var stderr bytes.Buffer
ctx := context{
stdout: &stdout,
stderr: &stderr,
cwd: root,
args: []string{"build-module"},
}
if err := runBuildModule(ctx); err != nil {
t.Fatalf("runBuildModule failed: %v", err)
}
moduleSource := mustReadFile(t, filepath.Join(root, "src", "module", "module.ifo.json"))
if !strings.Contains(moduleSource, "\"Mod_HakList\"") || !strings.Contains(moduleSource, "\"sow_core\"") {
t.Fatalf("expected manifest-applied module source, got %q", moduleSource)
}
if _, err := os.Stat(filepath.Join(root, ".cache", "ncs", "use_thing.ncs")); !os.IsNotExist(err) {
t.Fatalf("expected broken explicit compiler to skip compilation, got err=%v", err)
}
if !strings.Contains(stdout.String(), "script compilation: skipped") {
t.Fatalf("expected skip-compilation summary in output, got %q", stdout.String())
}
if strings.Contains(stdout.String(), "[build-module]") {
t.Fatalf("did not expect raw build-module progress in normal output, got %q", stdout.String())
}
}
func TestRunBuildModuleToolkitPreflightInstallsCompilerWhenNeeded(t *testing.T) {
root := t.TempDir()
mkdirAll(t, filepath.Join(root, "src", "module"))
mkdirAll(t, filepath.Join(root, "src", "placeables"))
mkdirAll(t, filepath.Join(root, "src", "scripts"))
mkdirAll(t, filepath.Join(root, "build"))
mkdirAll(t, filepath.Join(root, "scripts"))
writeFile(t, filepath.Join(root, "nwn-tool.yaml"), `
module:
name: Test Module
resref: testmod
paths:
source: src
build: build
scripts:
compiler:
search:
- tools/script-compiler/nwn_script_comp
`)
writeFile(t, filepath.Join(root, "src", "module", "module.ifo.json"), `{
"file_type": "IFO ",
"file_version": "V3.2",
"root": {
"struct_type": 0,
"fields": [
{"label": "Mod_Name", "type": "CExoString", "value": "Test Module"}
]
}
}
`)
writeFile(t, filepath.Join(root, "src", "placeables", "thing.utp.json"), `{
"file_type": "UTP ",
"file_version": "V3.2",
"root": {
"struct_type": 0,
"fields": [
{"label": "OnUsed", "type": "ResRef", "value": "use_thing"}
]
}
}
`)
writeFile(t, filepath.Join(root, "src", "scripts", "use_thing.nss"), "void main() {}\n")
writeFile(t, filepath.Join(root, "scripts", "fetch-hak-manifest.sh"), "#!/usr/bin/env sh\nset -eu\nmkdir -p \"$(dirname \"$1\")\"\nprintf '{\"module_haks\":[\"sow_core\"],\"haks\":[]}\n' >\"$1\"\n")
writeFile(t, filepath.Join(root, "scripts", "install-script-compiler.sh"), "#!/usr/bin/env sh\nset -eu\ninstall_dir=\"${SOW_SCRIPT_COMPILER_INSTALL_DIR:-./tools/script-compiler}\"\nmkdir -p \"$install_dir\"\ncat >\"$install_dir/nwn_script_comp\" <<'SH'\n#!/usr/bin/env sh\nset -eu\nif [ \"${1:-}\" = \"--help\" ]; then\n exit 0\nfi\nout=\"\"\nsrc=\"\"\nwhile [ \"$#\" -gt 0 ]; do\n case \"$1\" in\n -o)\n out=\"$2\"\n shift 2\n ;;\n --dirs)\n shift 2\n ;;\n *)\n src=\"$1\"\n shift\n ;;\n esac\ndone\nprintf 'compiled:%s\\n' \"$(basename \"$src\")\" >\"$out\"\nSH\nchmod +x \"$install_dir/nwn_script_comp\"\nprintf 'installed: %s\\n' \"$install_dir\"\n")
if err := os.Chmod(filepath.Join(root, "scripts", "fetch-hak-manifest.sh"), 0o755); err != nil {
t.Fatalf("chmod fetch-hak-manifest.sh: %v", err)
}
if err := os.Chmod(filepath.Join(root, "scripts", "install-script-compiler.sh"), 0o755); err != nil {
t.Fatalf("chmod install-script-compiler.sh: %v", err)
}
t.Setenv("SOW_NWN_SCRIPT_COMPILER", "")
t.Setenv("SOW_MODULE_SKIP_NSS_COMPILATION", "")
var stdout bytes.Buffer
var stderr bytes.Buffer
ctx := context{
stdout: &stdout,
stderr: &stderr,
cwd: root,
args: []string{"build-module"},
}
if err := runBuildModule(ctx); err != nil {
t.Fatalf("runBuildModule failed: %v\nstdout:\n%s\nstderr:\n%s", err, stdout.String(), stderr.String())
}
compiledScript := filepath.Join(root, ".cache", "ncs", "use_thing.ncs")
if _, err := os.Stat(compiledScript); err != nil {
t.Fatalf("expected installed compiler to compile script: %v", err)
}
if !strings.Contains(stdout.String(), "script compilation: installed") {
t.Fatalf("expected install summary in output, got %q", stdout.String())
}
if strings.Contains(stdout.String(), "[build-module]") {
t.Fatalf("did not expect raw build-module progress in normal output, got %q", stdout.String())
}
}
func TestRunMusicScanUsesConfiguredDatasetsWithoutWriting(t *testing.T) {
root := t.TempDir()
mkdirAll(t, filepath.Join(root, "assets", "audio", "westgate"))
@@ -736,11 +573,3 @@ func setFileTime(t *testing.T, path string, modTime time.Time) {
}
}
func mustReadFile(t *testing.T, path string) string {
t.Helper()
raw, err := os.ReadFile(path)
if err != nil {
t.Fatalf("read %s: %v", path, err)
}
return string(raw)
}