ninjasyntax.nim: don't escape build() args

This commit is contained in:
Anna “CyberTailor” 2023-04-27 19:36:40 +05:00
parent ea0456c81b
commit f43973d392
Signed by: CyberTaIlor
GPG Key ID: E7B76EDC50864BB1
6 changed files with 63 additions and 58 deletions

View File

@ -1,3 +1,7 @@
2023-04-27 Anna <cyber@sysrq.in>
* ninjasyntax.nim (build): don't escape args automatically
2023-04-25 Anna <cyber@sysrq.in>
* nimbus.nim: drop support for patched Nim compilers

View File

@ -12,7 +12,7 @@ proc newline*(f: File) =
f.write('\n')
proc comment*(f: File, text: string) =
f.write("# " & text & '\n')
f.write(&"# {text}\n")
proc line(f: File, text: string, indent = 0) =
## Write indented 'text', followed by a newline.
@ -20,19 +20,22 @@ proc line(f: File, text: string, indent = 0) =
proc variable*(f: File, key, value: string, indent = 0) =
if key.len != 0 and value.len != 0:
f.line(&"{key} = {value}", indent)
f.line(fmt"{key} = {value}", indent)
proc pool*(f: File, name, depth: string) =
if name.len != 0 and depth.len != 0:
f.line(&"pool {name}")
f.line(fmt"pool {name}")
f.variable("depth", depth, indent = 1)
proc rule*(f: File, name, command: string; description, depfile, deps, pool,
rspfile, rspfileContent = ""; generator, restat = false) =
proc rule*(
f: File, name, command: string;
description, depfile, deps, pool, rspfile, rspfileContent = "";
generator, restat = false
) =
if name.len == 0 or command.len == 0:
return
f.line(&"rule {name}")
f.line(fmt"rule {name}")
f.variable("command", command, indent = 1)
if description.len != 0:
f.variable("description", description, indent = 1)
@ -51,13 +54,11 @@ proc rule*(f: File, name, command: string; description, depfile, deps, pool,
if restat:
f.variable("restat", "1", indent = 1)
func escapePaths(paths: var openArray[string]) =
for i in 0..high(paths):
paths[i] = paths[i].replace("$", "$$").replace(" ", "$ ").replace(":", "$:")
proc build*(f: File, outputs: openArray[string], rule: string; inputs, implicit,
orderOnly, implicitOutputs: openArray[string] = [];
variables: StringTableRef = nil; pool, dyndep = "") =
proc build*(
f: File, outputs: openArray[string], rule: string;
inputs, implicit, orderOnly, implicitOutputs: openArray[string] = [];
variables: StringTableRef = nil; pool, dyndep = ""
) =
# note: filterIt converts openArray[string] to seq[string]
var outputs = outputs.filterIt(it != "")
if outputs.len == 0 or rule.len == 0:
@ -68,29 +69,23 @@ proc build*(f: File, outputs: openArray[string], rule: string; inputs, implicit,
var orderOnly = orderOnly.filterIt(it.len != 0)
var implicitOutputs = implicitOutputs.filterIt(it.len != 0)
escapePaths outputs
escapePaths inputs
if implicit.len != 0:
escapePaths implicit
inputs.add("|")
inputs.add(implicit)
if orderOnly.len != 0:
escapePaths orderOnly
inputs.add("||")
inputs.add(orderOnly)
if implicitOutputs.len != 0:
escapePaths implicitOutputs
outputs.add("|")
outputs.add(implicitOutputs)
let outputsStr = join(outputs, " ")
let inputsStr = join(@[rule] & inputs, " ")
f.line(&"build {outputsStr}: {inputsStr}")
f.line(fmt"build {outputsStr}: {inputsStr}")
if pool.len != 0:
f.line(&"pool = {pool}", indent = 1)
f.line(fmt"pool = {pool}", indent = 1)
if dyndep.len != 0:
f.line(&"dyndep = {dyndep}", indent = 1)
f.line(fmt"dyndep = {dyndep}", indent = 1)
if variables != nil:
for key, val in variables.pairs:
@ -98,21 +93,27 @@ proc build*(f: File, outputs: openArray[string], rule: string; inputs, implicit,
proc `include`*(f: File, path: string) =
if path.len != 0:
f.line(&"include {path}")
f.line(fmt"include {path}")
proc subninja*(f: File, path: string) =
if path.len != 0:
f.line(&"subninja {path}")
f.line(fmt"subninja {path}")
proc default*(f: File, paths: openArray[string]) =
var paths = paths.filterIt(it.len != 0)
if paths.len != 0:
f.line("default " & paths.join(" "))
proc default*(f: File, targets: openArray[string]) =
var targets = targets.filterIt(it.len != 0)
if targets.len != 0:
f.line("default " & targets.join(" "))
func escape*(s: string): string =
func escape*(s: string, body = false): string =
## Escape a string such that it can be embedded into a Ninja file without
## further interpretation.
assert not s.contains("\n"), "Ninja syntax does not allow newlines"
# We only have one special metacharacter: "$".
return s.replace("$", "$$")
if body:
return s.replace("$", "$$")
else:
return s.multiReplace(
("$", "$$"),
(" ", "$ "),
(":", "$:")
)

View File

@ -29,9 +29,9 @@ proc application(ninja: File, input, output: string, paths: seq[string]) =
if paths.len != 0:
vars["paths"] = "-p:" & paths.join(" -p:")
ninja.build([output],
ninja.build([output.escape],
rule = "nimc",
inputs = [input],
inputs = [input.escape],
variables = vars
)
@ -41,9 +41,9 @@ proc task(ninja: File, nimsFile, taskName: string) =
var vars = newStringTable()
vars["taskname"] = taskName
ninja.build([taskName],
ninja.build([taskName.escape],
rule = "nimbletask",
inputs = [nimsFile],
inputs = [nimsFile.escape],
implicit = ["PHONY"],
variables = vars
)
@ -112,12 +112,12 @@ proc setup(options: Options) =
ninja.newline()
debug("[build.ninja] Writing variables")
ninja.variable("nim", options.getNimBin())
ninja.variable("nimbus", getAppFilename())
ninja.variable("nimflags", options.getNimFlags())
ninja.variable("sourcedir", options.getSourceDir())
ninja.variable("nimcache", options.getNimCache())
ninja.variable("cmdline", options.getCmdLine())
ninja.variable("nim", options.getNimBin().escape(body = true))
ninja.variable("nimbus", getAppFilename().escape(body = true))
ninja.variable("nimflags", options.getNimFlags().escape(body = true))
ninja.variable("sourcedir", options.getSourceDir().escape(body = true))
ninja.variable("nimcache", options.getNimCache().escape(body = true))
ninja.variable("cmdline", options.getCmdLine().escape(body = true))
ninja.newline()
debug("[build.ninja] Generating 'REGENERATE_BUILD' rule")
@ -166,7 +166,7 @@ proc setup(options: Options) =
debug("[build.ninja] Generating 'build.ninja' target")
ninja.build(["build.ninja"],
rule = "REGENERATE_BUILD",
inputs = [pkgInfo.nimbleFile])
inputs = [pkgInfo.nimbleFile.escape])
ninja.newline()
debug("[build.ninja] Generating 'reconfigure' target")
@ -184,7 +184,7 @@ proc setup(options: Options) =
debug("[build.ninja] Generating 'all' target")
ninja.build(["all"],
rule = "phony",
inputs = pkgInfo.bin.mapIt(it.lastPathPart.addFileExt(ExeExt)))
inputs = pkgInfo.bin.mapIt(it.lastPathPart.addFileExt(ExeExt).escape))
ninja.default(["all"])
ninja.newline()
@ -192,14 +192,14 @@ proc setup(options: Options) =
debug("[build.ninja] Generating 'test' target")
ninja.build(["test"],
rule = "nimscript",
inputs = [testerFileName],
inputs = [testerFileName.escape(body = true)],
implicit = ["PHONY", "all"])
ninja.newline()
debug("[build.ninja] Generating 'install' target")
ninja.build(["install"],
rule = "nimscript",
inputs = [installerFileName],
inputs = [installerFileName.escape(body = true)],
implicit = ["PHONY", "all"])
ninja.close()

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 Anna <cyber@sysrq.in>
# SPDX-FileCopyrightText: 2022-2023 Anna <cyber@sysrq.in>
# SPDX-License-Identifier: BSD-3-Clause
discard """
@ -11,7 +11,7 @@ build main$$$ program: nimc
import nimbs/ninjasyntax
stdout.build(@["main program"], "nimc")
stdout.build(@["main:program"], "nimc")
stdout.build(@["main$"], "nimc")
stdout.build(@["main$ program"], "nimc")
stdout.build(["main program".escape], "nimc")
stdout.build(["main:program".escape], "nimc")
stdout.build(["main$".escape], "nimc")
stdout.build(["main$ program".escape], "nimc")

View File

@ -1,8 +0,0 @@
# SPDX-FileCopyrightText: 2022 Anna <cyber@sysrq.in>
# SPDX-License-Identifier: BSD-3-Clause
import nimbs/ninjasyntax
assert escape("normal string") == "normal string"
assert escape("$300") == "$$300"
assert escape("$$") == "$$$$"

View File

@ -0,0 +1,8 @@
# SPDX-FileCopyrightText: 2022-2023 Anna <cyber@sysrq.in>
# SPDX-License-Identifier: BSD-3-Clause
import nimbs/ninjasyntax
assert escape("normal string", body = true) == "normal string"
assert escape("$300", body = true) == "$$300"
assert escape("$$", body = true) == "$$$$"