From c6b9c7588eb628b72b15d2ee8827cf61a75568ae Mon Sep 17 00:00:00 2001 From: Yousef Khadadeh Date: Fri, 28 Aug 2026 21:44:05 +0300 Subject: [PATCH] Fixed Emscripten Build --- conan/emsdk/all/conandata.yml | 4 + conan/emsdk/all/conanfile.py | 107 ++++++++++++++++++++++ conan/emsdk/all/test_package/conanfile.py | 16 ++++ conan/emsdk/config.yml | 3 + conan/export_libs.bat | 1 + conan/export_libs.sh | 1 + docs/BUILDING.md | 2 - 7 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 conan/emsdk/all/conandata.yml create mode 100644 conan/emsdk/all/conanfile.py create mode 100644 conan/emsdk/all/test_package/conanfile.py create mode 100644 conan/emsdk/config.yml diff --git a/conan/emsdk/all/conandata.yml b/conan/emsdk/all/conandata.yml new file mode 100644 index 0000000..b41d0ca --- /dev/null +++ b/conan/emsdk/all/conandata.yml @@ -0,0 +1,4 @@ +sources: + "4.0.22": + url: "https://github.com/emscripten-core/emsdk/archive/4.0.22.tar.gz" + sha256: "8f5e26de5c103c41ae04411e1be1ffc17983fb368343b15102a1859b3aa64626" diff --git a/conan/emsdk/all/conanfile.py b/conan/emsdk/all/conanfile.py new file mode 100644 index 0000000..0fea66a --- /dev/null +++ b/conan/emsdk/all/conanfile.py @@ -0,0 +1,107 @@ +import os +from pathlib import Path + +from conan import ConanFile +from conan.tools.env import VirtualBuildEnv +from conan.tools.files import chdir, copy, get +from conan.tools.layout import basic_layout + +required_conan_version = ">=2.1" + + +class EmSDKConan(ConanFile): + name = "emsdk" + description = "Emscripten SDK. Emscripten is an Open Source LLVM to JavaScript compiler" + url = "https://github.com/conan-io/conan-center-index" + homepage = "https://github.com/kripken/emscripten" + topics = ("emsdk", "emscripten", "sdk", "emcc", "em++", "nodejs") + license = "MIT" + package_type = "application" + settings = "os", "arch" + + def layout(self): + basic_layout(self, src_folder="src") + + def source(self): + get(self, **self.conan_data["sources"][self.version], destination=self.source_folder, strip_root=True) + + @property + def _emscripten(self): + return os.path.join(self.package_folder, "bin", "upstream", "emscripten") + + @property + def _node_path(self): + subfolders = [path for path in (Path(self.package_folder) / "bin" / "node").iterdir() if path.is_dir()] + if len(subfolders) != 1: + return None + return os.path.join("bin", "node", subfolders[0].name, "bin") + + def generate(self): + # To avoid issues when cross-compiling or with not common arch in profiles we need to set EMSDK_ARCH + # This is important for the emsdk install command + env = VirtualBuildEnv(self) + # Special consideration for armv8 as emsdk expects "arm64" + arch = "arm64" if str(self.settings.arch) == "armv8" else str(self.settings.arch) + env.environment().define("EMSDK_ARCH", arch) + env.generate() + + def build(self): + with chdir(self, self.source_folder): + emsdk = "emsdk.bat" if self.settings_build.os == "Windows" else "./emsdk" + self.run(f"{emsdk} install latest") + self.run(f"{emsdk} activate latest") + + def package(self): + copy(self, "LICENSE", src=self.source_folder, dst=os.path.join(self.package_folder, "licenses")) + copy(self, "*", src=self.source_folder, dst=os.path.join(self.package_folder, "bin")) + + def finalize(self): + copy(self, "*", src=self.immutable_package_folder, dst=self.package_folder) + embuilder = os.path.join( + self._emscripten, "embuilder" if self.info.settings.os != "Windows" else "embuilder.bat" + ) + self.run(f"{embuilder} build MINIMAL") + + def _define_tool_var(self, value): + suffix = ".bat" if self.settings.os == "Windows" else "" + path = os.path.join(self._emscripten, f"{value}{suffix}") + return path + + def package_info(self): + self.cpp_info.bindirs = ["bin", os.path.join("bin", "upstream", "emscripten"), self._node_path] + self.cpp_info.includedirs = [] + self.cpp_info.libdirs = [] + self.cpp_info.resdirs = [] + + # If we are not building for Emscripten, probably we don't want to inject following environment variables, + # but it might be legit use cases... until we find them, let's be conservative. + if not hasattr(self, "settings_target") or self.settings_target is None: + return + + if self.settings_target.os != "Emscripten": + self.output.warning( + f"You've added {self.name}/{self.version} as a build requirement, while os={self.settings_target.os} != Emscripten" + ) + return + + toolchain = os.path.join( + self.package_folder, "bin", "upstream", "emscripten", "cmake", "Modules", "Platform", "Emscripten.cmake" + ) + self.conf_info.prepend("tools.cmake.cmaketoolchain:user_toolchain", toolchain) + + self.buildenv_info.define_path("EMSDK", os.path.join(self.package_folder, "bin")) + self.buildenv_info.define_path("EMSCRIPTEN", self._emscripten) + self.buildenv_info.define_path("EM_CONFIG", os.path.join(self.package_folder, "bin", ".emscripten")) + self.buildenv_info.define_path("EM_CACHE", os.path.join(self.package_folder, "bin", ".emscripten_cache")) + + compiler_executables = { + "c": self._define_tool_var("emcc"), + "cpp": self._define_tool_var("em++"), + } + self.conf_info.update("tools.build:compiler_executables", compiler_executables) + self.buildenv_info.define_path("CC", compiler_executables["c"]) + self.buildenv_info.define_path("CXX", compiler_executables["cpp"]) + self.buildenv_info.define_path("AR", self._define_tool_var("emar")) + self.buildenv_info.define_path("NM", self._define_tool_var("emnm")) + self.buildenv_info.define_path("RANLIB", self._define_tool_var("emranlib")) + self.buildenv_info.define_path("STRIP", self._define_tool_var("emstrip")) diff --git a/conan/emsdk/all/test_package/conanfile.py b/conan/emsdk/all/test_package/conanfile.py new file mode 100644 index 0000000..a0948be --- /dev/null +++ b/conan/emsdk/all/test_package/conanfile.py @@ -0,0 +1,16 @@ +from conan import ConanFile +from conan.tools.build import can_run + + +class TestPackageConan(ConanFile): + settings = "os", "arch", "compiler", "build_type" + + def requirements(self): + self.requires(self.tested_reference_str) + + def test(self): + # Check the package provides working binaries + if can_run(self): + self.run("emcc -v", env="conanrun") + self.run("em++ -v", env="conanrun") + self.run("node -v", env="conanrun") diff --git a/conan/emsdk/config.yml b/conan/emsdk/config.yml new file mode 100644 index 0000000..6764e36 --- /dev/null +++ b/conan/emsdk/config.yml @@ -0,0 +1,3 @@ +versions: + "4.0.22": + folder: all diff --git a/conan/export_libs.bat b/conan/export_libs.bat index 7eb6fc7..5d1a0d1 100755 --- a/conan/export_libs.bat +++ b/conan/export_libs.bat @@ -1,3 +1,4 @@ conan export %0/../conan-skia/recipes/skia/all/conanfile.py --version=143.20251028.0 +conan export %0/../conan-skia/recipes/emsdk/all/conanfile.py --version=4.0.22 conan export %0/../icu/all/conanfile.py --version=77.1 conan export %0/../sdl/3.x/conanfile.py --version=3.4.8 diff --git a/conan/export_libs.sh b/conan/export_libs.sh index 42163c2..4e0eda3 100755 --- a/conan/export_libs.sh +++ b/conan/export_libs.sh @@ -3,6 +3,7 @@ SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) conan export "$(dirname "$0")/conan-skia/recipes/skia/all/conanfile.py" --version=143.20251028.0 +conan export "$(dirname "$0")/emsdk/all/conanfile.py" --version=4.0.22 conan export "$(dirname "$0")/icu/all/conanfile.py" --version=77.1 conan export "$(dirname "$0")/sdl/3.x/conanfile.py" --version=3.4.8 #conan create . -o use_system_expat=False -o use_system_freetype=False -o use_system_harfbuzz=False -o use_system_icu=False -o use_system_libjpeg_turbo=False -o use_system_libpng=False -o use_system_libwebp=False -o use_system_zlib=False --build=missing --version=134.20250327.0 diff --git a/docs/BUILDING.md b/docs/BUILDING.md index 993e58b..0eb88ee 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -55,8 +55,6 @@ cpack -G NSIS ## Emscripten You can use Emscripten to build a web version of this program. Keep in mind that this version might be more buggy, and is missing a few features. In addition, I have only tried building it on a Linux machine. -You'll need to setup conan toolchains. You can read about that [here](https://github.com/conan-io/conan-toolchains). - After cloning the repository, `cd` into the repo, then update the git submodules to get `datachannel-wasm`: ``` git submodule update --init --recursive