255 lines
7.7 KiB
Nim
255 lines
7.7 KiB
Nim
# SPDX-FileCopyrightText: 2022-2023 Anna <cyber@sysrq.in>
|
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
import std/strutils except escape
|
|
import std/[logging, os, sequtils, strtabs, strformat]
|
|
|
|
import nimbs/[common, options]
|
|
import nimbs/[ninjasyntax]
|
|
import nimbs/[
|
|
dependencyresolver,
|
|
nimbleexecutor,
|
|
packageinfo,
|
|
packagemetadata,
|
|
version
|
|
]
|
|
import nimbs/[
|
|
builderscript,
|
|
installerscript,
|
|
querytoolscript,
|
|
testerscript
|
|
]
|
|
|
|
proc processDependencies(requires: seq[string], options: Options): seq[string] =
|
|
## Checks package dependencies and returns list of paths for the Nim compiler,
|
|
## shell-quoted.
|
|
for req in requires:
|
|
let dep = parseRequires(req)
|
|
case dep.name
|
|
of "nim":
|
|
let nimVer = getNimVersion(options)
|
|
if not withinRange(nimVer, dep.ver):
|
|
raise unsatisfiedDependencyError("Unsatisfied Nim dependency")
|
|
debug("Found suitable Nim version")
|
|
else:
|
|
result.add(dep.getPath(options).quoteShell)
|
|
|
|
proc application(ninja: File, input, target: string;
|
|
paths: seq[string], options: Options) =
|
|
debug(fmt"[build.ninja] Generating target for application '{target}'")
|
|
|
|
var vars = newStringTable()
|
|
vars["target"] = "$builddir" / target.escape(body = true)
|
|
vars["sourcefile"] = input.escape(body = true)
|
|
vars["nimcache"] = options.getNimCacheBaseDir() / target
|
|
if paths.len != 0:
|
|
vars["paths"] = escape("-p:" & paths.join(" -p:"), body = true)
|
|
|
|
let jsonScript = "$nimcache" / target.addFileExt("json").escape
|
|
ninja.build([jsonScript],
|
|
rule = "genscript",
|
|
inputs = [input.escape, "$builder"],
|
|
variables = vars
|
|
)
|
|
ninja.newline()
|
|
|
|
ninja.build([target.addFileExt(ExeExt).escape],
|
|
rule = "jsonscript",
|
|
inputs = [input.escape],
|
|
implicit = [jsonScript],
|
|
variables = vars
|
|
)
|
|
ninja.newline()
|
|
|
|
proc task(ninja: File, nimsFile, taskName: string) =
|
|
debug(fmt"[build.ninja] Generating target for task '{taskName}'")
|
|
|
|
var vars = newStringTable()
|
|
vars["taskname"] = taskName
|
|
|
|
ninja.build([taskName.escape],
|
|
rule = "nimbletask",
|
|
inputs = [nimsFile.escape],
|
|
implicit = ["PHONY"],
|
|
variables = vars
|
|
)
|
|
|
|
proc setup(options: Options) =
|
|
# if true, nimble-style tests are used
|
|
var nimbleTests = false
|
|
|
|
echo "The nimbus build system"
|
|
echo "Version: " & nimbusVersion
|
|
echo "Source dir: " & options.getSourceDir()
|
|
echo "Build dir: " & options.getBuildDir()
|
|
|
|
if options.getSourceDir() == options.getBuildDir():
|
|
quit("In-source builds are not allowed")
|
|
|
|
debug("Writing query tool script")
|
|
let queryTool = open(options.getBuildDir() / queryToolFileName, fmWrite)
|
|
queryTool.writeQueryToolScript()
|
|
queryTool.close()
|
|
|
|
debug("Populating package info")
|
|
let pkgInfo = initPackageInfo(options)
|
|
echo "Project name: " & pkgInfo.name
|
|
echo "Project version: " & pkgInfo.version
|
|
echo "Nim compiler: " & options.getNimBin()
|
|
echo ""
|
|
|
|
let nimbusPriv = options.getBuildDir() / "nimbus-private"
|
|
createDir(nimbusPriv)
|
|
|
|
let depPaths = processDependencies(pkgInfo.requires, options)
|
|
|
|
let nimsFile =
|
|
nimbusPriv / splitFile(pkgInfo.nimbleFile).name.changeFileExt("nims")
|
|
debug("Copying nimble file to nimbus-private")
|
|
copyFile(pkgInfo.nimbleFile, nimsFile)
|
|
debug("Querying tasks")
|
|
let tasks = nimsFile.getTasks(options)
|
|
|
|
if options.url.len != 0:
|
|
echo "-- Generating nimblemeta.json"
|
|
let nimblemeta = open(options.getBuildDir() / packageMetadataFileName,
|
|
fmWrite)
|
|
nimblemeta.writeMetaData(options.url)
|
|
nimblemeta.close()
|
|
|
|
if pkgInfo.bin.len != 0:
|
|
echo "-- Generating builder script"
|
|
let builder = open(options.getBuildDir() / builderFileName, fmWrite)
|
|
builder.writeBuilderScript(options)
|
|
builder.close()
|
|
|
|
if "test" notin tasks and dirExists(options.getSourceDir() / "tests"):
|
|
echo "-- Generating tester script"
|
|
let tester = open(options.getBuildDir() / testerFileName, fmWrite)
|
|
tester.writeTesterScript(options)
|
|
tester.close()
|
|
nimbleTests = true
|
|
|
|
echo "-- Generating installer script"
|
|
let installer = open(options.getBuildDir() / installerFileName, fmWrite)
|
|
installer.writeInstallerScript(pkgInfo, options)
|
|
installer.close()
|
|
|
|
echo "-- Generating build.ninja"
|
|
let ninja = open(options.getBuildDir() / "build.ninja", fmWrite)
|
|
|
|
debug("[build.ninja] Writing comments")
|
|
ninja.comment("This file is autogenerated by the nimbus build system.")
|
|
ninja.comment("Do not edit by hand.")
|
|
ninja.newline()
|
|
|
|
debug("[build.ninja] Writing variables")
|
|
ninja.variable("nim", options.getNimBin().escape(body = true))
|
|
ninja.variable("nimbus", getAppFilename().escape(body = true))
|
|
ninja.variable("cmdline", options.getCmdLine().escape(body = true))
|
|
ninja.variable("builder", builderFileName.escape(body = true))
|
|
ninja.newline()
|
|
|
|
debug("[build.ninja] Generating 'REGENERATE_BUILD' rule")
|
|
ninja.rule("REGENERATE_BUILD",
|
|
command = "$nimbus $cmdline",
|
|
description = "Regenerating build files.",
|
|
pool = "console",
|
|
generator = true)
|
|
ninja.newline()
|
|
|
|
debug("[build.ninja] Generating 'nimscript' rule")
|
|
ninja.rule("nimscript",
|
|
command = "$nim --hints:off e $in",
|
|
description = "Executing NimScript file $in",
|
|
pool = "console")
|
|
ninja.newline()
|
|
|
|
if pkgInfo.bin.len != 0:
|
|
debug("[build.ninja] Generating 'genscript' rule")
|
|
ninja.rule("genscript",
|
|
command = "$nim --hints:off e $builder -T:$target $paths $sourcefile",
|
|
description = "Generating build script for Nim application $out",
|
|
depfile = "$target".addFileExt("d"),
|
|
deps = "gcc",
|
|
pool = "console")
|
|
ninja.newline()
|
|
|
|
debug("[build.ninja] Generating 'jsonscript' rule")
|
|
ninja.rule("jsonscript",
|
|
command = "$nim jsonscript --nimcache:$nimcache -o:$out $sourcefile",
|
|
description = "Compiling Nim application $out",
|
|
depfile = "$target".addFileExt("d"),
|
|
deps = "gcc",
|
|
pool = "console")
|
|
ninja.newline()
|
|
|
|
if tasks.len != 0:
|
|
# most tasks are supposed to be run from the project root
|
|
debug("[build.ninja] Generating 'nimbletask' rule")
|
|
ninja.rule("nimbletask",
|
|
command = "cd $sourcedir && $nim --hints:off $taskname $in",
|
|
description = "Executing task $taskname",
|
|
pool = "console")
|
|
ninja.newline()
|
|
|
|
debug("[build.ninja] Generating 'PHONY' target")
|
|
ninja.comment("Phony build target, always out of date")
|
|
ninja.build(["PHONY"], rule = "phony")
|
|
ninja.newline()
|
|
|
|
for taskName in tasks:
|
|
ninja.task(nimsFile, taskName)
|
|
ninja.newline()
|
|
|
|
debug("[build.ninja] Generating 'build.ninja' target")
|
|
ninja.build(["build.ninja"],
|
|
rule = "REGENERATE_BUILD",
|
|
inputs = [pkgInfo.nimbleFile.escape])
|
|
ninja.newline()
|
|
|
|
debug("[build.ninja] Generating 'reconfigure' target")
|
|
ninja.build(["reconfigure"],
|
|
rule = "REGENERATE_BUILD",
|
|
implicit = ["PHONY"])
|
|
ninja.newline()
|
|
|
|
for bin in pkgInfo.bin:
|
|
let input = pkgInfo.getSourceDir(options) / bin.addFileExt("nim")
|
|
let target = bin.lastPathPart
|
|
ninja.application(input, target, depPaths, options)
|
|
|
|
debug("[build.ninja] Generating 'all' target")
|
|
ninja.build(["all"],
|
|
rule = "phony",
|
|
inputs = pkgInfo.bin.mapIt(it.lastPathPart.addFileExt(ExeExt).escape))
|
|
ninja.default(["all"])
|
|
ninja.newline()
|
|
|
|
if nimbleTests:
|
|
debug("[build.ninja] Generating 'test' target")
|
|
ninja.build(["test"],
|
|
rule = "nimscript",
|
|
inputs = [testerFileName.escape(body = true)],
|
|
implicit = ["PHONY", "all"])
|
|
ninja.newline()
|
|
|
|
debug("[build.ninja] Generating 'install' target")
|
|
ninja.build(["install"],
|
|
rule = "nimscript",
|
|
inputs = [installerFileName.escape(body = true)],
|
|
implicit = ["PHONY", "all"])
|
|
ninja.close()
|
|
|
|
when isMainModule:
|
|
var opt = parseCmdLine()
|
|
if opt.showHelp:
|
|
writeHelp() # quits
|
|
opt.setLogger
|
|
opt.setSourceDir
|
|
opt.setBuildDir
|
|
opt.setNimBin
|
|
opt.setNimbleDir
|
|
opt.setBinDir
|
|
opt.setup()
|