diff --git a/scripts/bindgen.py b/scripts/bindgen.py index 0f81a54..2eda83d 100755 --- a/scripts/bindgen.py +++ b/scripts/bindgen.py @@ -1,131 +1,135 @@ #!/usr/bin/env python3 import sys -if len(sys.argv) < 2: - print("bindgen require an api name\n") - exit(-1); +def bindgen(apiName, cdir): + inPath = cdir + '/bindgen_' + apiName + '_api.txt' + outPath = cdir + '/bindgen_' + apiName + '_api.c' -apiName = sys.argv[1] -cdir = '' + inFile = open(inPath, 'r') + outFile = open(outPath, 'w') -if len(sys.argv) > 2: - cdir = sys.argv[2] + stubs = [] + links = [] -inPath = cdir + '/bindgen_' + apiName + '_api.txt' -outPath = cdir + '/bindgen_' + apiName + '_api.c' + def gen_stub(name, sig, native_name): + if native_name == None: + native_name = name -inFile = open(inPath, 'r') -outFile = open(outPath, 'w') + src = 'const void* ' + name + '_stub(IM3Runtime runtime, IM3ImportContext _ctx, uint64_t * _sp, void * _mem)\n' + src += '{\n' + spIndex = 0 -stubs = [] -links = [] + parsingRet = True + retCount = 0 + argCount = 0 + retString = '' + argString = '' -def gen_stub(name, sig, native_name): - if native_name == None: - native_name = name - - src = 'const void* ' + name + '_stub(IM3Runtime runtime, IM3ImportContext _ctx, uint64_t * _sp, void * _mem)\n' - src += '{\n' - spIndex = 0 - - parsingRet = True - retCount = 0 - argCount = 0 - retString = '' - argString = '' - - for index, c in enumerate(sig): - if parsingRet: - if retCount > 1: - print('unsupported multiple return types\n') - break - if c == '(': - parsingRet = False - continue - elif c == ')': - print('unexpected ) while parsing return type\n') - break; - elif c == 'v': - continue - elif c == 'i': - retString = '*((i32*)&_sp[0]) = ' - elif c == 'I': - retString = '*((i64*)&_sp[0]) = ' - elif c == 'f': - retString = '*((f32*)&_sp[0]) = ' - elif c == 'F': - retString = '*((f64*)&_sp[0]) = ' - elif c == 'p': - print('returning pointers is not supported yet\n') - break + for index, c in enumerate(sig): + if parsingRet: + if retCount > 1: + print('unsupported multiple return types\n') + break + if c == '(': + parsingRet = False + continue + elif c == ')': + print('unexpected ) while parsing return type\n') + break; + elif c == 'v': + continue + elif c == 'i': + retString = '*((i32*)&_sp[0]) = ' + elif c == 'I': + retString = '*((i64*)&_sp[0]) = ' + elif c == 'f': + retString = '*((f32*)&_sp[0]) = ' + elif c == 'F': + retString = '*((f64*)&_sp[0]) = ' + elif c == 'p': + print('returning pointers is not supported yet\n') + break + else: + print('unrecognized type ' + c + ' in procedure return\n') + break + retCount += 1 else: - print('unrecognized type ' + c + ' in procedure return\n') - break - retCount += 1 - else: - argIndex = argCount + retCount - if c == ')': - break - elif c == 'v': - break - elif c == 'i': - argString += '*(i32*)&_sp[' + str(argIndex) + ']' - elif c == 'I': - argString += '*(i64*)&_sp[' + str(argIndex) + ']' - elif c == 'f': - argString += '*(f32*)&_sp[' + str(argIndex) + ']' - elif c == 'F': - argString += '*(f64*)&_sp[' + str(argIndex) + ']' - elif c == 'p': - argString += '(void*)((char*)_mem + *(i32*)&_sp[' + str(argIndex) + '])' + argIndex = argCount + retCount + if c == ')': + break + elif c == 'v': + break + elif c == 'i': + argString += '*(i32*)&_sp[' + str(argIndex) + ']' + elif c == 'I': + argString += '*(i64*)&_sp[' + str(argIndex) + ']' + elif c == 'f': + argString += '*(f32*)&_sp[' + str(argIndex) + ']' + elif c == 'F': + argString += '*(f64*)&_sp[' + str(argIndex) + ']' + elif c == 'p': + argString += '(void*)((char*)_mem + *(i32*)&_sp[' + str(argIndex) + '])' + else: + print('unrecognized type ' + c + ' in procedure signature\n') + break + + if index+2 < len(sig): + argString += ', ' + argCount += 1 + + src += '\t' + retString + native_name + '(' + argString + ');\n' + src += '\treturn(0);\n' + src += '}\n' + stubs.append(src) + + def gen_link(name, sig): + m3_Sig = '' + for c in sig: + if c == 'p': + m3_Sig += 'i' else: - print('unrecognized type ' + c + ' in procedure signature\n') - break + m3_Sig += c - if index+2 < len(sig): - argString += ', ' - argCount += 1 + src = '\tres = m3_LinkRawFunction(module, "*", "' + name + '", "' + m3_Sig + '", ' + name + '_stub);\n' + src += '\tif(res != m3Err_none && res != m3Err_functionLookupFailed) { log_error("error: %s\\n", res); return(-1); }\n\n' + links.append(src) - src += '\t' + retString + native_name + '(' + argString + ');\n' - src += '\treturn(0);\n' - src += '}\n' - stubs.append(src) + for line in inFile: + if line.isspace(): + continue + desc = line.split() -def gen_link(name, sig): - m3_Sig = '' - for c in sig: - if c == 'p': - m3_Sig += 'i' - else: - m3_Sig += c + gen_stub(desc[0], desc[1], desc[2] if len(desc) > 2 else None) + gen_link(desc[0], desc[1]) - src = '\tres = m3_LinkRawFunction(module, "*", "' + name + '", "' + m3_Sig + '", ' + name + '_stub);\n' - src += '\tif(res != m3Err_none && res != m3Err_functionLookupFailed) { log_error("error: %s\\n", res); return(-1); }\n\n' - links.append(src) + linkProc = 'int bindgen_link_' + apiName + '_api(IM3Module module)\n' + linkProc += '{\n' + linkProc += '\tM3Result res;\n' -for line in inFile: - if line.isspace(): - continue - desc = line.split() + for link in links: + linkProc += link - gen_stub(desc[0], desc[1], desc[2] if len(desc) > 2 else None) - gen_link(desc[0], desc[1]) + linkProc += '\treturn(0);\n' + linkProc += '}\n' -linkProc = 'int bindgen_link_' + apiName + '_api(IM3Module module)\n' -linkProc += '{\n' -linkProc += '\tM3Result res;\n' + for stub in stubs: + outFile.write(stub) -for link in links: - linkProc += link + outFile.write('\n') + outFile.write(linkProc) -linkProc += '\treturn(0);\n' -linkProc += '}\n' + inFile.close() + outFile.close() -for stub in stubs: - outFile.write(stub) +if __name__ == "__main__": + if len(sys.argv) < 2: + print("bindgen require an api name\n") + exit(-1) -outFile.write('\n') -outFile.write(linkProc) + apiName = sys.argv[1] + cdir = '' -inFile.close() -outFile.close() + if len(sys.argv) > 2: + cdir = sys.argv[2] + + bindgen(apiName, cdir) diff --git a/scripts/bindgen2.py b/scripts/bindgen2.py index 48baa0e..3dde8b1 100644 --- a/scripts/bindgen2.py +++ b/scripts/bindgen2.py @@ -3,31 +3,6 @@ from argparse import ArgumentParser import json -parser = ArgumentParser(prog='bindgen.py') -parser.add_argument('api') -parser.add_argument('spec') -parser.add_argument('-g', '--guest-stubs') -parser.add_argument('--guest-include') -parser.add_argument('--wasm3-bindings') - -args = parser.parse_args() - -apiName = args.api -spec = args.spec -guest_stubs_path = args.guest_stubs -if guest_stubs_path == None: - guest_stubs_path = 'bindgen_' + apiName + '_guest_stubs.c' - -wasm3_bindings_path = args.wasm3_bindings -if wasm3_bindings_path == None: - wasm3_bindings_path = 'bindgen_' + apiName + '_wasm3_bindings.c' - -host_bindings = open(wasm3_bindings_path, 'w') -guest_bindings = None - -specFile = open(spec, 'r') -data = json.load(specFile) - def needs_arg_ptr_stub(decl): res = (decl['ret']['tag'] == 'S') for arg in decl['args']: @@ -35,167 +10,205 @@ def needs_arg_ptr_stub(decl): res = True return(res) -for decl in data: - if needs_arg_ptr_stub(decl): - guest_bindings = open(guest_stubs_path, 'w') - if args.guest_include != None: - s = '#include"' + args.guest_include + '"\n\n' - print(s, file=guest_bindings) - break +def bindgen2(apiName, spec, **kwargs): + guest_stubs_path = kwargs["guest_stubs"] + guest_include = kwargs.get("guest-include") + wasm3_bindings_path = kwargs["wasm3_bindings"] -for decl in data: + host_bindings = open(wasm3_bindings_path, 'w') + guest_bindings = None - name = decl['name'] - cname = decl.get('cname', name) + specFile = open(spec, 'r') + data = json.load(specFile) - if needs_arg_ptr_stub(decl): - argPtrStubName = name + '_argptr_stub' - # pointer arg stub declaration - s = '' - if decl['ret']['tag'] == 'S': - s += 'void' - else: - s += decl['ret']['name'] + for decl in data: + if needs_arg_ptr_stub(decl): + guest_bindings = open(guest_stubs_path, 'w') + if guest_include != None: + s = '#include"' + guest_include + '"\n\n' + print(s, file=guest_bindings) + break - s += ' ORCA_IMPORT(' + argPtrStubName + ') (' + for decl in data: - if decl['ret']['tag'] == 'S': - s += decl['ret']['name'] + '* __retArg' - if len(decl['args']) > 0: - s += ', ' + name = decl['name'] + cname = decl.get('cname', name) - for i, arg in enumerate(decl['args']): - s += arg['type']['name'] - if arg['type']['tag'] == 'S': - s += '*' - s += ' ' + arg['name'] - if i+1 < len(decl['args']): - s += ', ' - s += ');\n\n' - - # forward function to pointer arg stub declaration - s += decl['ret']['name'] + ' ' + name + '(' - for i, arg in enumerate(decl['args']): - s += arg['type']['name'] + ' ' + arg['name'] - if i+1 < len(decl['args']): - s += ', ' - s += ')\n' - s += '{\n' - s += '\t' - if decl['ret']['tag'] == 'S': - s += decl['ret']['name'] + ' __ret;\n\t' - elif decl['ret']['tag'] != 'v': - s += decl['ret']['name'] - s += ' __ret = ' - s += argPtrStubName + '(' - - if decl['ret']['tag'] == 'S': - s += '&__ret' - if len(decl['args']) > 0: - s += ', ' - - for i, arg in enumerate(decl['args']): - if arg['type']['tag'] == 'S': - s += '&' - - s += arg['name'] - if i+1 < len(decl['args']): - s += ', ' - s += ');\n' - if decl['ret']['tag'] != 'v': - s += '\treturn(__ret);\n' - s += '}\n\n' - - print(s, file=guest_bindings) - - # host-side stub - s = 'const void* ' + cname + '_stub(IM3Runtime runtime, IM3ImportContext _ctx, uint64_t* _sp, void* _mem)' - - gen_stub = decl.get('gen_stub', True) - if gen_stub == False: - s += ';\n\n' - else: - s += '\n{\n\t' - retTag = decl['ret']['tag'] - - if retTag == 'i': - s += '*((i32*)&_sp[0]) = ' - elif retTag == 'I': - s += '*((i64*)&_sp[0]) = ' - elif retTag == 'f': - s += '*((f32*)&_sp[0]) = ' - elif retTag == 'F': - s += '*((f64*)&_sp[0]) = ' - elif retTag == 'S': - retTypeName = decl['ret']['name'] - retTypeCName = decl['ret'].get('cname', retTypeName) - s += '*(' + retTypeCName + '*)((char*)_mem + *(i32*)&_sp[0]) = ' - - s += cname + '(' - - firstArgIndex = 0 - if retTag != 'v': - firstArgIndex = 1 - - for i, arg in enumerate(decl['args']): - typeName = arg['type']['name'] - typeCName = arg['type'].get('cname', typeName) - argTag = arg['type']['tag'] - if argTag == 'i': - s += '*(i32*)&_sp[' + str(firstArgIndex + i) + ']' - elif argTag == 'I': - s += '*(i64*)&_sp[' + str(firstArgIndex + i) + ']' - elif argTag == 'f': - s += '*(f32*)&_sp[' + str(firstArgIndex + i) + ']' - elif argTag == 'F': - s += '*(f64*)&_sp[' + str(firstArgIndex + i) + ']' - elif argTag == 'p': - s += '(void*)((char*)_mem + *(i32*)&_sp[' + str(firstArgIndex + i) + '])' - elif argTag == 'S': - s += '*(' + typeCName + '*)((char*)_mem + *(i32*)&_sp[' + str(firstArgIndex + i) + '])' + if needs_arg_ptr_stub(decl): + argPtrStubName = name + '_argptr_stub' + # pointer arg stub declaration + s = '' + if decl['ret']['tag'] == 'S': + s += 'void' else: - print('unrecognized type ' + c + ' in procedure signature\n') - break + s += decl['ret']['name'] - if i+1 < len(decl['args']): - s += ', ' + s += ' ORCA_IMPORT(' + argPtrStubName + ') (' - s += ');\n\treturn(0);\n}\n\n' + if decl['ret']['tag'] == 'S': + s += decl['ret']['name'] + '* __retArg' + if len(decl['args']) > 0: + s += ', ' + + for i, arg in enumerate(decl['args']): + s += arg['type']['name'] + if arg['type']['tag'] == 'S': + s += '*' + s += ' ' + arg['name'] + if i+1 < len(decl['args']): + s += ', ' + s += ');\n\n' + + # forward function to pointer arg stub declaration + s += decl['ret']['name'] + ' ' + name + '(' + for i, arg in enumerate(decl['args']): + s += arg['type']['name'] + ' ' + arg['name'] + if i+1 < len(decl['args']): + s += ', ' + s += ')\n' + s += '{\n' + s += '\t' + if decl['ret']['tag'] == 'S': + s += decl['ret']['name'] + ' __ret;\n\t' + elif decl['ret']['tag'] != 'v': + s += decl['ret']['name'] + s += ' __ret = ' + s += argPtrStubName + '(' + + if decl['ret']['tag'] == 'S': + s += '&__ret' + if len(decl['args']) > 0: + s += ', ' + + for i, arg in enumerate(decl['args']): + if arg['type']['tag'] == 'S': + s += '&' + + s += arg['name'] + if i+1 < len(decl['args']): + s += ', ' + s += ');\n' + if decl['ret']['tag'] != 'v': + s += '\treturn(__ret);\n' + s += '}\n\n' + + print(s, file=guest_bindings) + + # host-side stub + s = 'const void* ' + cname + '_stub(IM3Runtime runtime, IM3ImportContext _ctx, uint64_t* _sp, void* _mem)' + + gen_stub = decl.get('gen_stub', True) + if gen_stub == False: + s += ';\n\n' + else: + s += '\n{\n\t' + retTag = decl['ret']['tag'] + + if retTag == 'i': + s += '*((i32*)&_sp[0]) = ' + elif retTag == 'I': + s += '*((i64*)&_sp[0]) = ' + elif retTag == 'f': + s += '*((f32*)&_sp[0]) = ' + elif retTag == 'F': + s += '*((f64*)&_sp[0]) = ' + elif retTag == 'S': + retTypeName = decl['ret']['name'] + retTypeCName = decl['ret'].get('cname', retTypeName) + s += '*(' + retTypeCName + '*)((char*)_mem + *(i32*)&_sp[0]) = ' + + s += cname + '(' + + firstArgIndex = 0 + if retTag != 'v': + firstArgIndex = 1 + + for i, arg in enumerate(decl['args']): + typeName = arg['type']['name'] + typeCName = arg['type'].get('cname', typeName) + argTag = arg['type']['tag'] + if argTag == 'i': + s += '*(i32*)&_sp[' + str(firstArgIndex + i) + ']' + elif argTag == 'I': + s += '*(i64*)&_sp[' + str(firstArgIndex + i) + ']' + elif argTag == 'f': + s += '*(f32*)&_sp[' + str(firstArgIndex + i) + ']' + elif argTag == 'F': + s += '*(f64*)&_sp[' + str(firstArgIndex + i) + ']' + elif argTag == 'p': + s += '(void*)((char*)_mem + *(i32*)&_sp[' + str(firstArgIndex + i) + '])' + elif argTag == 'S': + s += '*(' + typeCName + '*)((char*)_mem + *(i32*)&_sp[' + str(firstArgIndex + i) + '])' + else: + print('unrecognized type ' + c + ' in procedure signature\n') + break + + if i+1 < len(decl['args']): + s += ', ' + + s += ');\n\treturn(0);\n}\n\n' + + print(s, file=host_bindings) + + # link function + s = 'int bindgen_link_' + apiName + '_api(IM3Module module)\n{\n\t' + s += 'M3Result res;\n' + + for decl in data: + name = decl['name'] + cname = decl.get('cname', name) + + if needs_arg_ptr_stub(decl): + name = name + '_argptr_stub' + + m3Sig = '' + if decl['ret']['tag'] == 'S': + m3Sig += 'v' + else: + m3Sig += decl['ret']['tag'] + + m3Sig += '(' + if decl['ret']['tag'] == 'S': + m3Sig += 'i' + for arg in decl['args']: + tag = arg['type']['tag'] + if tag == 'p' or tag == 'S': + tag = 'i' + m3Sig += tag + m3Sig += ')' + + + s += '\tres = m3_LinkRawFunction(module, "*", "' + name + '", "' + m3Sig + '", ' + cname + '_stub);\n' + s += '\tif(res != m3Err_none && res != m3Err_functionLookupFailed) { log_error("error: %s\\n", res); return(-1); }\n\n' + + + s += '\treturn(0);\n}\n' print(s, file=host_bindings) -# link function -s = 'int bindgen_link_' + apiName + '_api(IM3Module module)\n{\n\t' -s += 'M3Result res;\n' -for decl in data: - name = decl['name'] - cname = decl.get('cname', name) +if __name__ == "__main__": + parser = ArgumentParser(prog='bindgen.py') + parser.add_argument('api') + parser.add_argument('spec') + parser.add_argument('-g', '--guest-stubs') + parser.add_argument('--guest-include') + parser.add_argument('--wasm3-bindings') - if needs_arg_ptr_stub(decl): - name = name + '_argptr_stub' + args = parser.parse_args() - m3Sig = '' - if decl['ret']['tag'] == 'S': - m3Sig += 'v' - else: - m3Sig += decl['ret']['tag'] + apiName = args.api + spec = args.spec + guest_stubs_path = args.guest_stubs + if guest_stubs_path == None: + guest_stubs_path = 'bindgen_' + apiName + '_guest_stubs.c' - m3Sig += '(' - if decl['ret']['tag'] == 'S': - m3Sig += 'i' - for arg in decl['args']: - tag = arg['type']['tag'] - if tag == 'p' or tag == 'S': - tag = 'i' - m3Sig += tag - m3Sig += ')' - - - s += '\tres = m3_LinkRawFunction(module, "*", "' + name + '", "' + m3Sig + '", ' + cname + '_stub);\n' - s += '\tif(res != m3Err_none && res != m3Err_functionLookupFailed) { log_error("error: %s\\n", res); return(-1); }\n\n' - - -s += '\treturn(0);\n}\n' - -print(s, file=host_bindings) + wasm3_bindings_path = args.wasm3_bindings + if wasm3_bindings_path == None: + wasm3_bindings_path = 'bindgen_' + apiName + '_wasm3_bindings.c' + + bindgen2(apiName, spec, + guest_stubs_path=guest_stubs_path, + guest_include=args.guest_include, + wasm3_bindings_path=wasm3_bindings_path, + ) diff --git a/scripts/build_runtime.py b/scripts/build_runtime.py index eb642f9..80d7a3b 100644 --- a/scripts/build_runtime.py +++ b/scripts/build_runtime.py @@ -9,6 +9,8 @@ import subprocess from zipfile import ZipFile import checksum +from bindgen import bindgen +from bindgen2 import bindgen2 from log import * from utils import pushd, removeall @@ -28,6 +30,7 @@ def build_runtime(args): build_milepost("lib", args.release) build_wasm3(args.release) + build_orca(args.release) def build_milepost(target, release): @@ -213,6 +216,75 @@ def build_wasm3_lib_win(release): ], check=True) +def build_orca(release): + print("Building Orca...") + + os.makedirs("bin", exist_ok=True) + + if platform.system() == "Windows": + build_orca_win(release) + elif platform.system() == "Darwin": + raise "can't yet build Orca on Mac" + else: + log_error(f"can't build Orca for unknown platform '{platform.system()}'") + exit(1) + + +def build_orca_win(release): + pthread_dir = "..\\vcpkg\\packages\\pthreads_x64-windows" + + # copy libraries + shutil.copy("milepost\\bin\\milepost.dll", "bin") + shutil.copy("milepost\\bin\\milepost.dll.lib", "bin") + shutil.copy(os.path.join(pthread_dir, "bin\\pthreadVC3.dll"), "bin") + + # generate wasm3 api bindings + bindgen("core", "src") + bindgen("gles", "src") + bindgen2("canvas", "src\\canvas_api.json", + guest_stubs="sdk\\orca_surface.c", + guest_include="graphics.h", + wasm3_bindings="src\\canvas_api_bind_gen.c", + ) + bindgen2("clock", "src\\clock_api.json", + guest_stubs="sdk\\orca_clock.c", + guest_include="platform_clock.h", + wasm3_bindings="src\\clock_api_bind_gen.c", + ) + bindgen2("io", "src\\io_api.json", + guest_stubs="sdk\\io_stubs.c", + wasm3_bindings="src\\io_api_bind_gen.c", + ) + + # compile orca + pthread_include = os.path.join(pthread_dir, "include") + includes = [ + "/I", "src", + "/I", "sdk", + "/I", "ext\wasm3\source", + "/I", "milepost\src", + "/I", "milepost\ext", + "/I", pthread_include, + ] + pthread_lib = os.path.join(pthread_dir, "lib") + libs = [ + "/LIBPATH:bin", + f"/LIBPATH:{pthread_lib}", + "milepost.dll.lib", + "wasm3.lib", + "pthreadVC3.lib", + ] + + subprocess.run([ + "cl", + "/Zi", "/Zc:preprocessor", "/std:c11", + *includes, + "src\\main.c", + "/link", *libs, + "/out:bin\\orca.exe", + ], check=True) + + def ensure_programs(): if platform.system() == "Windows": try: