[Bazel] Support generating a secondary cache (#1405)
This is a working solution for generating a separate Emscripten cache.
Note that this requires an additional entry in the workspace as follows:
```starlark
load("@emsdk//:emscripten_cache.bzl", emsdk_emscripten_cache = "emscripten_cache")
emsdk_emscripten_cache()
```
When used like this, the default Emscripten cache will be used. However,
if the entry is as follows:
```starlark
load("@emsdk//:emscripten_cache.bzl", emsdk_emscripten_cache = "emscripten_cache")
emsdk_emscripten_cache(flags = ["--lto"])
```
Then embuilder will be called to build all system libraries and ports
(i.e., the `ALL` option to embuilder) with the LTO option enabled. This
can take awhile, so I have also made possible to specify which libraries
you want to build explicitly:
```starlark
load("@emsdk//:emscripten_cache.bzl", emsdk_emscripten_cache = "emscripten_cache")
emsdk_emscripten_cache(
flags = ["--lto"],
libraries = [
"crtbegin",
"libprintf_long_double-debug",
"libstubs-debug",
"libnoexit",
"libc-debug",
"libdlmalloc",
"libcompiler_rt",
"libc++-noexcept",
"libc++abi-debug-noexcept",
"libsockets"
]
)
```
Resolves #807, resolves #971, resolves #1099, resolves #1362, resolves
#1401
2024-07-01 18:23:07 +02:00
|
|
|
BUILD_FILE_CONTENT_TEMPLATE = """
|
|
|
|
|
package(default_visibility = ['//visibility:public'])
|
|
|
|
|
exports_files(['emscripten_config'])
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
EMBUILDER_CONFIG_TEMPLATE = """
|
|
|
|
|
CACHE = '{cache}'
|
|
|
|
|
BINARYEN_ROOT = '{binaryen_root}'
|
|
|
|
|
LLVM_ROOT = '{llvm_root}'
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def get_root_and_script_ext(repository_ctx):
|
|
|
|
|
if repository_ctx.os.name.startswith('linux'):
|
|
|
|
|
if 'amd64' in repository_ctx.os.arch or 'x86_64' in repository_ctx.os.arch:
|
|
|
|
|
return (repository_ctx.path(Label("@emscripten_bin_linux//:BUILD.bazel")).dirname, '')
|
|
|
|
|
elif 'aarch64' in repository_ctx.os.arch:
|
|
|
|
|
return (repository_ctx.path(Label("@emscripten_bin_linux_arm64//:BUILD.bazel")).dirname, '')
|
|
|
|
|
else:
|
|
|
|
|
fail('Unsupported architecture for Linux')
|
|
|
|
|
elif repository_ctx.os.name.startswith('mac'):
|
|
|
|
|
if 'amd64' in repository_ctx.os.arch or 'x86_64' in repository_ctx.os.arch:
|
|
|
|
|
return (repository_ctx.path(Label("@emscripten_bin_mac//:BUILD.bazel")).dirname, '')
|
|
|
|
|
elif 'aarch64' in repository_ctx.os.arch:
|
|
|
|
|
return (repository_ctx.path(Label("@emscripten_bin_mac_arm64//:BUILD.bazel")).dirname, '')
|
|
|
|
|
else:
|
|
|
|
|
fail('Unsupported architecture for MacOS')
|
|
|
|
|
elif repository_ctx.os.name.startswith('windows'):
|
|
|
|
|
return (repository_ctx.path(Label("@emscripten_bin_win//:BUILD.bazel")).dirname, '.bat')
|
|
|
|
|
else:
|
|
|
|
|
fail('Unsupported operating system')
|
|
|
|
|
|
|
|
|
|
def _emscripten_cache_impl(repository_ctx):
|
|
|
|
|
# Read the default emscripten configuration file
|
|
|
|
|
default_config = repository_ctx.read(
|
|
|
|
|
repository_ctx.path(
|
|
|
|
|
Label("@emsdk//emscripten_toolchain:default_config")
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if repository_ctx.attr.targets or repository_ctx.attr.configuration:
|
|
|
|
|
root, script_ext = get_root_and_script_ext(repository_ctx)
|
|
|
|
|
llvm_root = root.get_child("bin")
|
|
|
|
|
cache = repository_ctx.path("cache")
|
|
|
|
|
# Create configuration file
|
|
|
|
|
embuilder_config_content = EMBUILDER_CONFIG_TEMPLATE.format(
|
|
|
|
|
cache=cache,
|
|
|
|
|
binaryen_root=root,
|
|
|
|
|
llvm_root=llvm_root,
|
|
|
|
|
)
|
|
|
|
|
repository_ctx.file("embuilder_config", embuilder_config_content)
|
|
|
|
|
embuilder_config_path = repository_ctx.path("embuilder_config")
|
|
|
|
|
embuilder_path = "{}{}".format(root.get_child("emscripten").get_child("embuilder"), script_ext)
|
|
|
|
|
# Prepare the command line
|
|
|
|
|
if repository_ctx.attr.targets:
|
|
|
|
|
targets = repository_ctx.attr.targets
|
|
|
|
|
else:
|
|
|
|
|
# If no targets are requested, build everything
|
|
|
|
|
targets = ["ALL"]
|
|
|
|
|
flags = ["--em-config", embuilder_config_path] + repository_ctx.attr.configuration
|
|
|
|
|
embuilder_args = [embuilder_path] + flags + ["build"] + targets
|
|
|
|
|
# Run embuilder
|
|
|
|
|
repository_ctx.report_progress("Building secondary cache")
|
|
|
|
|
result = repository_ctx.execute(
|
|
|
|
|
embuilder_args,
|
|
|
|
|
quiet=True,
|
|
|
|
|
environment = {
|
|
|
|
|
"EM_IGNORE_SANITY": "1",
|
|
|
|
|
"EM_NODE_JS": "empty",
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
if result.return_code != 0:
|
|
|
|
|
fail("Embuilder exited with a non-zero return code")
|
|
|
|
|
# Override Emscripten's cache with the secondary cache
|
|
|
|
|
default_config += "CACHE = '{}'\n".format(cache)
|
|
|
|
|
|
|
|
|
|
# Create the configuration file for the toolchain and export
|
|
|
|
|
repository_ctx.file('emscripten_config', default_config)
|
|
|
|
|
repository_ctx.file('BUILD.bazel', BUILD_FILE_CONTENT_TEMPLATE)
|
|
|
|
|
|
|
|
|
|
_emscripten_cache = repository_rule(
|
|
|
|
|
implementation = _emscripten_cache_impl,
|
|
|
|
|
attrs = {
|
|
|
|
|
"configuration": attr.string_list(),
|
|
|
|
|
"targets": attr.string_list(),
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def register_emscripten_toolchains(cache = {}):
|
|
|
|
|
_emscripten_cache(
|
|
|
|
|
name = "emscripten_cache",
|
|
|
|
|
configuration = cache["configuration"] if "configuration" in cache else [],
|
|
|
|
|
targets = cache["targets"] if "targets" in cache else [],
|
|
|
|
|
)
|
|
|
|
|
|
2022-11-08 14:21:06 -08:00
|
|
|
native.register_toolchains(str(Label("//emscripten_toolchain:cc-toolchain-wasm")))
|