| # Emscripten WebGPU port with optional Wagyu extensions |
| |
| |
| import os |
| import zlib |
| from typing import Dict, Optional |
| |
| |
| OPTIONS = { |
| 'wagyu': "Enable Wagyu extensions (default: false)", |
| } |
| _VALID_OPTION_VALUES = { |
| 'wagyu': {'true', 'false'}, |
| } |
| _opts: Dict[str, Optional[str]] = { |
| 'wagyu': 'false', |
| } |
| |
| |
| def _get_base_dir(): |
| return os.path.dirname(os.path.realpath(__file__)) |
| |
| |
| def _get_include_dir(): |
| return os.path.join(_get_base_dir(), 'include') |
| |
| |
| def _get_src_dir(): |
| return os.path.join(_get_base_dir(), 'src') |
| |
| |
| def _get_srcs(): |
| return [ _get_src_dir() + '/webgpu.c' ] |
| |
| def _recurse_dir(path): |
| for (dirpath, dirnames, filenames) in os.walk(path): |
| for filename in filenames: |
| yield os.path.join(dirpath, filename) |
| |
| |
| def _get_build_files(): |
| return sorted([ |
| __file__, |
| *_get_srcs(), |
| *_recurse_dir(_get_include_dir()), |
| ]) |
| |
| |
| # compiler |
| |
| def _check_option(option, value, error_handler): |
| if value not in _VALID_OPTION_VALUES[option]: |
| error_handler( |
| f'[{option}] can be {list(_VALID_OPTION_VALUES[option])}, got [{value}]' |
| ) |
| return value |
| |
| |
| def handle_options(options, error_handler): |
| for option, value in options.items(): |
| value = value.lower() |
| _opts[option] = _check_option(option, value, error_handler) |
| |
| |
| def process_args(ports): |
| args = ['-isystem', _get_include_dir()] |
| return args |
| |
| |
| # linker |
| |
| def _get_flags(settings): |
| lib_name_suffix = '' |
| flags = [] |
| return (lib_name_suffix, flags) |
| |
| |
| def _get_name(settings): |
| hash_value = 0 |
| |
| def add(x): |
| nonlocal hash_value |
| hash_value = zlib.adler32(x, hash_value) |
| |
| build_files = _get_build_files() |
| for filename in build_files: |
| add(open(filename, 'rb').read()) |
| |
| (lib_name_suffix, _) = _get_flags(settings) |
| return f'libwebgpu-{hash_value:08x}{lib_name_suffix}.a' |
| |
| |
| def clear(ports, settings, shared): |
| shared.cache.erase_lib(_get_name(settings)) |
| |
| |
| def get(ports, settings, shared): |
| if settings.allowed_settings: |
| return [] |
| |
| def create(final): |
| includes = [_get_include_dir()] |
| (_, flags) = _get_flags(settings) |
| flags += [] |
| ports.build_port(_get_src_dir(), final, 'webgpu', includes=includes, flags=flags, srcs=_get_srcs()) |
| |
| lib_name = _get_name(settings) |
| return [shared.cache.get_lib(lib_name, create, what='port')] |
| |
| |
| def linker_setup(ports, settings): |
| if settings.USE_WEBGPU: |
| raise Exception('webgpu-port is not compatible with deprecated Emscripten USE_WEBGPU option') |
| |
| src_dir = _get_src_dir() |
| |
| settings.JS_LIBRARIES += [ os.path.join(src_dir, 'library_webgpu_stubs.js') ] |
| |
| # Ensure the function gets exported |
| settings.EXPORTED_FUNCTIONS += ['_wgpuWagyuGetCompiledVersion'] |
| |
| if _opts['wagyu'] == 'true': |
| settings.JS_LIBRARIES += [ os.path.join(src_dir, 'library_webgpu_wagyu_stubs.js') ] |