diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index 6c74da542f..349e6c1cde 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -54,6 +54,7 @@ OPTION(USE_VLD "Use Visual Leak Detector for debugging memory leaks, MSVC-only." OPTION(USE_MMAP "Adds a command line options to parse IFC files from memory mapped files using Boost.Iostreams" OFF) OPTION(BUILD_SHARED_LIBS "Build IfcParse and IfcGeom as shared libs (SO/DLL)." OFF) OPTION(MSVC_PARALLEL_BUILD "Multi-threaded compilation in Microsoft Visual Studio (/MP)" OFF) +OPTION(WASM_BUILD OFF) if (${HAS_MAX}) OPTION(BUILD_IFCMAX "Build IfcMax, a 3ds Max plug-in, Windows-only." OFF) endif() @@ -149,6 +150,14 @@ UNIFY_ENVVARS_AND_CACHE(MPFR_INCLUDE_DIR) UNIFY_ENVVARS_AND_CACHE(MPFR_LIBRARY_DIR) endif() +if(WASM_BUILD) + # when using the nix/build-all.py build script we should not + # look into the sysroot for most of the dependencies but rather + # in the designated build/ folder created by the script. + set(CMAKE_FIND_ROOT_PATH_BACKUP "${CMAKE_FIND_ROOT_PATH}") + set(CMAKE_FIND_ROOT_PATH "") +endif() + if (NOT MINIMAL_BUILD AND GLTF_SUPPORT AND BUILD_CONVERT) UNIFY_ENVVARS_AND_CACHE(JSON_INCLUDE_DIR) FIND_FILE(json_hpp "json.hpp" ${JSON_INCLUDE_DIR}/nlohmann) @@ -199,7 +208,14 @@ ELSE() ENDIF() ENDIF() -set(BOOST_COMPONENTS system program_options regex thread date_time) +if (WASM_BUILD) + set(BOOST_COMPONENTS) +else() + # @todo review this, shouldn't this be all possible header-only now? + # ... or rewritten using C++17 features? + set(BOOST_COMPONENTS system program_options regex thread date_time) +endif() + if(USE_MMAP) if(MSVC) # filesystem is necessary for the utf-16 wpath @@ -210,6 +226,10 @@ if(USE_MMAP) add_definitions(-DUSE_MMAP) endif() +FIND_PACKAGE(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS}) +MESSAGE(STATUS "Boost include files found in ${Boost_INCLUDE_DIRS}") +MESSAGE(STATUS "Boost libraries found in ${Boost_LIBRARY_DIRS}") + if (NOT MINIMAL_BUILD) # libxml2 is required for IFCXML (optional) and SVGFILL (mandatory) find_package(LibXml2 REQUIRED) @@ -220,11 +240,6 @@ if (NOT MINIMAL_BUILD AND IFCXML_SUPPORT) set(SWIG_DEFINES ${SWIG_DEFINES} -DWITH_IFCXML) endif() -FIND_PACKAGE(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS}) -MESSAGE(STATUS "Boost include files found in ${Boost_INCLUDE_DIRS}") -MESSAGE(STATUS "Boost libraries found in ${Boost_LIBRARY_DIRS}") - - # Usage: # set(SOME_LIRARIES foo bar) # add_debug_variants(SOME_LIRARIES "${SOME_LIRARIES}" d) @@ -343,10 +358,21 @@ if("${libTKernelExt}" STREQUAL ".a") set(OCCT_STATIC ON) endif() +if(WASM_BUILD) + set(CMAKE_FIND_ROOT_PATH "${CMAKE_FIND_ROOT_PATH_BACKUP}") +endif() + if(OCCT_STATIC) find_package(Threads) - # OPENCASCADE_LIBRARIES repeated three times below in order to fix cyclic dependencies - use --start-group ... --end-group instead? - set(OPENCASCADE_LIBRARIES ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) + + if(WASM_BUILD) + set(OPENCASCADE_LIBRARIES ${OPENCASCADE_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) + else() + # OPENCASCADE_LIBRARIES repeated N times below in order to fix cyclic dependencies - use --start-group ... --end-group instead? + # tfk: --start-group ... --end-group didn't work on the apple linker when last tested + set(OPENCASCADE_LIBRARIES ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${OPENCASCADE_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) + endif() + if (NOT APPLE AND NOT WIN32) set(OPENCASCADE_LIBRARIES ${OPENCASCADE_LIBRARIES} "rt") endif() @@ -620,7 +646,12 @@ function(files_for_ifc_version IFC_VERSION RESULT_NAME) endfunction() if(NOT SCHEMA_VERSIONS) - set(SCHEMA_VERSIONS "2x3" "4" "4x1" "4x2" "4x3_rc1" "4x3_rc2" "4x3_rc3" "4x3_rc4" "4x3" "4x3_tc1" "4x3_add1") + if(WASM_BUILD) + # super arbitrarily try to keep size down at least a little bit + set(SCHEMA_VERSIONS "2x3" "4") + else() + set(SCHEMA_VERSIONS "2x3" "4" "4x1" "4x2" "4x3_rc1" "4x3_rc2" "4x3_rc3" "4x3_rc4" "4x3" "4x3_tc1" "4x3_add1") + endif() endif() foreach(s ${SCHEMA_VERSIONS}) @@ -697,7 +728,11 @@ if (BUILD_IFCGEOM) foreach(s ${SCHEMA_VERSIONS}) set(IFCGEOM_SCHEMA_LIBRARIES ${IFCGEOM_SCHEMA_LIBRARIES} IfcGeom_ifc${s}) endforeach() - set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES}) + if (WASM_BUILD) + set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES}) + else() + set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES} IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES}) + endif() endif() if (BUILD_CONVERT OR BUILD_IFCPYTHON) foreach(s ${SCHEMA_VERSIONS}) @@ -741,7 +776,11 @@ set(IFCPARSE_FILES ${IFCPARSE_CPP_FILES} ${IFCPARSE_H_FILES}) add_library(IfcParse ${IFCPARSE_FILES}) set_target_properties(IfcParse PROPERTIES COMPILE_FLAGS -DIFC_PARSE_EXPORTS VERSION "0.6.0" SOVERSION "0.6") -TARGET_LINK_LIBRARIES(IfcParse ${Boost_LIBRARIES} ${BCRYPT_LIBRARIES} ${LIBXML2_LIBRARIES}) +if (WASM_BUILD) + TARGET_LINK_LIBRARIES(IfcParse ${BCRYPT_LIBRARIES} ${LIBXML2_LIBRARIES}) +else() + TARGET_LINK_LIBRARIES(IfcParse ${Boost_LIBRARIES} ${BCRYPT_LIBRARIES} ${LIBXML2_LIBRARIES}) +endif() if (BUILD_IFCGEOM) @@ -753,7 +792,9 @@ set(IFCGEOM_FILES ${IFCGEOM_CPP_FILES} ${IFCGEOM_H_FILES}) foreach(s ${SCHEMA_VERSIONS}) add_library(IfcGeom_ifc${s} STATIC ${IFCGEOM_FILES}) set_target_properties(IfcGeom_ifc${s} PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS -DIfcSchema=Ifc${s}") - TARGET_LINK_LIBRARIES(IfcGeom_ifc${s} IfcParse ${OPENCASCADE_LIBRARIES}) + if (NOT WASM_BUILD) + TARGET_LINK_LIBRARIES(IfcGeom_ifc${s} IfcParse ${OPENCASCADE_LIBRARIES}) + endif() endforeach() # IfcGeom (schema agnostic) @@ -768,7 +809,11 @@ if (UNIX) find_package(Threads) endif() -TARGET_LINK_LIBRARIES(IfcGeom IfcParse ${IFCGEOM_SCHEMA_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) +if (WASM_BUILD) + TARGET_LINK_LIBRARIES(IfcGeom ${IFCGEOM_SCHEMA_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) +else() + TARGET_LINK_LIBRARIES(IfcGeom IfcParse ${IFCGEOM_SCHEMA_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) +endif() endif(BUILD_IFCGEOM) @@ -785,7 +830,12 @@ set(SERIALIZERS_S_FILES ${SERIALIZERS_S_H_FILES} ${SERIALIZERS_S_CPP_FILES}) foreach(s ${SCHEMA_VERSIONS}) add_library(Serializers_ifc${s} STATIC ${SERIALIZERS_S_FILES}) set_target_properties(Serializers_ifc${s} PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS -DIfcSchema=Ifc${s} ${CONVERT_PRECISION}") - TARGET_LINK_LIBRARIES(Serializers_ifc${s} IfcGeom ${OPENCASCADE_LIBRARIES} ${HDF5_LIBRARIES}) + + if (WASM_BUILD) + TARGET_LINK_LIBRARIES(Serializers_ifc${s} ${HDF5_LIBRARIES}) + else() + TARGET_LINK_LIBRARIES(Serializers_ifc${s} IfcGeom ${OPENCASCADE_LIBRARIES} ${HDF5_LIBRARIES}) + endif() endforeach() add_library(Serializers ${SERIALIZERS_FILES}) diff --git a/nix/build-all.py b/nix/build-all.py index b052db4ae5..a856f61ccb 100644 --- a/nix/build-all.py +++ b/nix/build-all.py @@ -26,7 +26,7 @@ # * cmake * git * bzip2 * tar * c(++) compilers * autoconf # # # # if building with USE_OCCT additionally: # -# * freetype * glx.h # +# * glx.h # # # # if building with OCCT 7.4.0 additionally: # # * libfontconfig1-dev # @@ -39,14 +39,14 @@ # # # on debian 7.8 these can be obtained with: # # $ apt-get install git gcc g++ autoconf bison bzip2 cmake # -# libfreetype6-dev mesa-common-dev libffi-dev libfontconfig1-dev # +# mesa-common-dev libffi-dev libfontconfig1-dev # # # # on ubuntu 14.04: # # $ apt-get install git gcc g++ autoconf bison make cmake # -# libfreetype6-dev mesa-common-dev libffi-dev libfontconfig1-dev # +# mesa-common-dev libffi-dev libfontconfig1-dev # # # # on OS X El Capitan with homebrew: # -# $ brew install git bison autoconf automake freetype libffi cmake # +# $ brew install git bison autoconf automake libffi cmake # # # ############################################################################### import logging @@ -76,7 +76,7 @@ PYTHON_VERSIONS = ["3.6.14", "3.7.13", "3.8.13", "3.9.11", "3.10.3", "3.11.0"] JSON_VERSION = "v3.6.1" OCE_VERSION = "0.18.3" OCCT_VERSION = "7.5.3" -BOOST_VERSION = "1.71.0" +BOOST_VERSION = "1.80.0" PCRE_VERSION = "8.41" LIBXML2_VERSION = "2.9.11" SWIG_VERSION = "4.0.2" @@ -103,6 +103,9 @@ curl = "curl" wget = "wget" strip = "strip" +explicit_targets = [s for s in sys.argv[1:] if not s.startswith("-")] +flags = set(s.lstrip('-') for s in sys.argv[1:] if s.startswith("-")) + # Helper function for coloured printing NO_COLOR = "\033[0m" # http://stackoverflow.com/questions/5947742/how-to-change-the-output-color-of-echo-in-linux @@ -135,9 +138,11 @@ if platform.system() == "Darwin": IFCOS_NUM_BUILD_PROCS = os.getenv("IFCOS_NUM_BUILD_PROCS", multiprocessing.cpu_count() + 1) -CMAKE_DIR = os.path.realpath(os.path.join("..", "cmake")) +CMAKE_DIR = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", "cmake")) -path = ["..", "build", platform.system(), platform.machine()] +build_dir = os.environ.get("BUILD_DIR", os.path.join(os.path.dirname(__file__), "..", "build")) + +path = [build_dir, platform.system(), "wasm" if "wasm" in flags else platform.machine()] if TOOLSET: path.append(TOOLSET) DEFAULT_DEPS_DIR = os.path.realpath(os.path.join(*path)) @@ -186,11 +191,12 @@ dependency_tree = { 'boost': (), 'libxml2': (), 'python': (), - 'occ': (), + 'occ': ('freetype',), 'pcre': (), 'json': (), 'hdf5': (), - 'cgal': () + 'cgal': (), + 'freetype': (), } def v(dep): @@ -199,10 +205,12 @@ def v(dep): for x in v(d): yield x -tgts = [s for s in sys.argv[1:] if not s.startswith("-")] -flags = set(s for s in sys.argv[1:] if s.startswith("-")) +if "v" in flags: + logger.setLevel(logging.DEBUG) +else: + logger.setLevel(logging.INFO) -BUILD_STATIC = not "-shared" in flags +BUILD_STATIC = "shared" not in flags ENABLE_FLAG = "--enable-static" if BUILD_STATIC else "--enable-shared" DISABLE_FLAG = "--disable-shared" if BUILD_STATIC else "--disable-static" LINK_TYPE = "static" if BUILD_STATIC else "shared" @@ -210,10 +218,15 @@ LINK_TYPE_UCFIRST = LINK_TYPE[0].upper() + LINK_TYPE[1:] LIBRARY_EXT = "a" if BUILD_STATIC else "so" PIC = "-fPIC" if BUILD_STATIC else "" -if len(tgts): - targets = set(sum((list(v(target)) for target in tgts), [])) +if any(f.startswith("py-") for f in flags): + PYTHON_VERSIONS = [pyv for pyv in PYTHON_VERSIONS if "py-%s" % "".join(pyv.split('.')[0:2]) in flags] + +if len(explicit_targets): + targets = set(sum((list(v(target)) for target in explicit_targets), [])) else: targets = set(dependency_tree.keys()) + +targets = set(t for t in targets if 'without-%s' % t.lower() not in flags) print("Building:", *sorted(targets, key=lambda t: len(list(v(t))))) @@ -237,7 +250,7 @@ if not os.path.exists(LOG_FILE): open(LOG_FILE, "w").close() logger.info(f"using command log file '{LOG_FILE}'") -def run(cmds, cwd=None): +def run(cmds, cwd=None, can_fail=False): """ Wraps `subprocess.Popen.communicate()` and logs the command being executed, @@ -254,7 +267,7 @@ def run(cmds, cwd=None): log_file_handle.close() logger.debug(f"command returned {proc.returncode}") - if proc.returncode != 0: + if proc.returncode != 0 and not can_fail: print("-" * 70) print(stderr) print("-" * 70) @@ -284,7 +297,12 @@ def run_autoconf(arg1, configure_args, cwd): run([bash, "./autogen.sh"], cwd=os.path.realpath(os.path.join(cwd, ".."))) # only run autogen.sh in the directory it is located and use cwd to achieve that in order to not mess up things # Using `sh` over `bash` fixes issues with building swig prefix = os.path.realpath(f"{DEPS_DIR}/install/{arg1}") - run(["/bin/sh", "../configure"] + configure_args + [f"--prefix={prefix}"], cwd=cwd) + + wasm = [] + if "wasm" in flags: + wasm.append("emconfigure") + + run([*wasm, "/bin/sh", "../configure"] + configure_args + [f"--prefix={prefix}"], cwd=cwd) def run_cmake(arg1, cmake_args, cmake_dir=None, cwd=None): @@ -292,7 +310,12 @@ def run_cmake(arg1, cmake_args, cmake_dir=None, cwd=None): P = ".." else: P = cmake_dir - run(["cmake", P] + cmake_args + [f"-DCMAKE_BUILD_TYPE={BUILD_CFG}"], cwd=cwd) + + wasm = [] + if "wasm" in flags: + wasm.append("emcmake") + + run([*wasm, "cmake", P, *cmake_args, f"-DCMAKE_BUILD_TYPE={BUILD_CFG}"], cwd=cwd) def git_clone_or_pull_repository(clone_url, target_dir, revision=None): @@ -374,12 +397,15 @@ def build_dependency(name, mode, build_tool_args, download_url, download_name, d urlretrieve(url, os.path.join(extract_dir, path)) if patch is not None: - patch_abs = os.path.abspath(os.path.join(os.path.dirname(__file__), patch)) - if os.path.exists(patch_abs): - try: run(["patch", "-p1", "--batch", "--forward", "-i", patch_abs], cwd=extract_dir) - except Exception as e: - # Assert that the patch has already been applied - run(["patch", "-p1", "--batch", "--reverse", "--dry-run", "-i", patch_abs], cwd=extract_dir) + if isinstance(patch, str): + patch = [patch] + for p in patch: + patch_abs = os.path.abspath(os.path.join(os.path.dirname(__file__), p)) + if os.path.exists(patch_abs): + try: run(["patch", "-p1", "--batch", "--forward", "-i", patch_abs], cwd=extract_dir) + except Exception as e: + # Assert that the patch has already been applied + run(["patch", "-p1", "--batch", "--reverse", "--dry-run", "-i", patch_abs], cwd=extract_dir) if mode == "ctest": run(["ctest", "-S", "HDF5config.cmake,BUILD_GENERATOR=Unix", "-C", BUILD_CFG, "-V", "-O", "hdf5.log"], cwd=extract_dir) @@ -410,7 +436,7 @@ def build_dependency(name, mode, build_tool_args, download_url, download_name, d logger.info(f"\rConfiguring {name}...") run([bash, "./bootstrap.sh"], cwd=extract_dir) logger.info(f"\rBuilding {name}... ") - run(["./b2", f"-j{IFCOS_NUM_BUILD_PROCS}"] + build_tool_args, cwd=extract_dir) + run(["./b2", f"-j{IFCOS_NUM_BUILD_PROCS}"] + build_tool_args, cwd=extract_dir, can_fail="wasm" in flags) logger.info(f"\rInstalling {name}... ") shutil.copytree(os.path.join(extract_dir, "boost"), os.path.join(DEPS_DIR, "install", f"boost-{BOOST_VERSION}", "boost")) logger.info(f"\rInstalled {name} \n") @@ -421,11 +447,13 @@ cecho("Collecting dependencies:", GREEN) # TODO: This is untested ADDITIONAL_ARGS = [] -BOOST_ADDRESS_MODEL = [] if platform.system() == "Darwin": ADDITIONAL_ARGS = [f"-mmacosx-version-min={TOOLSET}"] + ADDITIONAL_ARGS +if "wasm" in flags: + ADDITIONAL_ARGS.extend(("-sWASM_BIGINT", "-fexceptions")) + # If the linker supports GC sections, set it up to reduce binary file size # -fPIC is required for the shared libraries to work @@ -434,7 +462,7 @@ CFLAGS = os.environ.get("CFLAGS", "") LDFLAGS = os.environ.get("LDFLAGS", "") ADDITIONAL_ARGS_STR = " ".join(ADDITIONAL_ARGS) -if sp.call([bash, "-c", "ld --gc-sections 2>&1 | grep -- --gc-sections &> /dev/null"]) != 0: +if "wasm" not in flags and sp.call([bash, "-c", "ld --gc-sections 2>&1 | grep -- --gc-sections &> /dev/null"]) != 0: CXXFLAGS_MINIMAL = f"{CXXFLAGS} {PIC} {ADDITIONAL_ARGS_STR}" CFLAGS_MINIMAL = f"{CFLAGS} {PIC} {ADDITIONAL_ARGS_STR}" if BUILD_STATIC: @@ -504,8 +532,27 @@ if "swig" in targets: download_tool=download_tool_git, revision=f"rel-{SWIG_VERSION}" ) + +if "freetype" in targets: + build_dependency( + name=f"freetype", + mode="cmake", + build_tool_args=[ + f"-DCMAKE_INSTALL_PREFIX={DEPS_DIR}/install/freetype" + ], + download_url = "https://github.com/freetype/freetype", + download_name = "freetype2", + download_tool=download_tool_git, + ) if USE_OCCT and "occ" in targets: + patches = [] + if OCCT_VERSION < "7.4": + patches.append("./patches/occt/enable-exception-handling.patch") + + if "wasm" in flags: + patches.append("./patches/occt/no_em_js.patch") + build_dependency( name=f"occt-{OCCT_VERSION}", mode="cmake", @@ -513,12 +560,13 @@ if USE_OCCT and "occ" in targets: f"-DINSTALL_DIR={DEPS_DIR}/install/occt-{OCCT_VERSION}", f"-DBUILD_LIBRARY_TYPE={LINK_TYPE_UCFIRST}", "-DBUILD_MODULE_Draw=0", - "-DBUILD_RELEASE_DISABLE_EXCEPTIONS=Off" + "-DBUILD_RELEASE_DISABLE_EXCEPTIONS=Off", + f"-D3RDPARTY_FREETYPE_DIR={DEPS_DIR}/install/freetype" ], download_url = "https://github.com/Open-Cascade-SAS/OCCT", download_name = "occt", download_tool=download_tool_git, - patch=None if OCCT_VERSION >= "7.4" else "./patches/occt/enable-exception-handling.patch", + patch=patches, revision="V" + OCCT_VERSION.replace('.', '_') ) elif "occ" in targets: @@ -569,11 +617,11 @@ if "OpenCOLLADA" in targets: download_url="https://github.com/KhronosGroup/OpenCOLLADA.git", download_name="OpenCOLLADA", download_tool=download_tool_git, - patch="./patches/opencollada/pr622_and_disable_subdirs.patch", + patch=("./patches/opencollada/pr622_and_disable_subdirs.patch", "./patches/opencollada/remove_tr1.patch"), revision=OPENCOLLADA_VERSION ) -if "python" in targets and not USE_CURRENT_PYTHON_VERSION: +if "python" in targets and not USE_CURRENT_PYTHON_VERSION and "wasm" not in flags: # Python should not be built with -fvisibility=hidden, from experience that introduces segfaults OLD_CXX_FLAGS = os.environ["CXXFLAGS"] OLD_C_FLAGS = os.environ["CFLAGS"] @@ -612,6 +660,9 @@ if "python" in targets and not USE_CURRENT_PYTHON_VERSION: if "boost" in targets: str_concat = lambda prefix: lambda postfix: "" if postfix.strip() == "" else "=".join((prefix, postfix.strip())) + toolset = [] + if "wasm" in flags: + toolset.append("toolset=emscripten") build_dependency( f"boost-{BOOST_VERSION}", mode="bjam", @@ -623,21 +674,30 @@ if "boost" in targets: "--with-thread", "--with-date_time", "--with-iostreams", - f"link={LINK_TYPE}" - ] + \ - BOOST_ADDRESS_MODEL + \ - list(map(str_concat("cxxflags"), CXXFLAGS.strip().split(' '))) + \ - list(map(str_concat("linkflags"), LDFLAGS.strip().split(' '))) + \ - ["stage", "-s", "NO_BZIP2=1"], + f"link={LINK_TYPE}", + *toolset, + *map(str_concat("cxxflags"), CXXFLAGS.strip().split(' ')), + *map(str_concat("linkflags"), LDFLAGS.strip().split(' ')), + "stage", "-s", "NO_BZIP2=1"], download_url=BOOST_LOCATION, + patch="./patches/boost/boostorg_regex_62.patch", download_name=f"boost_{BOOST_VERSION_UNDERSCORE}.tar.bz2" ) + if "wasm" in flags: + # only supported on nix for now + run(("find", ".", "-name", "*.bc", "-exec", "bash", "-c", "emar q ${1%.bc}.a $1", "bash", "{}", ";"), cwd=f"{DEPS_DIR}/install/boost-{BOOST_VERSION}/lib") if "cgal" in targets: + gmp_args = [] + mpfr_args = [] + if "wasm" in flags: + gmp_args.extend(("--disable-assembly", "--host", "none", "--enable-cxx")) + mpfr_args.extend(("--host", "none")) + build_dependency( name=f"gmp-{GMP_VERSION}", mode="autoconf", - build_tool_args=[ENABLE_FLAG, DISABLE_FLAG, "--with-pic"], + build_tool_args=[ENABLE_FLAG, DISABLE_FLAG, "--with-pic", *gmp_args], download_url="https://ftp.gnu.org/gnu/gmp/", download_name=f"gmp-{GMP_VERSION}.tar.bz2" ) @@ -645,7 +705,7 @@ if "cgal" in targets: build_dependency( name=f"mpfr-{MPFR_VERSION}", mode="autoconf", - build_tool_args=[ENABLE_FLAG, DISABLE_FLAG, f"--with-gmp={DEPS_DIR}/install/gmp-{GMP_VERSION}"], + build_tool_args=[ENABLE_FLAG, DISABLE_FLAG, *mpfr_args, f"--with-gmp={DEPS_DIR}/install/gmp-{GMP_VERSION}"], download_url=f"http://www.mpfr.org/mpfr-{MPFR_VERSION}/", download_name=f"mpfr-{MPFR_VERSION}.tar.bz2" ) @@ -679,18 +739,8 @@ os.makedirs(IFCOS_DIR, exist_ok=True) executables_dir = os.path.join(IFCOS_DIR, "executables") os.makedirs(executables_dir, exist_ok=True) -logger.info("\rConfiguring executables...") - OFF_ON = ["OFF", "ON"] -exec_args = [ - "-DBUILD_IFCGEOM=" +OFF_ON["IfcGeom" in targets], - "-DBUILD_GEOMSERVER=" +OFF_ON["IfcGeomServer" in targets], - "-DBUILD_CONVERT=" +OFF_ON["IfcConvert" in targets], - "-DBUILD_IFCPYTHON=" "OFF", - "-DCMAKE_INSTALL_PREFIX=" f"{DEPS_DIR}/install/ifcopenshell", -] - cmake_args = [ "-DUSE_MMAP=" "OFF", "-DBUILD_EXAMPLES=" "OFF", @@ -702,6 +752,11 @@ cmake_args = [ "-DADD_COMMIT_SHA=" +("On" if ADD_COMMIT_SHA else "Off") ] +if "wasm" in flags: + # Boost is built by the build script so should not be found + # inside of the sysroot set by the emscriptem toolchain + cmake_args.append("-DWASM_BUILD=On") + if "cgal" in targets: cmake_args.extend([ "-DCGAL_INCLUDE_DIR=" f"{DEPS_DIR}/install/cgal-{CGAL_VERSION}/include", @@ -753,20 +808,36 @@ if "hdf5" in targets: "-DHDF5_INCLUDE_DIR=" f"{DEPS_DIR}/install/hdf5-{HDF5_VERSION}/include", "-DHDF5_LIBRARY_DIR=" f"{DEPS_DIR}/install/hdf5-{HDF5_VERSION}/lib" ]) +else: + cmake_args.append("-DHDF5_SUPPORT=Off") -run_cmake("", exec_args + cmake_args, cmake_dir=CMAKE_DIR, cwd=executables_dir) +if not explicit_targets or {"IfcGeom", "IfcConvert", "IfcGeomServer"} & set(explicit_targets): + logger.info("\rConfiguring executables...") -logger.info("\rBuilding executables... ") + exec_args = [ + "-DBUILD_IFCGEOM=" +OFF_ON["IfcGeom" in targets], + "-DBUILD_GEOMSERVER=" +OFF_ON["IfcGeomServer" in targets], + "-DBUILD_CONVERT=" +OFF_ON["IfcConvert" in targets], + "-DBUILD_IFCPYTHON=" "OFF", + "-DCMAKE_INSTALL_PREFIX=" f"{DEPS_DIR}/install/ifcopenshell", + ] + + run_cmake("", exec_args + cmake_args, cmake_dir=CMAKE_DIR, cwd=executables_dir) -run([make, f"-j{IFCOS_NUM_BUILD_PROCS}"], cwd=executables_dir) -run([make, "install/strip" if BUILD_CFG == "Release" else "install"], cwd=executables_dir) + logger.info("\rBuilding executables... ") + + run([make, f"-j{IFCOS_NUM_BUILD_PROCS}"], cwd=executables_dir) + run([make, "install/strip" if BUILD_CFG == "Release" else "install"], cwd=executables_dir) if "IfcOpenShell-Python" in targets: # On OSX the actual Python library is not linked against. ADDITIONAL_ARGS = "" if platform.system() == "Darwin": ADDITIONAL_ARGS = "-Wl,-flat_namespace,-undefined,suppress" - + + if "wasm" in flags: + ADDITIONAL_ARGS = f"-Wl,-undefined,suppress -sSIDE_MODULE=2 -sEXPORTED_FUNCTIONS=_PyInit__ifcopenshell_wrapper" + os.environ["CXXFLAGS"] = f"{CXXFLAGS_MINIMAL} {ADDITIONAL_ARGS}" os.environ["CFLAGS"] = f"{CFLAGS_MINIMAL} {ADDITIONAL_ARGS}" os.environ["LDFLAGS"] = f"{LDFLAGS} {ADDITIONAL_ARGS}" @@ -783,36 +854,57 @@ if "IfcOpenShell-Python" in targets: os.environ["PYTHON_LIBRARY_BASENAME"] = os.path.basename(python_library) + swig_when_built = [] + if "swig" in targets: + swig_when_built.append(f"-DSWIG_EXECUTABLE={DEPS_DIR}/install/swig/bin/swig") + run_cmake("", cmake_args + [ "-DPYTHON_LIBRARY=" +python_library, - "-DPYTHON_EXECUTABLE=" +python_executable, + *([f"-DPYTHON_EXECUTABLE={python_executable}"] if python_executable else []), + # *([f"-DPYTHON_MODULE_INSTALL_DIR={os.environ['PYTHONPATH']}/ifcopenshell"] if "wasm" in flags else []), + *(["-DPYTHON_MODULE_INSTALL_DIR="+os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "package"))] if "wasm" in flags else []), "-DPYTHON_INCLUDE_DIR=" +python_include, - "-DSWIG_EXECUTABLE=" f"{DEPS_DIR}/install/swig/bin/swig", "-DCMAKE_INSTALL_PREFIX=" f"{DEPS_DIR}/install/ifcopenshell/tmp", - "-DUSERSPACE_PYTHON_PREFIX=" +["Off", "On"][os.environ.get("PYTHON_USER_SITE", "").lower() in {"1", "on", "true"}] - ], cmake_dir=CMAKE_DIR, cwd=python_dir) + "-DUSERSPACE_PYTHON_PREFIX=" +["Off", "On"][os.environ.get("PYTHON_USER_SITE", "").lower() in {"1", "on", "true"}], + *swig_when_built], + cmake_dir=CMAKE_DIR, cwd=python_dir) logger.info(f"\rBuilding python {python_version} wrapper... ") run([make, f"-j{IFCOS_NUM_BUILD_PROCS}", "_ifcopenshell_wrapper"], cwd=python_dir) run([make, "install/local"], cwd=os.path.join(python_dir, "ifcwrap")) - module_dir = os.path.dirname(run([python_executable, "-c", "import inspect, ifcopenshell; print(inspect.getfile(ifcopenshell))"])) + if python_executable: + module_dir = os.path.dirname(run([python_executable, "-c", "import inspect, ifcopenshell; print(inspect.getfile(ifcopenshell))"])) - if platform.system() != "Darwin": - # TODO: This symbol name depends on the Python version? - run([strip, "-s", "-K", "PyInit__ifcopenshell_wrapper", "_ifcopenshell_wrapper.so"], cwd=module_dir) - return module_dir + if platform.system() != "Darwin": + # TODO: This symbol name depends on the Python version? + run([strip, "-s", "-K", "PyInit__ifcopenshell_wrapper", "_ifcopenshell_wrapper.so"], cwd=module_dir) + return module_dir - if USE_CURRENT_PYTHON_VERSION: - python_info = sysconfig.get_paths() - python_lib = os.path.join( - sysconfig.get_config_var('LIBDIR'), - sysconfig.get_config_var('multiarchsubdir').replace("/", ""), - sysconfig.get_config_var("INSTSONAME") + if "wasm" in flags: + compile_python_wrapper( + f"{os.environ['PYMAJOR']}.{os.environ['PYMINOR']}.{os.environ['PYMICRO']}", + f"{os.environ['TARGETINSTALLDIR']}/lib/libpython{os.environ['PYMAJOR']}.{os.environ['PYMINOR']}.a", + os.environ['PYTHONINCLUDE'], + None ) + + elif USE_CURRENT_PYTHON_VERSION: + python_info = sysconfig.get_paths() + + py_path_components = [ + sysconfig.get_config_var('LIBDIR'), + sysconfig.get_config_var("INSTSONAME") + ] + + if sysconfig.get_config_var('multiarchsubdir'): + py_path_components.insert(1, sysconfig.get_config_var('multiarchsubdir').replace("/", "")) + + python_lib = os.path.join(*py_path_components) + compile_python_wrapper(platform.python_version(), python_lib, python_info["include"], sys.executable) else: for python_version in PYTHON_VERSIONS: diff --git a/nix/patches/boost/boostorg_regex_62.patch b/nix/patches/boost/boostorg_regex_62.patch new file mode 100644 index 0000000000..963a4ac73c --- /dev/null +++ b/nix/patches/boost/boostorg_regex_62.patch @@ -0,0 +1,20 @@ +--- a/tools/build/src/tools/emscripten.jam ++++ b/tools/build/src/tools/emscripten.jam +@@ -6,6 +6,7 @@ + import feature ; + import os ; + import toolset ; ++import generators ; + import common ; + import gcc ; + import type ; +@@ -52,6 +53,8 @@ + off on + off on + ; ++generators.override builtin.lib-generator : emscripten.prebuilt ; ++generators.override emscripten.searched-lib-generator : searched-lib-generator ; + + type.set-generated-target-suffix EXE : emscripten : "js" ; + type.set-generated-target-suffix OBJ : emscripten : "bc" ; + \ No newline at end of file diff --git a/nix/patches/occt/no_em_js.patch b/nix/patches/occt/no_em_js.patch new file mode 100644 index 0000000000..2090686f9f --- /dev/null +++ b/nix/patches/occt/no_em_js.patch @@ -0,0 +1,99 @@ +diff --git a/src/Message/Message_PrinterSystemLog.cxx b/src/Message/Message_PrinterSystemLog.cxx +index 0c82c2167..a2e9e6d68 100644 +--- a/src/Message/Message_PrinterSystemLog.cxx ++++ b/src/Message/Message_PrinterSystemLog.cxx +@@ -55,27 +55,6 @@ + return ANDROID_LOG_DEBUG; + } + #elif defined(__EMSCRIPTEN__) +- #include +- +- //! Print message to console.debug(). +- EM_JS(void, occJSConsoleDebug, (const char* theStr), { +- console.debug(UTF8ToString(theStr)); +- }); +- +- //! Print message to console.info(). +- EM_JS(void, occJSConsoleInfo, (const char* theStr), { +- console.info(UTF8ToString(theStr)); +- }); +- +- //! Print message to console.warn(). +- EM_JS(void, occJSConsoleWarn, (const char* theStr), { +- console.warn(UTF8ToString(theStr)); +- }); +- +- //! Print message to console.error(). +- EM_JS(void, occJSConsoleError, (const char* theStr), { +- console.error(UTF8ToString(theStr)); +- }); + #else + #include + +@@ -169,16 +148,6 @@ void Message_PrinterSystemLog::send (const TCollection_AsciiString& theString, + #elif defined(__ANDROID__) + __android_log_write (getAndroidLogPriority (theGravity), myEventSourceName.ToCString(), theString.ToCString()); + #elif defined(__EMSCRIPTEN__) +- // don't use bogus emscripten_log() corrupting UNICODE strings +- switch (theGravity) +- { +- case Message_Trace: occJSConsoleDebug(theString.ToCString()); return; +- case Message_Info: occJSConsoleInfo (theString.ToCString()); return; +- case Message_Warning: occJSConsoleWarn (theString.ToCString()); return; +- case Message_Alarm: occJSConsoleError(theString.ToCString()); return; +- case Message_Fail: occJSConsoleError(theString.ToCString()); return; +- } +- occJSConsoleWarn (theString.ToCString()); + #else + syslog (getSysLogPriority (theGravity), "%s", theString.ToCString()); + #endif +diff --git a/src/OSD/OSD_MemInfo.cxx b/src/OSD/OSD_MemInfo.cxx +index 08a939beb..7c1fc79b3 100644 +--- a/src/OSD/OSD_MemInfo.cxx ++++ b/src/OSD/OSD_MemInfo.cxx +@@ -37,15 +37,6 @@ + + #include + +-#if defined(__EMSCRIPTEN__) +- #include +- +- //! Return WebAssembly heap size in bytes. +- EM_JS(size_t, OSD_MemInfo_getModuleHeapLength, (), { +- return Module.HEAP8.length; +- }); +-#endif +- + // ======================================================================= + // function : OSD_MemInfo + // purpose : +@@ -156,29 +147,6 @@ void OSD_MemInfo::Update() + } + + #elif defined(__EMSCRIPTEN__) +- if (IsActive (MemHeapUsage) +- || IsActive (MemWorkingSet) +- || IsActive (MemWorkingSetPeak)) +- { +- // /proc/%d/status is not emulated - get more info from mallinfo() +- const struct mallinfo aMI = mallinfo(); +- if (IsActive (MemHeapUsage)) +- { +- myCounters[MemHeapUsage] = aMI.uordblks; +- } +- if (IsActive (MemWorkingSet)) +- { +- myCounters[MemWorkingSet] = aMI.uordblks; +- } +- if (IsActive (MemWorkingSetPeak)) +- { +- myCounters[MemWorkingSetPeak] = aMI.usmblks; +- } +- } +- if (IsActive (MemVirtual)) +- { +- myCounters[MemVirtual] = OSD_MemInfo_getModuleHeapLength(); +- } + #elif (defined(__linux__) || defined(__linux)) + if (IsActive (MemHeapUsage)) + { diff --git a/nix/patches/opencollada/remove_tr1.patch b/nix/patches/opencollada/remove_tr1.patch new file mode 100644 index 0000000000..f586279d49 --- /dev/null +++ b/nix/patches/opencollada/remove_tr1.patch @@ -0,0 +1,81 @@ +diff --git a/COLLADABaseUtils/include/COLLADABUhash_map.h b/COLLADABaseUtils/include/COLLADABUhash_map.h +index 8ab0fb9b..12503bfb 100644 +--- a/COLLADABaseUtils/include/COLLADABUhash_map.h ++++ b/COLLADABaseUtils/include/COLLADABUhash_map.h +@@ -32,9 +32,9 @@ + #include + #include + +- #define COLLADABU_HASH_MAP std::tr1::unordered_map +- #define COLLADABU_HASH_MULTIMAP std::tr1::unordered_multimap +- #define COLLADABU_HASH_SET std::tr1::unordered_set ++ #define COLLADABU_HASH_MAP std::unordered_map ++ #define COLLADABU_HASH_MULTIMAP std::unordered_multimap ++ #define COLLADABU_HASH_SET std::unordered_set + #define COLLADABU_HASH_NAMESPACE_OPEN std { namespace tr1 + #define COLLADABU_HASH_NAMESPACE_CLOSE } + #define COLLADABU_HASH_FUN hash +@@ -50,12 +50,12 @@ + #define COLLADABU_HASH_FUN hash + #endif + #elif defined(__MINGW32__) || defined(__MINGW64__) +- #include +- #include ++ #include ++ #include + +- #define COLLADABU_HASH_MAP std::tr1::unordered_map +- #define COLLADABU_HASH_MULTIMAP std::tr1::unordered_multimap +- #define COLLADABU_HASH_SET std::tr1::unordered_set ++ #define COLLADABU_HASH_MAP std::unordered_map ++ #define COLLADABU_HASH_MULTIMAP std::unordered_multimap ++ #define COLLADABU_HASH_SET std::unordered_set + #define COLLADABU_HASH_NAMESPACE_OPEN std { namespace tr1 + #define COLLADABU_HASH_NAMESPACE_CLOSE } + #define COLLADABU_HASH_FUN hash +@@ -107,12 +107,12 @@ + #define COLLADABU_HASH_NAMESPACE_CLOSE + #define COLLADABU_HASH_FUN hash + #else +- #include +- #include ++ #include ++ #include + +- #define COLLADABU_HASH_MAP std::tr1::unordered_map +- #define COLLADABU_HASH_MULTIMAP std::tr1::unordered_multimap +- #define COLLADABU_HASH_SET std::tr1::unordered_set ++ #define COLLADABU_HASH_MAP std::unordered_map ++ #define COLLADABU_HASH_MULTIMAP std::unordered_multimap ++ #define COLLADABU_HASH_SET std::unordered_set + #define COLLADABU_HASH_NAMESPACE_OPEN std { namespace tr1 + #define COLLADABU_HASH_NAMESPACE_CLOSE } + #define COLLADABU_HASH_FUN hash +diff --git a/common/libBuffer/include/CommonFWriteBufferFlusher.h b/common/libBuffer/include/CommonFWriteBufferFlusher.h +index c7af45b2..fac4f133 100644 +--- a/common/libBuffer/include/CommonFWriteBufferFlusher.h ++++ b/common/libBuffer/include/CommonFWriteBufferFlusher.h +@@ -15,12 +15,12 @@ + + #if (defined(WIN64) || defined(_WIN64) || defined(__WIN64__)) || (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || defined(__APPLE__)) + #if defined(__GNUC__) && !defined(_LIBCPP_VERSION) +-# include ++# include + #else + # include + #endif + #else +-# include ++# include + #endif + + #ifdef _LIBCPP_VERSION +@@ -58,7 +58,7 @@ namespace Common + #else + typedef __int64 FilePosType; + #endif +- typedef std::tr1::unordered_map MarkIdToFilePos; ++ typedef std::unordered_map MarkIdToFilePos; + + public: + static const size_t DEFAUL_BUFFER_SIZE = 64*1024; diff --git a/pyodide/meta.yaml b/pyodide/meta.yaml new file mode 100644 index 0000000000..06c55eb97e --- /dev/null +++ b/pyodide/meta.yaml @@ -0,0 +1,18 @@ +package: + name: ifcopenshell + version: 0.7.0 + +source: + path: IfcOpenShell + +build: + script: | + python nix/build-all.py --without-hdf5 --without-opencollada --without-swig --without-pcre -v --wasm --py310 IfcOpenShell-Python + cp pyodide/setup.py . + +about: + home: http://ifcopenshell.org + license: LGPL-3.0-or-later + summary: | + IfcOpenShell is an open source (LGPL) software library for + working with the Industry Foundation Classes (IFC) file format. diff --git a/pyodide/setup.py b/pyodide/setup.py new file mode 100644 index 0000000000..67faf709cd --- /dev/null +++ b/pyodide/setup.py @@ -0,0 +1,11 @@ +from setuptools import setup, find_packages + +setup(name='IfcOpenShell', + version='0.7.0', + description='IfcOpenShell is an open source (LGPL) software library for working with the Industry Foundation Classes (IFC) file format.', + author='Thomas Krijnen', + author_email='thomas@aecgeeks.com', + url='http://ifcopenshell.org', + packages=find_packages(), + package_data={'': ['*.so']}, +) diff --git a/src/ifcwrap/CMakeLists.txt b/src/ifcwrap/CMakeLists.txt index 150ac6363f..8caee5a956 100644 --- a/src/ifcwrap/CMakeLists.txt +++ b/src/ifcwrap/CMakeLists.txt @@ -67,20 +67,24 @@ endif() # Try to find the Python interpreter to get the site-packages # directory in which the wrapper can be installed. FIND_PACKAGE(PythonInterp) -IF(PYTHONINTERP_FOUND AND NOT "${PYTHON_EXECUTABLE}" STREQUAL "") - IF (USERSPACE_PYTHON_PREFIX) - EXECUTE_PROCESS( - COMMAND ${PYTHON_EXECUTABLE} -c "import sys; import site; sys.stdout.write(site.USER_SITE)" - OUTPUT_VARIABLE python_package_dir - ) - ELSE () - EXECUTE_PROCESS( - COMMAND ${PYTHON_EXECUTABLE} -c "import sys; from distutils.sysconfig import get_python_lib; sys.stdout.write(get_python_lib(1))" - OUTPUT_VARIABLE python_package_dir - ) - ENDIF() - if (BUILD_PACKAGE) - set(python_package_dir ${CMAKE_INSTALL_LIBDIR}/python${PYTHON_VERSION_MAJOR}/dist-packages/) +IF((PYTHONINTERP_FOUND AND NOT "${PYTHON_EXECUTABLE}" STREQUAL "") OR PYTHON_MODULE_INSTALL_DIR) + if (PYTHON_MODULE_INSTALL_DIR) + set(python_package_dir "${PYTHON_MODULE_INSTALL_DIR}") + else() + IF (USERSPACE_PYTHON_PREFIX) + EXECUTE_PROCESS( + COMMAND ${PYTHON_EXECUTABLE} -c "import sys; import site; sys.stdout.write(site.USER_SITE)" + OUTPUT_VARIABLE python_package_dir + ) + ELSE () + EXECUTE_PROCESS( + COMMAND ${PYTHON_EXECUTABLE} -c "import sys; from distutils.sysconfig import get_python_lib; sys.stdout.write(get_python_lib(1))" + OUTPUT_VARIABLE python_package_dir + ) + ENDIF() + if (BUILD_PACKAGE) + set(python_package_dir ${CMAKE_INSTALL_LIBDIR}/python${PYTHON_VERSION_MAJOR}/dist-packages/) + endif() endif() IF("${python_package_dir}" STREQUAL "") MESSAGE(WARNING "Unable to locate Python site-package directory, unable to install the Python wrapper") diff --git a/src/svgfill b/src/svgfill index ac17050194..4d2aa7acf7 160000 --- a/src/svgfill +++ b/src/svgfill @@ -1 +1 @@ -Subproject commit ac1705019422ea012e8a5751cc5ed4639481b402 +Subproject commit 4d2aa7acf76cb8509ab3ba2961c34502adf3abe9