Update CROSSTOOL tutorial
We're moving away from using CROSSTOOL files (Issue #5380, #7320).
RELNOTES: None.
PiperOrigin-RevId: 234609662
diff --git a/site/_layouts/documentation.html b/site/_layouts/documentation.html
index f8a629b..d027346 100644
--- a/site/_layouts/documentation.html
+++ b/site/_layouts/documentation.html
@@ -183,8 +183,16 @@
<ul class="collapse sidebar-nav sidebar-submenu" id="cpp-menu">
<li><a href="/versions/{{ current_version }}/bazel-and-cpp.html">C++ Resources</a></li>
<li><a href="/versions/{{ current_version }}/cpp-use-cases.html">C++ Use Cases</a></li>
- <li><a href="/versions/{{ current_version }}/cc-toolchain-config-reference.html">C++ toolchain configuration</a></li>
- <li><a href="/versions/{{ current_version }}/tutorial/crosstool.html">Tutorial: CROSSTOOL</a></li>
+ <li>
+ <a href="/versions/{{ current_version }}/cc-toolchain-config-reference.html">
+ C++ Toolchain Configuration
+ </a>
+ </li>
+ <li>
+ <a href="/versions/{{ current_version }}/tutorial/cc-toolchain-config.html">
+ Tutorial: Configuring C++ Toolchains
+ </a>
+ </li>
</ul>
</li>
diff --git a/site/docs/bazel-and-cpp.md b/site/docs/bazel-and-cpp.md
index e2c0ff7..8f43648 100644
--- a/site/docs/bazel-and-cpp.md
+++ b/site/docs/bazel-and-cpp.md
@@ -24,7 +24,7 @@
* [C++ common use cases](cpp-use-cases.html)
* [C/C++ rules](be/c-cpp.html)
* [C++ toolchain configuration](cc-toolchain-config-reference.html)
-* [Configuring CROSSTOOL](tutorial/crosstool.html)
+* [Tutorial: Configuring C++ toolchains](tutorial/cc-toolchain-config.html)
## Best practices
diff --git a/site/docs/tutorial/cc-toolchain-config.md b/site/docs/tutorial/cc-toolchain-config.md
new file mode 100644
index 0000000..e61527a
--- /dev/null
+++ b/site/docs/tutorial/cc-toolchain-config.md
@@ -0,0 +1,620 @@
+---
+layout: documentation
+title: Configuring C++ toolchains
+---
+
+# Configuring C++ toolchains
+
+* ToC
+{:toc}
+
+## Overview
+
+This tutorial uses an example scenario to describe how to configure C++
+toolchains for a project. It's based on an
+[example C++ project](https://github.com/bazelbuild/examples/tree/master/cpp-tutorial/stage1)
+that builds error-free using `gcc`, `clang`, and `msvc`.
+
+In this tutorial, you will create a Starlark rule that provides additional
+configuration for the `cc_toolchain` so that Bazel can build the application
+with `emscripten`. The expected outcome is to run
+`bazel build --config=asmjs //main:helloworld.js` on a Linux machine and build the
+C++ application using [`emscripten`](https://kripken.github.io/emscripten-site/)
+targeting [`asm.js`](http://asmjs.org/).
+
+## Setting up the build environment
+
+This tutorial assumes you are on Linux on which you have successfully built
+C++ applications - in other words, we assume that appropriate tooling and
+libraries have been installed.
+
+Set up your build environment as follows:
+
+1. If you have not already done so,
+ [download and install Bazel 0.23](install-ubuntu.html) or later.
+
+2. Download the
+ [example C++ project](https://github.com/bazelbuild/examples/tree/master/cpp-tutorial/stage1)
+ from GitHub and place it in an empty directory on your local machine.
+
+
+3. Add the following `cc_binary` target to the `main/BUILD` file:
+
+ ```
+ cc_binary(
+ name = "helloworld.js",
+ srcs = ["hello-world.cc"],
+ )
+ ```
+
+4. Create a `.bazelrc` file at the root of the workspace directory with the
+ following contents to enable the use of the `--config` flag:
+
+ ```
+ # Use our custom-configured c++ toolchain.
+
+ build:asmjs --crosstool_top=//toolchain:emscripten
+
+ # Use --cpu as a differentiator.
+
+ build:asmjs --cpu=asmjs
+
+ # Use the default Bazel C++ toolchain to build the tools used during the
+ # build.
+
+ build:asmjs --host_crosstool_top=@bazel_tools//tools/cpp:toolchain
+ ```
+
+In this example, we are using the `--cpu` flag as a differentiator, since
+`emscripten` can target both `asmjs` and Web assembly. We are not configuring a
+Web assembly toolchain, however. Since Bazel uses many internal tools written in
+C++, such as process-wrapper, we are specifying a "sane" C++ toolchain for the
+host platform.
+
+## Configuring the C++ toolchain
+
+To configure the C++ toolchain, repeatedly build the application and eliminate
+each error one by one as described below.
+
+**Note:** This tutorial assumes you're using Bazel 0.23 or later. If you're
+using an older release of Bazel, look for the "Configuring CROSSTOOL" tutorial.
+
+1. Run the build with the following command:
+
+ ```
+ bazel build --config=asmjs //main:helloworld.js
+ ```
+
+ Because you specified `--crosstool_top=//toolchain:emscripten` in the
+ `.bazelrc` file, Bazel throws the following error:
+
+ ```
+ No such package `toolchain`: BUILD file not found on package path.
+ ```
+
+ In the workspace directory, create the `toolchain` directory for the package
+ and an empty `BUILD` file inside the `toolchain` directory.
+
+2. Run the build again. Because the `toolchain` package does not yet define the
+ `emscripten` target, Bazel throws the following error:
+
+ ```
+ No such target '//toolchain:emscripten': target 'emscripten' not declared in
+ package 'toolchain' defined by .../toolchain/BUILD
+ ```
+
+ In the `toolchain/BUILD` file, define an empty filegroup as follows:
+
+ ```
+ package(default_visibility = ['//visibility:public'])
+ filegroup(name = "emscripten")
+ ```
+
+3. Run the build again. Bazel throws the following error:
+
+ ```
+ '//toolchain:emscripten' does not have mandatory providers: 'ToolchainInfo'
+ ```
+
+ Bazel discovered that the `--crosstool_top` flag points to a rule that
+ doesn't provide the necessary `ToolchainInfo` provider. So we need to point
+ `--crosstool_top` to a rule that does provide `ToolchainInfo` - that is the
+ `cc_toolchain_suite` rule. In the `toolchain/BUILD` file, replace the empty
+ filegroup with the following:
+
+ ```
+ cc_toolchain_suite(
+ name = "emscripten",
+ toolchains = {
+ "asmjs": ":asmjs_toolchain",
+ },
+ )
+ ```
+
+ The `toolchains` attribute automatically maps the `--cpu` (and also
+ `--compiler` when specified) values to `cc_toolchain`. You have not yet
+ defined any `cc_toolchain` targets and Bazel will complain about that
+ shortly.
+
+4. Run the build again. Bazel throws the following error:
+
+ ```
+ Rule '//toolchain:asmjs_toolchain' does not exist
+ ```
+
+ Now you need to define `cc_toolchain` targets for every value in the
+ `cc_toolchain_suite.toolchains` attribute. This is where you specify the
+ files that comprise the toolchain so that Bazel can set up sandboxing. Add
+ the following to the `toolchain/BUILD` file:
+
+ ```
+ filegroup(name = "empty")
+
+ cc_toolchain(
+ name = "asmjs_toolchain",
+ toolchain_identifier = "asmjs-toolchain",
+ toolchain_config = ":asmjs_toolchain_config",
+ all_files = ":empty",
+ compiler_files = ":empty",
+ cpu = "asmjs",
+ dwp_files = ":empty",
+ linker_files = ":empty",
+ objcopy_files = ":empty",
+ strip_files = ":empty",
+ supports_param_files = 0,
+ )
+ ```
+
+5. Run the build again. Bazel throws the following error:
+
+ ```
+ Rule '//toolchain:asmjs-toolchain' does not exist
+ ```
+
+ Let's add a ":asmjs-toolchain-config" target to the `toolchain/BUILD` file:
+
+ ```
+ filegroup(name = "asmjs_toolchain_config")
+ ```
+
+6. Run the build again. Bazel throws the following error:
+
+ ```
+ '//toolchain:asmjs_toolchain_config' does not have mandatory providers:
+ 'CcToolchainConfigInfo'
+ ```
+
+ `CcToolchainConfigInfo` is a provider that we use to configure our C++
+ toolchains. We are going to create a Starlark rule that will provide
+ `CcToolchainConfigInfo`. Create a `toolchains/cc_toolchain_config.bzl`
+ file with the following content:
+ ```
+ def _impl(ctx):
+ return cc_common.create_cc_toolchain_config_info(
+ ctx = ctx,
+ toolchain_identifier = "asmjs-toolchain",
+ host_system_name = "i686-unknown-linux-gnu",
+ target_system_name = "asmjs-unknown-emscripten",
+ target_cpu = "asmjs",
+ target_libc = "unknown",
+ compiler = "emscripten",
+ abi_version = "unknown",
+ abi_libc_version = "unknown",
+ )
+
+ cc_toolchain_config = rule(
+ implementation = _impl,
+ attrs = {},
+ provides = [CcToolchainConfigInfo],
+ )
+ ```
+
+ `cc_common.create_cc_toolchain_config_info()` creates the needed provider
+ `CcToolchainConfigInfo`. Now let's declare a rule that will make use of
+ the newly implemented `cc_toolchain_config` rule. Add a load statement to
+ `toolchains/BUILD`:
+
+ ```
+ load(":cc_toolchain_config.bzl", "cc_toolchain_config")
+ ```
+
+ And replace the "asmjs_toolchain_config" filegroup with a declaration of a
+ `cc_toolchain_config` rule:
+
+ ```
+ cc_toolchain_config(name = "asmjs_toolchain_config")
+ ```
+
+7. Run the build again. Bazel throws the following error:
+
+ ```
+ .../BUILD:1:1: C++ compilation of rule '//:helloworld.js' failed (Exit 1)
+ src/main/tools/linux-sandbox-pid1.cc:421:
+ "execvp(toolchain/DUMMY_GCC_TOOL, 0x11f20e0)": No such file or directory
+ Target //:helloworld.js failed to build`
+ ```
+
+ At this point, Bazel has enough information to attempt building the code but
+ it still does not know what tools to use to complete the required build
+ actions. We will modify our Starlark rule implementation to tell Bazel what
+ tools to use. For that, we'll need the tool_path() constructor from
+ [`@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl`](https://source.bazel.build/bazel/+/4eea5c62a566d21832c93e4c18ec559e75d5c1ce:tools/cpp/cc_toolchain_config_lib.bzl;l=400):
+
+ ```
+ # toolchain/cc_toolchain_config.bzl:
+ load("@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl", "tool_path")
+
+ def impl(ctx):
+ tool_paths = [
+ tool_path(
+ name = "gcc",
+ path = "emcc.sh",
+ ),
+ tool_path(
+ name = "ld",
+ path = "emcc.sh",
+ ),
+ tool_path(
+ name = "ar",
+ path = "/bin/false",
+ ),
+ tool_path(
+ name = "cpp",
+ path = "/bin/false",
+ ),
+ tool_path(
+ name = "gcov",
+ path = "/bin/false",
+ ),
+ tool_path(
+ name = "nm",
+ path = "/bin/false",
+ ),
+ tool_path(
+ name = "objdump",
+ path = "/bin/false",
+ ),
+ tool_path(
+ name = "strip",
+ path: "/bin/false",
+ ),
+ ]
+ return cc_common.create_cc_toolchain_config_info(
+ ctx = ctx,
+ toolchain_identifier = "asmjs-toolchain",
+ host_system_name = "i686-unknown-linux-gnu",
+ target_system_name = "asmjs-unknown-emscripten",
+ target_cpu = "asmjs",
+ target_libc = "unknown",
+ compiler = "emscripten",
+ abi_version = "unknown",
+ abi_libc_version = "unknown",
+ tool_paths = tools,
+ )
+ ```
+
+ You may notice the `emcc.sh` wrapper script, which delegates to the external
+ `emcc.py` file. Create the script in the `toolchain` package directory with
+ the following contents and set its executable bit:
+
+ ```
+ #!/bin/bash
+ set -euo pipefail
+ python external/emscripten_toolchain/emcc.py "$@"
+ ```
+
+ Paths specified in the `tool_paths` list are relative to the package where
+ the `cc_toolchain_config` target is specified.
+
+ The `emcc.py` file does not yet exist in the workspace directory. To obtain
+ it, you can either check the `emscripten` toolchain in with your project or
+ pull it from its GitHub repository. This tutorial uses the latter approach.
+ To pull the toolchain from the GitHub repository, add the following
+ `http_archive` repository definitions to your `WORKSPACE` file:
+
+ ```
+ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+ http_archive(
+ name = 'emscripten_toolchain',
+ url = 'https://github.com/kripken/emscripten/archive/1.37.22.tar.gz',
+ build_file = 'emscripten-toolchain.BUILD',
+ strip_prefix = "emscripten-1.37.22",
+ )
+
+ http_archive(
+ name = 'emscripten_clang',
+ url = 'https://s3.amazonaws.com/mozilla-games/emscripten/packages/llvm/tag/linux_64bit/emscripten-llvm-e1.37.22.tar.gz',
+ build_file = 'emscripten-clang.BUILD',
+ strip_prefix = "emscripten-llvm-e1.37.22",
+ )
+ ```
+
+ In the workspace directory root, create the `emscripten-toolchain.BUILD` and
+ `emscripten-clang.BUILD` files that expose these repositories as filegroups
+ and establish their visibility across the build.
+
+ First create the `emscripten-toolchain.BUILD` file with the following
+ contents:
+
+ ```
+ package(default_visibility = ['//visibility:public'])
+
+ filegroup(
+ name = "all",
+ srcs = glob(["**/*"]),
+ )
+ ```
+
+ Next, create the `emscripten-clang.BUILD` file with the following contents:
+
+ ```
+ package(default_visibility = ['//visibility:public'])`
+
+ filegroup(
+ name = "all",
+ srcs = glob(["**/*"]),
+ )
+ ```
+
+ You may notice that the targets simply parse all of the files contained in
+ the archives pulled by the `http_archive` repository rules. In a real
+ world scenario, you would likely want to be more selective and granular by
+ only parsing the files needed by the build and splitting them by action,
+ such as compilation, linking, and so on. For the sake of simplicity, this
+ tutorial omits this step.
+
+8. Run the build again. Bazel throws the following error:
+
+ ```
+ "execvp(toolchain/emcc.sh, 0x12bd0e0)": No such file or directory
+ ```
+
+ You now need to make Bazel aware of the artifacts you added in the previous
+ step. In particular, the `emcc.sh` script must also be explicitly listed as
+ a dependency of the corresponding `cc_toolchain` rule. Modify the
+ `toolchain/BUILD` file to look as follows:
+
+ ```
+ package(default_visibility = ["//visibility:public"])
+
+ load(":cc_toolchain_config.bzl", "cc_toolchain_config")
+
+ cc_toolchain_config(name = "asmjs_toolchain_config")
+
+ cc_toolchain_suite(
+ name = "emscripten",
+ toolchains = {
+ "asmjs": ":asmjs_toolchain",
+ },
+ )
+
+ filegroup(
+ name = "all",
+ srcs = [
+ "emcc.sh",
+ "@emscripten_clang//:all",
+ "@emscripten_toolchain//:all",
+ ],
+ )
+
+ cc_toolchain(
+ name = "asmjs_toolchain",
+ toolchain_identifier = "asmjs-toolchain",
+ toolchain_config = ":asmjs_toolchain_config",
+ all_files = ":all",
+ compiler_files = ":all",
+ cpu = "asmjs",
+ dwp_files = ":empty",
+ linker_files = ":all",
+ objcopy_files = ":empty",
+ strip_files = ":empty",
+ supports_param_files = 0,
+ )
+ ```
+
+ Congratulations! You are now using the `emscripten` toolchain to build your
+ C++ sample code. The next steps are optional but are included for
+ completeness.
+
+
+9. (Optional) Run the build again. Bazel throws the following error:
+
+ ```
+ ERROR: .../BUILD:1:1: C++ compilation of rule '//:helloworld.js' failed (Exit 1)
+ ```
+
+ The next step is to make the toolchain deterministic and hermetic - that
+ is, limit it to only touch files it's supposed to touch and ensure it
+ doesn't write temporary data outside the sandbox.
+
+ You also need to ensure the toolchain does not assume the existence of your
+ home directory with its configuration files and that it does not depend on
+ unspecified environment variables.
+
+ For our example project, make the following modifications to the
+ `toolchain/BUILD` file:
+
+ ```
+ filegroup(
+ name = "all",
+ srcs = [
+ "emcc.sh",
+ "@emscripten_toolchain//:all",
+ "@emscripten_clang//:all",
+ ":emscripten_cache_content"
+ ],
+ )
+
+ filegroup(
+ name = "emscripten_cache_content",
+ srcs = glob(["emscripten_cache/**/*"]),
+ )
+ ```
+
+ Since `emscripten` caches standard library files, you can save time by not
+ compiling `stdlib` for every action and also prevent it from storing
+ temporary data in random place, check in the precompiled bitcode files into
+ the `toolchain/emscript_cache directory`. You can create them by calling
+ the following from the `emscripten_clang` repository (or let `emscripten`
+ create them in `~/.emscripten_cache`):
+
+ ```
+ python embuilder.py build dlmalloc libcxx libc gl libcxxabi libcxx_noexcept wasm-libc
+ ```
+
+ Copy those files to `toolchain/emscripten_cache`.
+ Also update the `emcc.sh` script to look as follows:
+
+ ```
+ #!/bin/bash
+
+ set -euo pipefail
+
+ export LLVM_ROOT='external/emscripten_clang'
+ export EMSCRIPTEN_NATIVE_OPTIMIZER='external/emscripten_clang/optimizer'
+ export BINARYEN_ROOT='external/emscripten_clang/'
+ export NODE_JS=''
+ export EMSCRIPTEN_ROOT='external/emscripten_toolchain'
+ export SPIDERMONKEY_ENGINE=''
+ export EM_EXCLUSIVE_CACHE_ACCESS=1
+ export EMCC_SKIP_SANITY_CHECK=1
+ export EMCC_WASM_BACKEND=0
+
+ mkdir -p "tmp/emscripten_cache"
+
+ export EM_CACHE="tmp/emscripten_cache"
+ export TEMP_DIR="tmp"
+
+ # Prepare the cache content so emscripten doesn't keep rebuilding it
+ cp -r toolchain/emscripten_cache/* tmp/emscripten_cache
+
+ # Run emscripten to compile and link
+ python external/emscripten_toolchain/emcc.py "$@"
+
+ # Remove the first line of .d file
+ find . -name "*.d" -exec sed -i '2d' {} \;
+ ```
+
+ Bazel can now properly compile the sample C++ code in `hello-world.cc`.
+
+
+10. (Optional) Run the build again. Bazel throws the following error:
+
+ ```
+ ..../BUILD:1:1: undeclared inclusion(s) in rule '//:helloworld.js':
+ this rule is missing dependency declarations for the following files included by 'helloworld.cc':
+ '.../external/emscripten_toolchain/system/include/libcxx/stdio.h'
+ '.../external/emscripten_toolchain/system/include/libcxx/__config'
+ '.../external/emscripten_toolchain/system/include/libc/stdio.h'
+ '.../external/emscripten_toolchain/system/include/libc/features.h'
+ '.../external/emscripten_toolchain/system/include/libc/bits/alltypes.h'
+ ```
+
+ At this point you have successfully compiled the example C++ code. The
+ error above occurs because Bazel uses a `.d` file produced by the compiler
+ to verify that all includes have been declared and to prune action inputs.
+
+ In the `.d` file, Bazel discovered that our source code references system
+ headers that have not been explicitly declared in the `BUILD` file. This in
+ and of itself is not a problem and you can easily fix this by adding the
+ target folders as `-isystem` directories. For this, you'll need to add
+ a [`feature`](https://source.bazel.build/bazel/+/4eea5c62a566d21832c93e4c18ec559e75d5c1ce:tools/cpp/cc_toolchain_config_lib.bzl;l=336) to the `CcToolchainConfigInfo`.
+ Modify `toolchain/cc_toolchain_config.bzl` to look like this:
+ ```
+ load("@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl",
+ "feature",
+ "flag_group",
+ "flag_set",
+ "tool_path")
+ load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES")
+
+ def _impl(ctx):
+ tool_paths = [
+ tool_path(
+ name = "gcc",
+ path = "emcc.sh",
+ ),
+ tool_path(
+ name = "ld",
+ path = "emcc.sh",
+ ),
+ tool_path(
+ name = "ar",
+ path = "/bin/false",
+ ),
+ tool_path(
+ name = "cpp",
+ path = "/bin/false",
+ ),
+ tool_path(
+ name = "gcov",
+ path = "/bin/false",
+ ),
+ tool_path(
+ name = "nm",
+ path = "/bin/false",
+ ),
+ tool_path(
+ name = "objdump",
+ path = "/bin/false",
+ ),
+ tool_path(
+ name = "strip",
+ path = "/bin/false",
+ ),
+ ]
+ toolchain_include_directories_feature = feature(
+ name = "toolchain_include_directories",
+ enabled = True,
+ flag_sets = [
+ flag_set(
+ actions = [
+ "ACTION_NAMES.assemble",
+ "ACTION_NAMES.preprocess_assemble",
+ "ACTION_NAMES.linkstamp_compile",
+ "ACTION_NAMES.c_compile",
+ "ACTION_NAMES.cpp_compile",
+ "ACTION_NAMES.cpp_header_parsing",
+ "ACTION_NAMES.cpp_module_compile",
+ "ACTION_NAMES.cpp_module_codegen",
+ "ACTION_NAMES.lto_backend",
+ "ACTION_NAMES.clif_match",
+ ],
+ flag_groups = [
+ flag_group(
+ flags = [
+ "-isystem",
+ "external/emscripten_toolchain/system/include/libcxx",
+ "-isystem",
+ "external/emscripten_toolchain/system/include/libc",
+ ],
+ ),
+ ],
+ ),
+ ],
+ )
+
+ return cc_common.create_cc_toolchain_config_info(
+ ctx = ctx,
+ toolchain_identifier = "asmjs-toolchain",
+ host_system_name = "i686-unknown-linux-gnu",
+ target_system_name = "asmjs-unknown-emscripten",
+ target_cpu = "asmjs",
+ target_libc = "unknown",
+ compiler = "emscripten",
+ abi_version = "unknown",
+ abi_libc_version = "unknown",
+ tool_paths = tool_paths,
+ features = [toolchain_include_directories_feature],
+ )
+
+ cc_toolchain_config = rule(
+ implementation = _impl,
+ attrs = {},
+ provides = [CcToolchainConfigInfo],
+ )
+ ```
+
+11. (Optional) Run the build again. With this final change, the build now
+ completes error-free.
diff --git a/site/docs/tutorial/crosstool.md b/site/docs/tutorial/crosstool.md
deleted file mode 100644
index 8551f92..0000000
--- a/site/docs/tutorial/crosstool.md
+++ /dev/null
@@ -1,526 +0,0 @@
----
-layout: documentation
-title: Configuring CROSSTOOL
----
-
-# Configuring CROSSTOOL
-
-* ToC
-{:toc}
-
-## Overview
-
-This tutorial uses an example scenario to describe how to configure CROSSTOOL
-for a project. It's based on an
-[example C++ project](https://github.com/bazelbuild/examples/tree/master/cpp-tutorial/stage3)
-that builds error-free using `gcc`, `clang`, and `msvc`.
-
-In this tutorial, you configure a CROSSTOOL file so that Bazel can build the
-application with `emscripten`. The expected outcome is to run
-`bazel build --config=asmjs test/helloworld.js` on a Linux machine and build the
-C++ application using [`emscripten`](https://kripken.github.io/emscripten-site/)
-targeting [`asm.js`](http://asmjs.org/).
-
-## Setting up the build environment
-
-This tutorial assumes you are on Linux on which you have successfully built
-C++ applications - in other words, we assume that appropriate tooling and
-libraries have been installed.
-
-Set up your build environment as follows:
-
-1. If you have not already done so,
- [download and install Bazel 0.19](install-ubuntu.html) or later.
-
-2. Download the
- [example C++ project](https://github.com/bazelbuild/examples/tree/master/cpp-tutorial/stage3)
- from GitHub and place it in an empty directory on your local machine.
-
-
-3. Add the following `cc_binary` target to the `main/BUILD` file:
-
- ```
- cc_binary(
- name = "helloworld.js",
- srcs = ["helloworld.cc"],
- )
- ```
-
-4. Create a `.bazelrc` file at the root of the workspace directory with the
- following contents to enable the use of the `--config` flag:
-
- ```
- # Create a new CROSSTOOL file for our toolchain.
-
- build:asmjs --crosstool_top=//toolchain:emscripten
-
- # Use --cpu as a differentiator.
-
- build:asmjs --cpu=asmjs
-
- # Specify a "sane" C++ toolchain for the host platform.
-
- build:asmjs --host_crosstool_top=@bazel_tools//tools/cpp:toolchain
- ```
-
-In this example, we are using the `--cpu` flag as a differentiator, since
-`emscripten` can target both `asmjs` and Web assembly. We are not configuring a
-Web assembly toolchain, however. Since Bazel uses many internal tools written in
-C++, such as process-wrapper, we are specifying a "sane" C++ toolchain for the
-host platform.
-
-## Configuring the C++ toolchain
-
-To configure the C++ toolchain, repeatedly build the application and eliminate
-each error one by one as described below.
-
-**Note:** This tutorial assumes you're using Bazel 0.19 or later. If you're
-using an older release of Bazel, the build errors listed may appear in a
-different order, but the configuration procedure is the same.
-
-1. Run the build with the following command:
-
- ```
- bazel build --config=asmjs helloworld.js
- ```
-
- Because you specified `--crosstool_top=//toolchain:emscripten` in the
- `.bazelrc` file, Bazel throws the following error:
-
- ```
- No such package `toolchain`: BUILD file not found on package path.
- ```
-
- In the workspace directory, create the `toolchain` directory for the package
- and an empty `BUILD` file inside the `toolchain` directory.
-
-2. Run the build again. Because the `toolchain` package does not yet define the
- `emscripten` target, Bazel throws the following error:
-
- ```
- No such target '//toolchain:emscripten': target 'emscripten' not declared in
- package 'toolchain' defined by .../toolchain/BUILD
- ```
-
- In the `toolchain/BUILD` file, define an empty filegroup as follows:
-
- ```
- package(default_visibility = ['//visibility:public'])
- filegroup(name = "emscripten")
- ```
-
-3. Run the build again. Bazel throws the following error:
-
- ```
- The specified --crosstool_top '//toolchain:emscripten' is not a valid
- cc_toolchain_suite rule.
- ```
-
- Bazel discovered that the `--crosstool_top` flag does not point to the
- `cc_toolchain_suite` rule. In the `toolchain/BUILD` file, replace the empty
- filegroup with the following:
-
- ```
- cc_toolchain_suite(
- name = "emscripten",
- toolchains = {
- "asmjs": ":asmjs_toolchain",
- "asmjs|emscripten": ":asmjs_toolchain",
- },
- )
- ```
-
- The `toolchains` attribute automatically maps the `--cpu` (and also
- `--compiler` when specified) values to `cc_toolchain`. You have not yet
- defined any `cc_toolchain` targets and Bazel will complain about that
- shortly.
-
-4. Run the build again. Bazel throws the following error:
-
- ```
- The crosstool_top you specified was resolved to '//toolchain:emscripten',
- which does not contain a CROSSTOOL file.
- ```
-
- Bazel expects a `CROSSTOOL` file in the `tooolchain:emscripten` package.
- Create an empty `CROSSTOOL` file inside the `toolchain` directory.
-
-5. Run the build again. Bazel throws the following error:
-
- ```
- Could not read the crosstool configuration file
- 'CROSSTOOL file .../toolchain/CROSSTOOL', because of an incomplete protocol
- buffer (Message missing required fields: major_version, minor_version, default_target_cpu)
- ```
-
- Bazel read the `CROSSTOOL` file and found nothing inside. Populate the
- `CROSTOOL` file as follows:
-
- ```
- major_version: "1"
- minor_version: "0"
- default_target_cpu: "asmjs"
- ```
-
-6. Run the build again. Bazel throws the following error:
-
- ```
- The label '//toolchain:asmjs_toolchain' is not a cc_toolchain rule.
- ```
-
- This is an important milestone in which you define `cc_toolchain` targets
- for every toolchain in the `CROSSTOOL` file. This is where you specify the
- files that comprise the toolchain so that Bazel can set up sandboxing. Add
- the following to the `toolchain/BUILD` file:
-
- ```
- filegroup(name = "empty")
-
- cc_toolchain(
- name = "asmjs_toolchain",
- toolchain_identifier = "asmjs-toolchain",
- all_files = ":empty",
- compiler_files = ":empty",
- cpu = "asmjs",
- dwp_files = ":empty",
- linker_files = ":empty",
- objcopy_files = ":empty",
- strip_files = ":empty",
- supports_param_files = 0,
- )
- ```
-
-7. Run the build again. Bazel throws the following error:
-
- ```
- No toolchain found for cpu 'asmjs'.
- ```
-
- Since you have specified `--crosstool_top` and `--cpu` in the `.bazelrc`
- file, `//toolchain:asmjs_toolchain` is selected. Because we specify
- `toolchain_identifier = "asmjs-toolchain"`, we need to create a toolchain
- definition with this identifier. Add the following to the `CROSTOOL` file:
-
- ```
- toolchain {
- toolchain_identifier: "asmjs-toolchain"
- host_system_name: "i686-unknown-linux-gnu"
- target_system_name: "asmjs-unknown-emscripten"
- target_cpu: "asmjs"
- target_libc: "unknown"
- compiler: "emscripten"
- abi_version: "unknown"
- abi_libc_version: "unknown"
- }
- ```
-
- The above definition also specifies the compiler, which you can use to more
- precisely select the C++ toolchain.
-
- Because we want to omit the `--compiler` flag and only use the `--cpu` flag,
- we have added a `asmjs` key into `cc_toolchain_suite.toolchains`.
-
-8. Run the build again. Bazel throws the following error:
-
- ```
- .../BUILD:1:1: C++ compilation of rule '//:helloworld.js' failed (Exit 1)
- src/main/tools/linux-sandbox-pid1.cc:421:
- "execvp(toolchain/DUMMY_GCC_TOOL, 0x11f20e0)": No such file or directory
- Target //:helloworld.js failed to build`
- ```
-
- At this point, Bazel has enough information to attempt building the code but
- it still does not know what tools to use to complete the required build
- actions. Add the following to your `CROSSTOOL` file to tell Bazel what tools
- to use:
-
- ```
- # toolchain/CROSSTOOL
- # ...
- tool_path {
- name: "gcc"
- path: "emcc.sh"
- }
- tool_path {
- name: "ld"
- path: "emcc.sh"
- }
- tool_path {
- name: "ar"
- path: "/bin/false"
- }
- tool_path {
- name: "cpp"
- path: "/bin/false"
- }
- tool_path {
- name: "gcov"
- path: "/bin/false"
- }
- tool_path {
- name: "nm"
- path: "/bin/false"
- }
- tool_path {
- name: "objdump"
- path: "/bin/false"
- }
- tool_path {
- name: "strip"
- path: "/bin/false"
- }
- ```
-
- You may notice the `emcc.sh` wrapper script, which delegates to the external
- `emcc.py` file. Create the script in the `toolchain` package directory with
- the following contents and set its executable bit:
-
- ```
- #!/bin/bash
- set -euo pipefail
- python external/emscripten_toolchain/emcc.py "$@"
- ```
-
- Paths specified in the `CROSSTOOL` file are relative to the location of the
- `CROSSTOOL` file itself.
-
- The `emcc.py` file does not yet exist in the workspace directory. To obtain
- it, you can either check the `emscripten` toolchain in with your project or
- pull it from its GitHub repository. This tutorial uses the latter approach.
- To pull the toolchain from the GitHub repository, add the following
- `new_http_archive` repository definitions to your `WORKSPACE` file:
-
- ```
- new_http_archive(
- name = 'emscripten_toolchain',
- url = 'https://github.com/kripken/emscripten/archive/1.37.22.tar.gz',
- build_file = 'emscripten-toolchain.BUILD',
- strip_prefix = "emscripten-1.37.22",
- )
-
- new_http_archive(
- name = 'emscripten_clang',
- url = 'https://s3.amazonaws.com/mozilla-games/emscripten/packages/llvm/tag/linux_64bit/emscripten-llvm-e1.37.22.tar.gz',
- build_file = 'emscripten-clang.BUILD',
- strip_prefix = "emscripten-llvm-e1.37.22",
- )
- ```
-
- In the workspace directory root, create the `emscripten-toolchain.BUILD` and
- `emscripten-clang.BUILD` files that expose these repositories as filegroups
- and establish their visibility across the build.
-
- First create the `emscripten-toolchain.BUILD` file with the following
- contents:
-
- ```
- package(default_visibility = ['//visibility:public'])
-
- filegroup(
- name = "all",
- srcs = glob(["**/*"]),
- )
- ```
-
- Next, create the `emscripten-clang.BUILD` file with the following contents:
-
- ```
- package(default_visibility = ['//visibility:public'])`
-
- filegroup(
- name = "all",
- srcs = glob(["**/*"]),
- )
- ```
-
- You may notice that the targets simply parse all of the files contained in
- the archives pulled by the `new_http_archive` repository rules. In a real
- world scenario, you would likely want to be more selective and granular by
- only parsing the files needed by the build and splitting them by action,
- such as compilation, linking, and so on. For the sake of simplicity, this
- tutorial omits this step.
-
-9. Run the build again. Bazel throws the following error:
-
- ```
- "execvp(toolchain/emcc.sh, 0x12bd0e0)": No such file or directory
- ```
-
- You now need to make Bazel aware of the artifacts you added in the previous
- step. In particular, the `emcc.sh` script must also be explicitly listed as
- a dependency of the corresponding `cc_toolchain` rule. Modify the
- `toolchain/BUILD` file to look as follows:
-
- ```
- package(default_visibility = ['//visibility:public'])
-
- cc_toolchain_suite(
- name = "emscripten",
- toolchains = {
- "asmjs": ":asmjs_toolchain",
- "asmjs|emscripten": ":asmjs_toolchain",
- },
- )
-
- filegroup(name = "empty")
-
- filegroup(
- name = "all",
- srcs = [
- "emcc.sh",
- "@emscripten_toolchain//:all",
- "@emscripten_clang//:all"
- ],
- )
-
- cc_toolchain(
- name = "asmjs_toolchain",
- toolchain_identifier = "asmjs-toolchain",
- all_files = ":all",
- compiler_files = ":all",
- cpu = "asmjs",
- dwp_files = ":empty",
- linker_files = ":all",
- objcopy_files = ":empty",
- strip_files = ":empty",
- supports_param_files = 0,
- )
- ```
-
- Congratulations! You are now using the `emscripten` toolchain to build your
- C++ sample code. The next steps are optional but are included for
- completeness.
-
-
-10. (Optional) Run the build again. Bazel throws the following error:
-
- ```
- ERROR: .../BUILD:1:1: C++ compilation of rule '//:helloworld.js' failed (Exit 1)
- ```
-
- The next step is to make the toolchain deterministic and hermetic - that
- is, limit it to only touch files it's supposed to touch and ensure it
- doesn't write temporary data outside the sandbox.
-
- You also need to ensure the toolchain does not assume the existence of your
- home directory with its configuration files and that it does not depend on
- unspecified environment variables.
-
- For our example project, make the following modifications to the
- `toolchain/BUILD` file:
-
- ```
- filegroup(
- name = "all",
- srcs = [
- "emcc.sh",
- "@emscripten_toolchain//:all",
- "@emscripten_clang//:all",
- ":emscripten_cache_content"
- ],
- )
-
- filegroup(
- name = "emscripten_cache_content",
- srcs = glob(["emscripten_cache/**/*"]),
- )
- ```
-
- Since `emscripten` caches standard library files, you can save time by not
- compiling `stdlib` for every action and also prevent it from storing
- temporary data in random place, check in the precompiled bitcode files into
- the `toolchain/emscript_cache directory`. You can create them by calling
- the following from the `emscripten_clang` repository (or let `emscripten`
- create them in `~/.emscripten_cache`):
-
- ```
- embuilder.py build dlmalloc libcxx libc gl libcxxabi libcxx_noexcept wasm-libc
- ```
-
- Copy those files to `toolchain/emscripten_cache`. Modify your `toolchain/BUILD`
- file to look as follows:
-
- ```
-
- filegroup(
- name = "all",
- srcs = [
- "emcc.sh",
- "@emscripten_toolchain//:all",
- "@emscripten_clang//:all",
- ":emscripten_cache_content"
- ],
- )
-
- filegroup(
- name = "emscripten_cache_content",
- srcs = glob(["emscripten_cache/**/*"]),
- )
- ```
-
- Also update the `emcc.sh` script to look as follows:
-
- ```
- #!/bin/bash
-
- set -euo pipefail
-
- export LLVM_ROOT='external/emscripten_clang'
- export EMSCRIPTEN_NATIVE_OPTIMIZER='external/emscripten_clang/optimizer'
- export BINARYEN_ROOT='external/emscripten_clang/'
- export NODE_JS=''
- export EMSCRIPTEN_ROOT='external/emscripten_toolchain'
- export SPIDERMONKEY_ENGINE=''
- export EM_EXCLUSIVE_CACHE_ACCESS=1
- export EMCC_SKIP_SANITY_CHECK=1
- export EMCC_WASM_BACKEND=0
-
- mkdir -p "tmp/emscripten_cache"
-
- export EM_CACHE="tmp/emscripten_cache"
- export TEMP_DIR="tmp"
-
- # Prepare the cache content so emscripten doesn't keep rebuilding it
- cp -r toolchain/emscripten_cache/* tmp/emscripten_cache
-
- # Run emscripten to compile and link
- python external/emscripten_toolchain/emcc.py "$@"
-
- # Remove the first line of .d file
- find . -name "*.d" -exec sed -i '2d' {} \;
- ```
-
- Bazel can now properly compile the sample C++ code in `helloworld.cc`.
-
-
-11. (Optional) Run the build again. Bazel throws the following error:
-
- ```
- ..../BUILD:1:1: undeclared inclusion(s) in rule '//:helloworld.js':
- this rule is missing dependency declarations for the following files included by 'helloworld.cc':
- '.../external/emscripten_toolchain/system/include/libcxx/stdio.h'
- '.../external/emscripten_toolchain/system/include/libcxx/__config'
- '.../external/emscripten_toolchain/system/include/libc/stdio.h'
- '.../external/emscripten_toolchain/system/include/libc/features.h'
- '.../external/emscripten_toolchain/system/include/libc/bits/alltypes.h'
- ```
-
- At this point you have successfully compiled the example C++ code. The
- error above occurs because Bazel uses a `.d` file produced by the compiler
- to verify that all includes have been declared and to prune action inputs.
-
- In the `.d` file, Bazel discovered that our source code references system
- headers that have not been explicitly declared in the `BUILD` file. This in
- and of itself is not a problem and you can easily fix this by adding the
- target folders as `-isystem` directories to the toolchain definition in the
- `CROSSTOOL` file as follows:
-
- ```
- compiler_flag: "-isystem"
- compiler_flag: "external/emscripten_toolchain/system/include/libcxx"
- compiler_flag: "-isystem"
- compiler_flag: "external/emscripten_toolchain/system/include/libc"
- ```
-
-12. (Optional) Run the build again. With this final change, the build now
- completes error-free.