-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathbuildwasmnoise.py
380 lines (330 loc) · 12 KB
/
buildwasmnoise.py
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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
"""
Build script to compile WasmNoise from C++ to a WebAssembly Binary file
Requires Python 3
"""
import configparser
import sys
import subprocess
import os
import enum
import json
from removeextraexports import removeExtraExports
from constructautoloader import outputAutloaderFile
class BuildType(enum.IntEnum):
"""Enum for build types"""
build = 0
patch = 1
minor = 2
major = 3
buildTypeLookup = ["build", "patch", "minor", "major"]
class FunctionEnableType(enum.IntEnum):
"""Enum for function sets"""
EnableAll = 0
EnablePerlin = 1
EnablePerlinFractal = 2
EnableAllPerlin = 3
EnableSimplex = 4
EnableSimplexFractal = 5
EnableAllSimplex = 6
EnableCellular = 7
EnableCellularFractal = 8
EnableAllCellular = 9
# Lookup maps to exportNames array
enableTypeLookup = [
[0], # EnableAll has special case
[0, 2], # Enable Perlin
[0, 1, 3], # Enable Perlin Fractal
[0, 1, 2, 3], # Enable Perlin and Perlin Fractal
[0, 4], # Enable Simplex
[0, 1, 5], # Enable Simplex Fractal
[0, 1, 4, 5], # Enable Simplex and Simplex Fractal
[0, 6, 7], # Enable Cellular
[0, 6, 8], # Enable Cellular Fractal
[0, 6, 7, 8], # Enable Cellular and Cellular Fractal
]
exportNames = [
"getset", #0
"fractalGetSet", #1
"perlin", #2
"perlinFractal", #3
"simplex", #4
"simplexFractal", #5
"cellularGetSet", #6
"cellular", #7
"cellularFractal",#8
]
class TextColours:
"""Class to store Text Colours"""
Blue = '\033[94m'
Green = '\033[92m'
Red = '\033[91m'
StopColour = '\033[0m'
def getEnabledExportsSet(enableType):
"""
Constructs a set of strings by mapping the indexes in
the corresponding enableTypeLookup array to the function sets in exportNames
"""
enabledExports = set()
lookupArr = enableTypeLookup[int(enableType)]
for i in lookupArr:
enabledExports.add(exportNames[i])
return enabledExports
def getEnabledFunctions(enables):
"""
Constructs a set of enabled function groups by adding (ORing) together all the
enabled sets. Bypasses this loop if EnableAll is present.
"""
enabledFunctions = set()
if FunctionEnableType.EnableAll in enables:
return exportNames
for enable in enables:
enabledFunctions |= getEnabledExportsSet(enable)
return enabledFunctions
def runCommand(cmd):
"""
Helper to run subprocesses and catch errors they may return
"""
try:
subprocess.run(cmd, shell=True, check=True)
except subprocess.CalledProcessError as err:
print(TextColours.Red + "Error: " + ' '.join(err.cmd) + " returned code: " + str(err.returncode) + "\nCheck output above for more information, stopping build." + TextColours.StopColour)
exit()
# Args:
# --build to increment the build counter in version.ini
# --patch to increment the patch counter in version.ini and zero build
# --minor to increment the minor counter in version.ini and zero build and patch
# --major to increment the major counter in version.ini and zero the rest
def main(args):
"""
Main Function
Processes arguments passed to the script and then configures the build process
"""
recognisedBuildTypeArgs = {
"-build": BuildType.build,
"-patch": BuildType.patch,
"-minor": BuildType.minor,
"-major": BuildType.major
}
recognisedOptimisationArgs = ["-O0", "-O1", "-O2", "-O3"]
verboseArg = "-v"
recognisedEnableArgs = {
"-EnableAll": FunctionEnableType.EnableAll,
"-EnablePerlin": FunctionEnableType.EnablePerlin,
"-EnablePerlinFractal": FunctionEnableType.EnablePerlinFractal,
"-EnableAllPerlin": FunctionEnableType.EnableAllPerlin,
"-EnableSimplex": FunctionEnableType.EnableSimplex,
"-EnableSimplexFractal": FunctionEnableType.EnableSimplexFractal,
"-EnableAllSimplex": FunctionEnableType.EnableAllSimplex,
"-EnableCellular": FunctionEnableType.EnableCellular,
"-EnableCellularFractal": FunctionEnableType.EnableCellularFractal,
"-EnableAllCellular": FunctionEnableType.EnableAllCellular
}
helpArgs = ["-h", "-help", "--h", "--help", "-H", "--H"]
allowAbortArg = "-AllowAbort"
buildType = BuildType(0)
optimisationLevel = "-O3"
verboseMode = False
allowAbort = False
enableFlags = []
if(len(args) > 1):
# Check for a help arg
if any(arg in args for arg in helpArgs):
print(
"Allowed Configuration Arguments As Follows:\n",
"Build Types:\n",
"\t-build\tIncrement Build Number\n",
"\t-patch\tIncrement Patch Number\n",
"\t-minor\tIncrement Minor Number\n",
"\t-major\tIncrement Major Number\n",
"Optimisation Args:\n",
"\t -O0, -O1, -O2, -O3\n",
"Verbose Arg:\n",
"\t-v\n",
"Enable Function Sets With:\n",
"\tCombine as necessary\n",
"\t-EnableAll\t\tEnable All Function Sets\n",
"\t-EnablePerlin\t\tEnable Non-Fractal Perlin Functions Only\n",
"\t-EnablePerlinFractal\tEnable Fractal Perlin Functions Only\n",
"\t-EnableAllPerlin\tEnable All Perlin Functions (Fractal and Non-Fractal)\n",
"\t-EnableSimplex\t\tEnable Non-Fractal Simplex Functions Only\n",
"\t-EnableSimplexFractal\tEnable Fractal Simplex Functions Only\n",
"\t-EnableAllSimplex\tEnable All Simplex Functions (Fractal and Non-Fractal)\n",
"\t-EnableCellular\t\tEnable Cellular Functions\n",
"\t-EnableCellularFractal\tEnable Fractal Cellular Functions Only\n",
"\t-EnableAllCellular\tEnable All Cellular Functions (Fractal and Non-Fractal)\n",
"Allow Abort Alerts:\n",
"(Only recommended for testing and development, not for production)\n",
"\t-AllowAbort\n",
"This Help Message -\n",
"\t-h --h -H --H -help --help"
)
return
# For each arg
for arg in args:
doneWithArg = False
if "buildwasmnoise.py" in arg:
continue
# Check if it is a recognised Build Type
for recongisedArg, value in recognisedBuildTypeArgs.items():
if arg.strip() == recongisedArg:
buildType = value
doneWithArg = True
break
if doneWithArg:
continue
# Check if it is a recognised optimisation arg
for recongisedArg in recognisedOptimisationArgs:
if arg.strip() == recongisedArg:
optimisationLevel = arg
doneWithArg = True
break
if doneWithArg:
continue
# Check if it is a recognised enable arg
for recognisedArg, value in recognisedEnableArgs.items():
if arg.strip() == recognisedArg:
enableFlags.append(value)
doneWithArg = True
break
if doneWithArg:
continue
# Check if it is a verbose arg
if arg.strip() == verboseArg:
verboseMode = True
continue
# Check if it is an allowAbort arg
if arg.strip() == allowAbortArg:
allowAbort = True
continue
# Else, unrecognised arg
print("Ignoring Unrecongised Option '", arg, "'")
# Assume enable all if no flags provided
if not enableFlags:
enableFlags.append(FunctionEnableType.EnableAll)
print("Building WasmNoise, incrementing", buildTypeLookup[int(buildType)])
build(buildType, optimisationLevel, verboseMode, allowAbort, enableFlags)
def build(buildType, optLevel, verbose, allowAbort, enabledFlags):
#TODO: Break version increment off into own function for neatness
"""
Build process, multi-step
"""
iniLoc = "version.ini"
# Create a configparser and load version.ini
config = configparser.ConfigParser()
config.read(iniLoc)
# Increment the build version
config["VERSION"][buildTypeLookup[int(buildType)]] = str(
int(config["VERSION"][buildTypeLookup[buildType]])+1
)
# Reset lower values
if buildType == BuildType.major:
config["VERSION"]["minor"] = str(0)
config["VERSION"]["patch"] = str(0)
config["VERSION"]["build"] = str(0)
elif buildType == BuildType.minor:
config["VERSION"]["patch"] = str(0)
config["VERSION"]["build"] = str(0)
elif buildType == BuildType.patch:
config["VERSION"]["build"] = str(0)
# Output the new value
print("New Version: " + config["VERSION"]["major"] + '.' + config["VERSION"]["minor"] + '.' + config["VERSION"]["patch"] + '.' + config["VERSION"]["build"])
# File output name (minus file type)
outName = "wasmnoise-" + config["VERSION"]["major"] + '.' + config["VERSION"]["minor"] + '.' + config["VERSION"]["patch"]
# Save the modified file
with open(iniLoc, 'w') as configfile:
config.write(configfile)
# Prepare the enable functions array
enabledFunctions = getEnabledFunctions(enabledFlags)
enabledFuncMacros = []
for enable in enabledFunctions:
print(TextColours.Blue + "Enabling function set: " + enable + TextColours.StopColour)
# Load the exports json file and collect macros for enabled function sets
exportsFile = open("wasmnoiseexports.json", "r")
exports = json.load(exportsFile)
for enabledFuncSet in enabledFunctions:
try:
enabledFuncMacros.append(exports["exports"][enabledFuncSet]["macro"])
except KeyError:
continue
# Grab all code files from source directory
codefiles = []
for file in os.listdir("source"):
if(file.endswith(".cpp") or file.endswith(".c")): # Is .c necessary?
codefiles.append(file)
# Assemble build commands
binLoc = "bin/" + outName + '.b' + config["VERSION"]["build"]
llvmlinkOut = outName+".bc"
llcOut = outName+".s"
s2wasmOut = outName+".wat"
processedWat = outName+".cleanexports.wat"
wat2wasmOut = outName+".wasm"
wasmoptOut = outName+".opt.wasm"
optimisationLevel = optLevel
clangCmd = [
"clang++",
"--target=wasm32",
"-emit-llvm",
"-std=c++17",
optimisationLevel,
"-c",
"-I..\\..\\..\\wasm-stdlib-hack\\include\\libc",
"../../source/*.cpp",
"-pedantic",
"-Wall",
"-Wextra"
]
for macro in enabledFuncMacros:
clangCmd.append(macro)
if allowAbort:
clangCmd.append("-DWN_ALLOW_ABORT")
if verbose:
clangCmd.append("-v")
llcCmd = [
"llc",
"-asm-verbose=false",
optimisationLevel,
"-o", llcOut,
llvmlinkOut
]
s2wasmCmd = [
"s2wasm",
"-s", "524288",
"--import-memory",
llcOut, ">", s2wasmOut
]
wat2wasmCmd = ["wat2wasm", processedWat, "-o", wat2wasmOut]
wasmoptCmd = ["wasm-opt", optimisationLevel, wat2wasmOut, "-o", wasmoptOut]
# Make sure the build directory exists
os.makedirs(binLoc, exist_ok=True)
# Change the working directory to the build directory
os.chdir(binLoc)
# Run build commands
print(TextColours.Blue + "Compiling With clang..." + TextColours.StopColour)
runCommand(clangCmd)
# The llvm-link command requires knowledge of the output from the clang command
# so we assemble it here
llvmlinkCmd = ["llvm-link", "-v", "-o", llvmlinkOut]
for file in os.listdir('.'):
if file.endswith(".bc"):
llvmlinkCmd.append(file)
# Include memory.bc
llvmlinkCmd.append("../../source/memory-bitcode/memory.bc")
print(TextColours.Blue + "Linking with llvm-link..." + TextColours.StopColour)
runCommand(llvmlinkCmd)
print(TextColours.Blue + "Converting to S-expressions with llc..." + TextColours.StopColour)
runCommand(llcCmd)
print(TextColours.Blue + "Converting S-expressions to wat..." + TextColours.StopColour)
runCommand(s2wasmCmd)
# Remove extra exports from the wat file before compiling
print(TextColours.Blue + "Removing unwanted exports from wat file..." + TextColours.StopColour)
removeExtraExports(s2wasmOut, enabledFunctions, exports)
print(TextColours.Blue + "Compiling wat to wasm..." + TextColours.StopColour)
runCommand(wat2wasmCmd)
print(TextColours.Blue + "Passing compiled wasm through wasm-opt to try to achieve faster and smaller binary..." + TextColours.StopColour)
runCommand(wasmoptCmd)
print(TextColours.Green + "Wasm compiled successfully! " + wat2wasmOut + " file now located at " + binLoc + TextColours.StopColour)
print(TextColours.Blue + "Writing Autoloader Script..." + TextColours.StopColour)
outputAutloaderFile(wasmoptOut, enabledFunctions, exports)
print(TextColours.Green + "wasmnoise.autoloader.js written successfully!" + TextColours.StopColour)
if __name__ == "__main__":
main(sys.argv)