-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathkoch.nim
223 lines (184 loc) · 6.31 KB
/
koch.nim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
## Tool for performing various build and testing related actions for the
## repository. *Koch* is a German noun meaning *cook* or *chef* in English.
import std/[sequtils, strutils, tables, os, osproc, parseopt]
const
HelpText = """
Usage:
koch [options] <command>
Options:
--nim:path use the specified NimSkull compiler
Commands:
all [args] builds all programs
single <name> [args] builds the single program with the given name
generate [dir] generates the various language-related modules
build-defs verifies the language definitions and generates
the textual representation for them
"""
Programs: seq[(string, string, bool, bool)] = @[
("tester", "tools/tester.nim", true, true),
("passtool", "tools/passtool/passtool.nim", true, true),
("repl", "phy/repl.nim", false, true),
("phy", "phy/phy.nim", false, true),
("skully", "skully/skully.nim", true, false)
# ^^ excluded from 'all' because the program takes too long to compile
]
## program name, module path, whether the program doesn't depend on
## generated modules, and whether the program is built with 'all'
DefaultGenerated = "generated"
## the default path for the generated modules
var
nimExe = findExe("nim")
verbose = true
proc run(exe: sink string, args: varargs[string]): bool =
if verbose:
echo "run: ", exe, " ", args.join(" ")
let p = startProcess(exe, args=args, options={poParentStreams, poUsePath})
result = p.waitForExit(-1) == 0
p.close()
proc require(x: bool) =
if not x:
echo "failure"
quit(1)
proc compile(file: sink string, name: string, extra: varargs[string]): bool =
## Compiles the given NimSkull `file` into an exectuable located in the bin/
## directory, using `name` as the name.
var args = @["c", "--nimcache:build/cache/" & name,
"-o:bin/" & addFileExt(name, ExeExt)]
args.add extra
args.add file
result = run(nimExe, args)
proc check(file: sink string, extra: varargs[string]): bool =
## Runs the ``check`` command on the given NimSkull `file`.
var args = @["check"]
args.add extra
args.add file
result = run(nimExe, args)
proc saneSplit(s: string): seq[string] =
## Compared to the normal split, returns an empty sequence for an empty
## string.
let s = strip(s)
if s.len > 0: split(s, ' ')
else: @[]
# helpers:
proc generateModules(dir: string) =
## Invokes the passtool for generating the language-derived modules.
let passtool = addFileExt("bin/passtool", ExeExt)
createDir(dir)
# generate the modules:
require run(passtool, "gen-checks", "languages", "lang30", "passes/syntax",
dir / "*_checks.nim")
require run(passtool, "gen-checks", "languages", "specification",
"passes/syntax_source", dir / "source_checks.nim")
proc buildSingle(args: string): bool
proc regenerate() =
## Makes sure the generated modules are up-to-date.
if not dirExists(DefaultGenerated):
# assume that the 'generated' folder existing means that it's up-to-date.
# This is usually *not* correct, but it's the simplest heuristic we have
discard buildSingle("passtool -d:release")
generateModules(DefaultGenerated)
# command implementations:
proc buildSingle(args: string): bool =
## Builds the single program specified by `args`.
var args = args.saneSplit()
if args.len == 0:
return false # not enough arguments, show the help
let progName = args[0]
args.delete(0)
result = true
for (name, path, standalone, _) in Programs.items:
if name == progName:
if not standalone:
# depends on some generated modules; first make sure they're
# up-to-date
regenerate()
if not compile(getCurrentDir() / path, name, args):
echo "Failure"
quit(1)
return
# report a "not found" error
echo "no program with name: '", progName, "' exists. Candidates are:"
echo " ", mapIt(Programs, it[0]).join(", ")
quit(1)
proc buildAll(args: string): bool =
## Builds all programs, passing `args` along to the compiler.
let extra = args.saneSplit()
# build all standalone programs first:
for (name, path, standalone, inAll) in Programs.items:
if standalone and inAll:
if not compile(getCurrentDir() / path, name, extra):
echo "Failure"
quit(1)
# then generate the language-derived modules:
generateModules(DefaultGenerated)
# finally, build the remaining programs:
for (name, path, standalone, inAll) in Programs.items:
if not standalone and inAll:
if not compile(getCurrentDir() / path, name, extra):
echo "Failure"
quit(1)
result = true
proc generate(args: string): bool =
## Invokes the passtool for generating the language-related modules.
let args = saneSplit(args)
if args.len > 1:
echo "too many arguments"
return false
# the passtool binary might be out-of-date; it's better to always compile it
result = buildSingle("passtool -d:release")
if not result:
return
generateModules():
if args.len == 1: args[0] else: DefaultGenerated
result = true
proc buildDefs(args: string): bool =
## Handles verifying the language definitions and producing the artifacts
## based on them.
if args.len > 0:
return false
# there's nothing to do with the compiled language definition, making sure
# the macro succeeds is enough
if not check(getCurrentDir() / "languages" / "source.nim"):
echo "Failure"
quit(1)
result = true
proc showHelp(): bool =
## Shows the help text.
echo HelpText
result = true
# main command processing:
var
opts = initOptParser(getExecArgs())
success = false
while true:
opts.next()
case opts.kind
of cmdLongOption, cmdShortOption:
case normalize(opts.key)
of "nim":
nimExe = opts.val
else:
echo "Unknown option: ", normalize(opts.key)
break
of cmdArgument:
success =
case normalize(opts.key)
of "all":
buildAll(opts.cmdLineRest)
of "single":
buildSingle(opts.cmdLineRest)
of "generate":
generate(opts.cmdLineRest)
of "build-defs":
buildDefs(opts.cmdLineRest)
of "help":
showHelp()
else:
echo "Unknown command: ", normalize(opts.key)
false
break
of cmdEnd:
break
if not success:
echo HelpText
quit(1)