diff --git a/.dockerignore b/.dockerignore index d3012846..96853ff8 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,3 @@ -dist/ node_modules/ occt-src/ **/.git diff --git a/Dockerfile b/Dockerfile index d3c5d9a6..ce77b062 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,5 @@ -FROM emscripten/emsdk:3.1.14 AS base-image +FROM emscripten/emsdk:3.1.20 +# FROM emscripten/emsdk:3.1.20 AS base-image RUN \ apt update -y && \ @@ -54,16 +55,17 @@ WORKDIR /src/ ARG threading=single-threaded ENV threading=$threading -FROM base-image AS test-image +# FROM base-image AS test-image RUN \ mkdir /opencascade.js/build/ && \ - mkdir /opencascade.js/dist/ && \ /opencascade.js/src/applyPatches.py -ENTRYPOINT ["/opencascade.js/src/buildFromYaml.py"] +VOLUME /opencascade.js/src +VOLUME /opencascade.js/build +VOLUME /src -FROM test-image AS custom-build-image +# FROM test-image AS custom-build-image RUN \ /opencascade.js/src/generateBindings.py && \ diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..079507ed --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,10 @@ + +version: "3.9" +services: + opencascade: + build: . + volumes: + - ./build:/opencascade.js/build + - ./src:/opencascade.js/src + - ./dist:/src + diff --git a/src/bindings.py b/src/bindings.py index 72b49c70..5a19a95a 100644 --- a/src/bindings.py +++ b/src/bindings.py @@ -1,5 +1,6 @@ import clang.cindex import re +import sys from wasmGenerator.Common import SkipException, isAbstractClass, getMethodOverloadPostfix from filter.filterClasses import filterClass @@ -36,11 +37,10 @@ def shouldProcessClass(child: clang.cindex.Cursor, occtBasePath: str): child.kind == clang.cindex.CursorKind.CLASS_DECL or child.kind == clang.cindex.CursorKind.STRUCT_DECL ): - baseSpec = list(filter(lambda x: x.kind == clang.cindex.CursorKind.CXX_BASE_SPECIFIER and x.access_specifier == clang.cindex.AccessSpecifier.PUBLIC, child.get_children())) - if len(baseSpec) > 1: - print("cannot handle multiple base classes (" + child.spelling + ")") - return False - + # baseSpec = list(filter(lambda x: x.kind == clang.cindex.CursorKind.CXX_BASE_SPECIFIER and x.access_specifier == clang.cindex.AccessSpecifier.PUBLIC, child.get_children())) + # if len(baseSpec) > 1: + # print("cannot handle multiple base classes (" + child.spelling + ")") + # return False return True return False @@ -399,8 +399,10 @@ def generateInvocationArgs(x): if method.access_specifier == clang.cindex.AccessSpecifier.PUBLIC and method.kind == clang.cindex.CursorKind.FIELD_DECL: if method.type.kind == clang.cindex.TypeKind.CONSTANTARRAY: print("Cannot handle array properties, skipping " + className + "::" + method.spelling) + sys.stdout.flush() elif not method.type.get_pointee().kind == clang.cindex.TypeKind.INVALID: print("Cannot handle pointer properties, skipping " + className + "::" + method.spelling) + sys.stdout.flush() else: output += f"{indent(2)}.property(\"{method.spelling}\", &{className}::{method.spelling})\n" return output @@ -462,15 +464,23 @@ def __init__( def processClass(self, theClass, templateDecl = None, templateArgs = None): output = "" baseSpec = list(filter(lambda x: x.kind == clang.cindex.CursorKind.CXX_BASE_SPECIFIER and x.access_specifier == clang.cindex.AccessSpecifier.PUBLIC, theClass.get_children())) + name = getClassTypeName(theClass, templateDecl) baseClassDefinition = "" if len(baseSpec) > 0: if any(x in baseSpec[0].type.spelling for x in [":", "<"]): print("Unsupported character for base class \"" + baseSpec[0].type.spelling + "\" (" + theClass.spelling + ")") + sys.stdout.flush() else: - baseClassDefinition = " extends " + baseSpec[0].type.spelling + if len(baseSpec) == 1: + baseClassDefinition = " extends " + baseSpec[0].type.spelling + else: + classes = ", ".join(map(lambda x: x.type.spelling, baseSpec)) + baseClassDefinition = " extends __" + name + output += "class __" + name + " {}\n" + output += "interface __" + name + " extends " + classes + " {}\n" + output += "applyMixins(__" + name + ", [" + classes + "]);\n" # self.addImportIfWeHaveTo(baseSpec[0].type.spelling) - name = getClassTypeName(theClass, templateDecl) output += "export declare class " + name + baseClassDefinition + " {\n" self.exports.append(name) @@ -546,6 +556,7 @@ def getTypescriptDefFromResultType(self, res, templateDecl = None, templateArgs resTypeName = resTypedefType if resTypeName == "" or "(" in resTypeName or ":" in resTypeName or "<" in resTypeName: print("could not generate proper types for type name '" + resTypeName + "', using 'any' instead.") + sys.stdout.flush() resTypeName = "any" return resTypeName @@ -555,6 +566,7 @@ def getTypescriptDefFromArg(self, arg, suffix = "", templateDecl = None, templat argTypeName = self.convertBuiltinTypes(argTypeName) if argTypeName == "" or "(" in argTypeName or ":" in argTypeName: print("could not generate proper types for type name '" + argTypeName + "', using 'any' instead.") + sys.stdout.flush() argTypeName = "any" argname = (arg.spelling if not arg.spelling == "" else ("a" + str(suffix))) diff --git a/src/buildFromYaml.py b/src/buildFromYaml.py index 19ede080..a5f2e270 100755 --- a/src/buildFromYaml.py +++ b/src/buildFromYaml.py @@ -1,6 +1,7 @@ #!/usr/bin/python3 import os +import sys import subprocess import json from itertools import chain @@ -30,12 +31,14 @@ except Exception: pass -generateCustomCodeBindings(buildConfig["additionalCppCode"]) -compileCustomCodeBindings({ - "threading": os.environ['threading'], -}) +# generateCustomCodeBindings(buildConfig["additionalCppCode"]) +# compileCustomCodeBindings({ +# "threading": os.environ['threading'], +# }) def verifyBinding(binding) -> bool: + print("Verify Binding: " + binding["symbol"]) + sys.stdout.flush() for dirpath, dirnames, filenames in os.walk(libraryBasePath + "/bindings"): for item in filenames: if item.endswith(".cpp.o") and binding["symbol"] == item[:-6]: @@ -101,6 +104,7 @@ def getAdditionalBindCodeO(): return None additionalBindCodeO = getAdditionalBindCodeO() print("Running build: " + build["name"]) + sys.stdout.flush() bindingsO = [] for dirpath, dirnames, filenames in os.walk(libraryBasePath + "/bindings"): for item in filenames: @@ -115,20 +119,23 @@ def getAdditionalBindCodeO(): continue if item.endswith(".o"): sourcesO.append(dirpath + "/" + item) + outFileName = os.getcwd() + "/" + build["name"] subprocess.check_call([ "emcc", "-lembind", ("" if additionalBindCodeO is None else additionalBindCodeO), *bindingsO, *sourcesO, - "-o", os.getcwd() + "/" + build["name"], + "-o", outFileName, "-pthread" if os.environ["threading"] == "multi-threaded" else "", *build["emccFlags"], ]) - print("Build finished") + print("Build finished: " + outFileName) runBuild(buildConfig["mainBuild"]) for extraBuild in buildConfig["extraBuilds"]: runBuild(extraBuild) if buildConfig["generateTypescriptDefinitions"]: + print("Generate Typescript Definitions") + sys.stdout.flush() typescriptDefinitionOutput = "" typescriptExports = [] for dts in typescriptDefinitions: @@ -295,8 +302,25 @@ def getAdditionalBindCodeO(): "}\n\n" + \ "\nexport type OpenCascadeInstance = {FS: typeof FS} & {\n " + ";\n ".join(map(lambda x: x["export"] + ((": typeof " + x["export"]) if x["kind"] == "class" else (": " + x["export"])), typescriptExports)) + ";\n" + \ "};\n\n" + \ + "\n\n" + \ + "function applyMixins(derivedCtor: any, constructors: any[]) {\n" + \ + " constructors.forEach((baseCtor) => {\n" + \ + " Object.getOwnPropertyNames(baseCtor.prototype).forEach((name) => {\n" + \ + " Object.defineProperty(\n" + \ + " derivedCtor.prototype,\n" + \ + " name,\n" + \ + " Object.getOwnPropertyDescriptor(baseCtor.prototype, name) ||\n" + \ + " Object.create(null)\n" + \ + " );\n" + \ + " });\n" + \ + " });\n" + \ + "}\n" + \ + "\n\n" + \ "declare function init(): Promise;\n\n" + \ "export default init;\n" - typescriptDefinitionsFile = open(os.getcwd() + "/" + os.path.splitext(buildConfig["mainBuild"]["name"])[0] + ".d.ts", "w") + tsFilename = os.getcwd() + "/" + os.path.splitext(buildConfig["mainBuild"]["name"])[0] + ".d.ts" + typescriptDefinitionsFile = open(tsFilename, "w") typescriptDefinitionsFile.write(typescriptDefinitionOutput) + print("Typescript: " + tsFilename) + sys.stdout.flush() diff --git a/src/compileBindings.py b/src/compileBindings.py index 75f9d675..0a2ccdfe 100755 --- a/src/compileBindings.py +++ b/src/compileBindings.py @@ -1,6 +1,7 @@ #!/usr/bin/python3 import os +import sys from Common import ocIncludePaths, additionalIncludePaths import subprocess import multiprocessing @@ -13,6 +14,7 @@ def buildOneFile(args, item): if not os.path.exists(item + ".o"): print("building " + item) + sys.stdout.flush() command = [ "emcc", "-flto", @@ -36,6 +38,7 @@ def buildOneFile(args, item): ]) else: print("file " + item + ".o already exists, skipping") + sys.stdout.flush() def compileCustomCodeBindings(args): filesToBuild = [] diff --git a/src/compileSources.py b/src/compileSources.py index 5560d52a..62c0dcba 100755 --- a/src/compileSources.py +++ b/src/compileSources.py @@ -1,6 +1,7 @@ #!/usr/bin/python3 import os +import sys import subprocess import multiprocessing @@ -72,12 +73,14 @@ def buildObjectFiles(file, args): if not os.path.exists(libraryBasePath + "/" + relativeFile + ".o"): print("Building " + relativeFile) + sys.stdout.flush() subprocess.check_call([ *command, "-o", libraryBasePath + "/" + relativeFile + ".o", ]) else: print(relativeFile + ".o already exists, skipping") + sys.stdout.flush() allModules = {} for dirpath, dirnames, filenames in os.walk(sourceBasePath): diff --git a/src/generateBindings.py b/src/generateBindings.py index b28da449..f1ff2a43 100755 --- a/src/generateBindings.py +++ b/src/generateBindings.py @@ -4,6 +4,7 @@ from bindings import EmbindBindings, TypescriptBindings, shouldProcessClass import clang.cindex import os +import sys import errno from filter.filterTypedefs import filterTypedef from filter.filterEnums import filterEnum @@ -85,14 +86,17 @@ def processChildBatch(customCode, generator, buildType: str, extension: str, fil if not os.path.exists(filename): print("Processing " + child.spelling) + sys.stdout.flush() try: output = processFunction(tu, preamble, child, typedefGenerator(tu), templateTypedefGenerator(tu)) bindingsFile = open(filename, "w") bindingsFile.write(output) except SkipException as e: print(str(e)) + sys.stdout.flush() else: print("file " + child.spelling + ".cpp already exists, skipping") + sys.stdout.flush() def split(a, n): k, m = divmod(len(a), n)