From 8210dbb9932d326e49c380ef90080720e4d64251 Mon Sep 17 00:00:00 2001 From: Ed Burke Date: Mon, 18 Dec 2023 21:44:33 -0500 Subject: [PATCH] set nwn_script_comp as default compiler This commit sets `nwn_script_comp` as the default compiler and reuses the configuration options `nssCompiler` and `nssFlags`. `nwn_script_comp` output is read through peg and modified to appear nasher-organic. --- CHANGELOG.md | 6 +++ README.md | 67 ++++++++++++++-------------- nasher.nimble | 2 +- src/nasher/compile.nim | 40 ++++++++++++----- src/nasher/utils/cli.nim | 2 +- src/nasher/utils/compiler.nim | 83 +++++++++++++++++++++++------------ src/nasher/utils/shared.nim | 4 +- 7 files changed, 130 insertions(+), 74 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 985ee3e..e2f1fac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # nasher changelog +## 0.22.0: + +- neverwinter.nim's `nwn_script_comp` is now the default script compiler. + Users who want to continue using nwnsc must set the `nssCompiler` and + `nssFlags` configuration values as noted in the [readme](readme.md). + ## 0.21.0: September 2, 2023 - The `compile` command now returns a non-zero exit code on failure. When diff --git a/README.md b/README.md index 440fd44..15724d4 100644 --- a/README.md +++ b/README.md @@ -64,15 +64,19 @@ of nasher for your OS and place a pointer to the location of the executable file in your [`PATH` environment variable](https://superuser.com/a/284351). In addition, you will need the following tools: -* [neverwinter.nim](https://github.com/niv/neverwinter.nim/releases) >= 1.6.4 +* [neverwinter.nim](https://github.com/niv/neverwinter.nim/releases) >= 1.7.0 * [nwnt](https://github.com/WilliamDraco/NWNT) >= 1.3.3 -* [nwnsc](https://github.com/nwneetools/nwnsc/releases) >= 1.1.3 * [git](https://git-scm.com/downloads) +Starting with 0.22.0, nasher's default script compiler is neverwinter.nim's +`nwn_script_comp`. If you'd like to use the legacy script compiler nwnsc, +you'll need: +* [nwnsc](https://github.com/nwneetools/nwnsc/releases) >= 1.1.3 + #### Tips * Keep the binaries for nasher, neverwinter.nim, and nwnsc in the same location. -* Do not keep binaries in your nasher project folder +* Do not keep binaries in your nasher project folder. * Do not publish binaries with your source control repository. If you are collaborating, each team member should download and install the binaries individually. @@ -84,9 +88,11 @@ In addition, you will need the following tools: First, install the following: * [nim](https://nim-lang.org) and it package manager `nimble`. The easy way to install is to use [choosenim](https://github.com/dom96/choosenim). -* [nwnsc](https://github.com/nwneetools/nwnsc) >= 1.1.3 * [git](https://git-scm.com/downloads) +Optionally, install nwnsc if you'd like to use the legacy script compiler: +* [nwnsc](https://github.com/nwneetools/nwnsc) >= 1.1.3 + *Note: when building nasher, nimble will download and install neverwinter.nim automatically. You do not need to install it yourself.* @@ -130,31 +136,37 @@ alias nasher='docker run --rm -it -v ${pwd}:/nasher nwntools/nasher:latest ' ``` #### Tips -Create batch/script files to run your most common nasher commands since the +* Create batch/script files to run your most common nasher commands since the docker commands can be rather verbose. An excellent example of this is in [The Frozen North](https://github.com/b5635/the-frozen-north) GitHub repository. ## Getting Started ### First-Time Setup -nasher will detect nwnsc and the neverwinter.nim tools if they are in your -`PATH`. You can also use nasher's `config` command to set the proper locations: +nasher will detect neverwinter.nim tools and your chosen script compiler if +they are in your `PATH` enviromental variable. You can also use nasher's +`config` command to set the proper locations if the tools are not on your +`PATH` variable: ```console -$ # Set the path to nwnsc -$ nasher config nssCompiler "%USERPROFILE%/bin/nwnsc.exe" # Windows -$ nasher config nssCompiler "~/.local/bin/nwnsc" # Posix +$ # Set the path to the script compiler +$ # neverwinter.nim script compiler +$ nasher config nssCompiler "%USERPROFILE%/bin/nwn_script_comp.exe" # Windows +$ nasher config nssCompiler "~/.local/bin/nwn_script_comp" # Posix +$ # or nwnsc (this must be set if you want to use nwnsc with nasher >=0.22.0) +$ nasher config nssCompiler "%USERPROFILE%/bin/nwnsc.exe" # Windows +$ nasher config nssCompiler "~/.local/bin/nwnsc" # Posix $ # Set the path to nwn_erf -$ nasher config erfUtil "%USERPROFILE%/bin/nwn_erf.exe" # Windows -$ nasher config erfUtil "~/.local/bin/nwn_erf" # Posix +$ nasher config erfUtil "%USERPROFILE%/bin/nwn_erf.exe" # Windows +$ nasher config erfUtil "~/.local/bin/nwn_erf" # Posix $ # Set the path to nwn_gff -$ nasher config gffUtil "%USERPROFILE%/bin/nwn_gff.exe" # Windows -$ nasher config gffUtil "~/.local/bin/nwn_gff" # Posix +$ nasher config gffUtil "%USERPROFILE%/bin/nwn_gff.exe" # Windows +$ nasher config gffUtil "~/.local/bin/nwn_gff" # Posix $ # Set the path to nwn_tlk -$ nasher config tlkUtil "%USERPROFILE%/bin/nwn_tlk.exe" # Windows -$ nasher config tlkUtil "~/.local/bin/nwn_tlk" # Posix +$ nasher config tlkUtil "%USERPROFILE%/bin/nwn_tlk.exe" # Windows +$ nasher config tlkUtil "~/.local/bin/nwn_tlk" # Posix ``` nasher will also detect NWN if it was installed by Steam, Beamdog, or GOG. If you are having issues getting nasher to recognize your NWN install, you can set @@ -205,7 +217,7 @@ $ nasher install You can get help for nasher or one of its commands using the `--help` flag: ```console $ nasher --help # General help -$ nasher init --help # Command-specific help +$ nasher --help # Command-specific help ``` If you're still stuck, you can get assistance in several locations: @@ -324,7 +336,7 @@ file = "myPWtlk.tlk" include = "src/tlk/*.json" ``` -While you can write your own package file, the [`init`](#init) command will +While you can write your own configuration file, the [`init`](#init) command will create one for you. It will show prompts for each section and provide useful defaults. If you don't want to answer the prompts and just want to quickly initialize the package, you can pass the `--default` flag when running `init`. @@ -350,7 +362,7 @@ Some fields, while optional, are inherited from the package by | --- | --- | --- | | `file` | no | filename including extension be created; can optionally include path info | | `group` | yes | a group a target may belong to; used to build multiple targets at once | -| `flags` | yes | command line arguments to send to nwnsc at compile-time | +| `flags` | yes | command line arguments to send to the script compiler at compile-time | | `branch` | no | the git branch to use for source files | | `modName` | no | the name to give a module target file | | `modMinGameVersion` | no | the minimum game version to run a module target file | @@ -381,7 +393,7 @@ must be specified before the child target. | `parent` | no | no | a target to inherit missing values from (if missing, will inherit from `[package]`) | | `file` | no | yes | filename including extension be created; can optionally include path info | | `group` | yes | yes | a group this target belongs to; used to build multiple targets at once | -| `flags` | yes | yes | command line arguments to send to nwnsc at compile-time | +| `flags` | yes | yes | command line arguments to send to the script compiler at compile-time | | `branch` | no | yes | the git branch to use for source files | | `modName` | no | yes | the name to give a module target file | | `modMinGameVersion` | no | yes | the minimum game version to run a module target file | @@ -642,8 +654,8 @@ by passing the key/value pair as an option to the command. - default (Posix): `nwnsc` - default (Windows): `nwnsc.exe` - `nssFlags`: the default flags to use on packages - - default: `-lowqey` - - note: since nwnsc can read the `NWN_ROOT` environment variable to find + - default: `` for `nwn_script_comp``, `-lowqey` for `nwnsc` + - note: since compilers can read the `NWN_ROOT` environment variable to find your NWN install, it is preferable to use that rather than passing the location through `nssFlags`. If `NWN_ROOT` is set (or if nasher can find your NWN install without it), nwnsc should work fine using the default @@ -704,17 +716,6 @@ by passing the key/value pair as an option to the command. float value changes. - default: `4` - supported: `1` - `32` -- `modName`: the name for any module file to be generated by the target. This - is independent of the filename. Only relevant when `convert` will be called. - - default: "" -- `modMinGameVersion`: the minimum game version that can run any module file - generated by the target. Only relevant when `convert` will be called. - - default: "" - - note: if blank, the version in the `module.ifo` file will be unchanged. -- `modDescription`: the description for a module file generated by a target. - Only relevant when `convert` will be called. - - default: "" - - note: If blank, the description in the `module.ifo` file will be unchanged. - `onMultipleSources`: an action to perform when multiple source files of the same name are found for a target. - default: `choose` diff --git a/nasher.nimble b/nasher.nimble index ca7a0bb..35e7a9d 100644 --- a/nasher.nimble +++ b/nasher.nimble @@ -1,6 +1,6 @@ # Package -version = "0.21.0" +version = "0.22.0" author = "Michael A. Sinclair" description = "A build tool for Neverwinter Nights projects" license = "MIT" diff --git a/src/nasher/compile.nim b/src/nasher/compile.nim index f3cd775..859f05d 100644 --- a/src/nasher/compile.nim +++ b/src/nasher/compile.nim @@ -8,8 +8,10 @@ Usage: Description: Compiles all nss sources for . If is not supplied, the first - target supplied by the config files will be compiled. The input and output - files are placed in .nasher/cache/. + target supplied by the config files will be compiled. The input files are + placed in .nasher/cache/. The output files are placed in the same + folder unless the compiler flags `-b` (for nwnsc`) or `-o`/`-d` (for + nwn_script_comp) are specified. Compilation of scripts is handled automatically by 'nasher pack', so you only need to use this if you want to compile the scripts without converting gff @@ -117,10 +119,27 @@ proc getUpdated(updatedNSS: var seq[string], files: seq[string]): seq[string] = updatedNss = result +proc getFlags(compiler: Compiler, opts: Options, target: Target): seq[string] = + let nssFlags = opts.get("nssFlags", compilerFlags[compiler.ord]).parseCmdLine + var flags = nssFlags & target.flags + + case compiler: + of Organic: + let + installDir = opts.get("installDir", getEnv("NWN_HOME")).expandPath + rootDir = getNwnRootDir().expandPath + + if installDir.len > 0: flags = flags & "--userdirectory" & installDir + if rootDir.len > 0: flags = flags & "--root" & rootDir + + result = flags & "-c" + of Legacy: + result = flags + proc compile*(opts: Options, target: Target, updatedNss: var seq[string], exitCode: var int): bool = let cmd = opts["command"] - cacheDir = ".nasher" / "cache" / target.name + cacheDir = (".nasher" / "cache" / target.name) abortOnCompileError = if opts.hasKey("abortOnCompileError"): if opts.get("abortOnCompileError", false): Answer.No @@ -134,8 +153,8 @@ proc compile*(opts: Options, target: Target, updatedNss: var seq[string], exitCo return cmd != "compile" let - compiler = opts.findBin("nssCompiler", "nwnsc", "script compiler") - userFlags = opts.get("nssFlags", "-lowqey").parseCmdLine + bin = opts.findBin("nssCompiler", $Compiler.low, "script compiler") + compiler = parseEnum[Compiler](bin.splitPath.tail.splitFile.name, Compiler.low) withDir(cacheDir): # If we are only compiling one file... @@ -186,8 +205,9 @@ proc compile*(opts: Options, target: Target, updatedNss: var seq[string], exitCo for chunk, scripts in distribute(scripts, chunks): if chunks > 1: info("Compiling", "$1 scripts (chunk $2/$3)" % [$scripts.len, $(chunk + 1), $chunks]) - let args = userFlags & target.flags & scripts - if runCompiler(compiler, args) != 0: + + let args = compiler.getFlags(opts, target) & scripts + if runCompiler(bin, args) != 0: warning("Errors encountered during compilation (see above)") if chunk + 1 < chunks: let forced = getForceAnswer() @@ -204,8 +224,8 @@ proc compile*(opts: Options, target: Target, updatedNss: var seq[string], exitCo let unmatchedNcs = executables.filterIt(not fileExists(it.changeFileExt("ncs"))) if unmatchedNcs.len > 0: warning(""" - Compiled only $1 of $2 scripts. The following executable scripts do not - have matching .ncs files due to an nwnsc error: $3""".dedent % + Compiled $1 of $2 scripts. The following executable scripts do not + have a matching compiled (.ncs) script file: $3""".dedent % [$(scripts.len - unmatchedNcs.len), $scripts.len, unmatchedNcs.join(", ")]) if cmd in ["pack", "install", "serve", "test", "play"]: let forced = getForceAnswer() @@ -219,7 +239,7 @@ proc compile*(opts: Options, target: Target, updatedNss: var seq[string], exitCo else: exitCode = QuitFailure else: - success("All executable scripts have a matching compiled (.ncs) script", LowPriority); + success("All executable scripts have a matching compiled (.ncs) script file", LowPriority); if scripts.len > 0: success("Compiled $1 scripts" % $scripts.len) diff --git a/src/nasher/utils/cli.nim b/src/nasher/utils/cli.nim index df235c2..471fafa 100644 --- a/src/nasher/utils/cli.nim +++ b/src/nasher/utils/cli.nim @@ -20,7 +20,7 @@ type None, No, Yes, Default const - colWidth = len("Initializing") + colWidth = len("Compile Error:") foregrounds: array[Error .. Prompt, ForegroundColor] = [fgRed, fgYellow, fgCyan, fgGreen, fgYellow] styles: array[DebugPriority .. HighPriority, set[Style]] = diff --git a/src/nasher/utils/compiler.nim b/src/nasher/utils/compiler.nim index 5d920ce..dbc2aca 100644 --- a/src/nasher/utils/compiler.nim +++ b/src/nasher/utils/compiler.nim @@ -1,39 +1,68 @@ -import osproc, parseutils, sequtils, streams, strutils +import os, osproc, parseutils, pegs, sequtils, streams, strutils import cli +type Compiler* = enum + Organic = "nwn_script_comp" #default + Legacy = "nwnsc" -proc parseCompilerOutput(line: var string): bool = - ## Intercepts nwnsc's output and converts it into nasher's cli format. Returns - ## whether any errors were detected. We have to do this here because nwnsc - ## does not return consistent exit codes. - var - token: string - parsed = line.parseUntil(token, ':') + 2 - - case token - of "Compiling": - info("Compiling", line[parsed..^1]) - of "Error": - error(line[parsed..^1]) - else: - if token == line: - debug("Compiler:", line) - else: - var lines = line.split(':').mapIt(it.strip) - if lines.contains("Error"): - error(lines.filterIt(it != "Error").join("\n")) - result = true - elif lines.contains("Warning"): - warning(lines.filterIt(it != "Warning").join("\n")) +const compilerFlags* = + # Default compiler flags; ordered referenced to `Compiler` enum above + ["-y", "-lowqey"] + +proc parseCompilerOutput(line: var string, compiler: Compiler): bool = + ## Intercepts the compiler's output and converts it into nasher's cli format. + case compiler: + of Organic: + if line =~ peg""" + output <- error / result + error <- type data* path file errorData data + result <- type data* results + type <- {[EI]} \s* + data <- \[ @ \] \s* + path <- (([A-Z][:]) / '~' / \/)? (![:] . )* [:] \s* + file <- {(![:] .)*}[:] \s* + errorData <- "ERROR:" \s* {(!\[.)*} &\[ + results <- {.*} + """: + case matches[0]: + of "I": + display("Results:", matches[1]) + of "E": + error("Compile Error:", "$1 :: $2" % [matches[1], matches[2].strip]) + result = true + else: + #Catchall to find other errors I didn't expect + display("Unknown Compiler Output", line) + of Legacy: + var + token: string + parsed = line.parseUntil(token, ':') + 2 + + case token + of "Compiling": + info("Compiling", line[parsed..^1]) + of "Error": + error(line[parsed..^1]) else: - display(lines.join("\n")) + if token == line: + debug("Compiler:", line) + else: + var lines = line.split(':').mapIt(it.strip) + if lines.contains("Error"): + error(lines.filterIt(it != "Error").join("\n")) + result = true + elif lines.contains("Warning"): + warning(lines.filterIt(it != "Warning").join("\n")) + else: + display(lines.join("\n")) proc runCompiler*(cmd: string, args: openArray[string] = []): int = - ## Runs the nwnsc compiler and returns its error code + ## Runs the compiler and returns its error code let params = args.filterIt(it.len > 0) options = {poUsePath, poStdErrToStdOut} + compiler = parseEnum[Compiler](cmd.splitPath.tail.splitFile.name, Compiler.low) debug("Executing", "$1 $2" % [cmd, params.join(" ")]) var @@ -43,7 +72,7 @@ proc runCompiler*(cmd: string, args: openArray[string] = []): int = while p.running: if s.readLine(line): - if line.parseCompilerOutput: + if line.parseCompilerOutput(compiler): result = 1 p.close diff --git a/src/nasher/utils/shared.nim b/src/nasher/utils/shared.nim index 2d073b1..270cc62 100644 --- a/src/nasher/utils/shared.nim +++ b/src/nasher/utils/shared.nim @@ -59,8 +59,8 @@ Utility Options: const CompilerOpts = """ Compiler Options: --abortOnCompileError Quit if an error was encountered during compilation - --nssCompiler: Binary for compiling nss scripts [default: nwnsc] - --nssFlags: Flags to pass to the compiler [default: -lowqey] + --nssCompiler: Binary for compiling nss scripts [default: nwn_script_comp] + --nssFlags: Flags to pass to the compiler [default: ] --nssChunks: Max scripts to compile per compiler exec [default: 500] """