1Configuring a custom C toolchain 2================================ 3 4.. External links are here 5.. _Configuring CROSSTOOL: https://docs.bazel.build/versions/0.23.0/tutorial/crosstool.html 6.. _Understanding CROSSTOOL: https://docs.bazel.build/versions/0.23.0/crosstool-reference.html 7.. _Configuring C++ toolchains: https://docs.bazel.build/versions/master/tutorial/cc-toolchain-config.html 8.. _cc_library: https://docs.bazel.build/versions/master/be/c-cpp.html#cc_library 9.. _crosstool_config.proto: https://github.com/bazelbuild/bazel/blob/master/src/main/protobuf/crosstool_config.proto 10.. _go_binary: docs/go/core/rules.md#go_binary 11.. _go_library: docs/go/core/rules.md#go_library 12.. _toolchain: https://docs.bazel.build/versions/master/be/platform.html#toolchain 13.. _#1642: https://github.com/bazelbuild/rules_go/issues/1642 14 15References 16---------- 17 18* `Configuring CROSSTOOL`_ 19* `Understanding CROSSTOOL`_ 20* `Configuring C++ toolchains`_ 21 22Introduction 23------------ 24 25**WARNING:** This documentation is out of date. Some of the linked Bazel 26documentation has been deleted in later versions, and there are a number of 27TODOs. In particular, building and configuring a cross-compiling C++ toolchain 28and testing it with cgo should be covered. `#1642`_ tracks progress on this. 29 30The Go toolchain sometimes needs access to a working C/C++ toolchain in order to 31produce binaries that contain cgo code or require external linking. rules_go 32uses whatever C/C++ toolchain Bazel is configured to use. This means 33`go_library`_ and `cc_library`_ rules can be linked into the same binary (via 34the ``cdeps`` attribute in Go rules). 35 36Bazel uses a CROSSTOOL file to configure the C/C++ toolchain, plus a few build 37rules that declare constraints, dependencies, and file groups. By default, Bazel 38will attempt to detect the toolchain installed on the host machine. This works 39in most cases, but it's not hermetic (developers may have completely different 40toolchains installed), and it doesn't always work with remote execution. It also 41doesn't work with cross-compilation. Explicit configuration is required in these 42situations. 43 44This documented is intended to serve as a walk-through for configuring a custom 45C/C++ toolchain for use with rules_go. 46 47NOTE: The Go toolchain requires gcc, clang, or something that accepts the same 48command-line arguments and produce the same error messages. MSVC is not 49supported. This is a limitation of the Go toolchain, not rules_go. cgo infers 50the types of C definitions based on the text of error messages. 51 52TODO: Change the example to use a cross-compiling toolchain. 53 54TODO: Add instructions for building a C compiler from scratch. 55 56TODO: Build the standard C library and binutils for use with this toolchain. 57 58Tutorial 59-------- 60 61In this tutorial, we'll download a binary Clang release and install it into 62a new workspace. This workspace can be uploaded into a new repository and 63referenced from other Bazel workspaces. 64 65You can find a copy of the example repository described here at 66`https://github.com/jayconrod/bazel_cc_toolchains <https://github.com/jayconrod/bazel_cc_toolchains>`_. 67 68Step 1: Create the repository 69~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 70 71Create a new repository and add a WORKSPACE file to the root directory. It 72may be empty, but it's probably a good idea to give it a name. 73 74.. code:: bash 75 76 $ cat >WORKSPACE <<EOF 77 workspace(name = "bazel_cc_toolchains") 78 EOF 79 80Step 2: Download a toolchain 81~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 82 83Download or compile a C toolchain, and install it in a subdirectory of your 84workspace. I put it in ``tools``. 85 86Note that this toolchain has Unixy subdirectories like ``bin``, ``lib``, and 87``include``. 88 89.. code:: bash 90 91 $ curl http://releases.llvm.org/7.0.0/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz | tar xJ 92 $ mv clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04 tools 93 $ ls tools 94 bin include lib libexec share 95 96Step 3: Write a CROSSTOOL 97~~~~~~~~~~~~~~~~~~~~~~~~~ 98 99We'll create a file named ``tools/CROSSTOOL``, which describes our toolchain 100to Bazel. If you have more than one C/C++ toolchain (e.g., different tools for 101debug and optimized builds, or different compilers for different platforms), 102they should all be configured in the same ``CROSSTOOL`` file. 103 104The format for this file is defined in `crosstool_config.proto`_. Specifically, 105CROSSTOOL should contain a ``CrosstoolRelease`` message, formatted as text. 106Each ``toolchain`` field is a ``CToolchain`` message. 107 108Here's a short example: 109 110.. code:: proto 111 112 major_version: "local" 113 minor_version: "" 114 115 toolchain { 116 toolchain_identifier: "clang" 117 host_system_name: "linux" 118 target_system_name: "linux" 119 target_cpu: "x86_64" 120 target_libc: "x86_64" 121 compiler: "clang" 122 abi_version: "unknown" 123 abi_libc_version: "unknown" 124 125 tool_path { name: "ar" path: "bin/llvm-ar" } 126 tool_path { name: "cpp" path: "bin/clang-cpp" } 127 tool_path { name: "dwp" path: "bin/llvm-dwp" } 128 tool_path { name: "gcc" path: "bin/clang" } 129 tool_path { name: "gcov" path: "bin/llvm-profdata" } 130 tool_path { name: "ld" path: "bin/ld.lld" } 131 tool_path { name: "nm" path: "bin/llvm-nm" } 132 tool_path { name: "objcopy" path: "bin/llvm-objcopy" } 133 tool_path { name: "objdump" path: "bin/llvm-objdump" } 134 tool_path { name: "strip" path: "bin/llvm-strip" } 135 136 compiler_flag: "-no-canonical-prefixes" 137 linker_flag: "-no-canonical-prefixes" 138 139 compiler_flag: "-v" 140 cxx_builtin_include_directory: "/usr/include" 141 } 142 143 default_toolchain { 144 cpu: "x86_64" 145 toolchain_identifier: "clang" 146 } 147 148For a more complete example, build any ``cc_binary`` with Bazel without 149explicitly configuring ``CROSSTOOL``, then look at the ``CROSSTOOL`` that 150Bazel generates for the automatically detected host toolchain. This can 151be found in ``$(bazel info 152output_base)/external/bazel_tools/tools/cpp/CROSSTOOL``. (You have to build 153something with the host toolchain before this will show up). 154 155Some notes: 156 157* ``toolchain_identifier`` is the main name for the toolchain. You'll refer to 158 it using this identifier from other messages and from build files. 159* Most of the other fields at the top of ``toolchain`` are descriptive and 160 can have any value. 161* ``tool_path`` fields describe the various tools Bazel may invoke. The paths 162 are relative to the directory that contains the ``CROSSTOOL`` file. 163* ``compiler_flag`` and ``linker_flag`` are passed to the compiler and linker 164 on each invocation, respectively. 165* ``cxx_builtin_include_directory`` is a directory with include files that 166 the compiler may read. Without this declaration, these files won't be 167 visible in the sandbox. (TODO: make this hermetic). 168 169Step 4: Write a build file 170~~~~~~~~~~~~~~~~~~~~~~~~~~ 171 172We'll create a set of targets that will link the CROSSTOOL into Bazel's 173toolchain system. It's likely this API will change in the future. This will be 174in ``tools/BUILD.bazel``. 175 176First, we'll create some ``filegroups`` that we can reference from other rules. 177 178.. code:: bzl 179 180 package(default_visibility = ["//visibility:public"]) 181 182 filegroup( 183 name = "empty", 184 srcs = [], 185 ) 186 187 filegroup( 188 name = "all", 189 srcs = glob([ 190 "bin/*", 191 "lib/**", 192 "libexec/**", 193 "share/**", 194 ]), 195 ) 196 197Next, we'll create a ``cc_toolchain`` target that tells Bazel where to find some 198important files. This API is undocumented and will very likely change in the 199future. We need to create one of these for each ``toolchain`` in ``CROSSTOOL``. 200The ``toolchain_identifier`` and ``cpu`` fields should match, and the 201filegroups should cover the files referenced in ``CROSSTOOL``. 202 203.. code:: bzl 204 205 cc_toolchain( 206 name = "cc-compiler-clang", 207 all_files = ":all", 208 compiler_files = ":all", 209 cpu = "x86_64", 210 dwp_files = ":empty", 211 dynamic_runtime_libs = [":empty"], 212 linker_files = ":all", 213 objcopy_files = ":empty", 214 static_runtime_libs = [":empty"], 215 strip_files = ":empty", 216 supports_param_files = 1, 217 toolchain_identifier = "clang", 218 ) 219 220Finally, we'll create a ``cc_toolchain_suite`` target. This should reference 221``cc_toolchain`` targets for all the toolchains in ``CROSSTOOL``. This API is 222also undocumented and will probably change. 223 224.. code:: bzl 225 226 cc_toolchain_suite( 227 name = "clang-toolchain", 228 toolchains = { 229 "x86_64": ":cc-compiler-clang", 230 "x86_64|clang": ":cc-compiler-clang", 231 }, 232 ) 233 234Step 5: Verify your toolchain works 235~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 236 237At this point, you should be able to build a simple binary by passing a bunch 238of extra flags to Bazel. 239 240.. code:: bash 241 242 $ mkdir example 243 $ cat >example/hello.c <<EOF 244 #include <stdio.h> 245 246 int main() { 247 printf("Hello, world!\n"); 248 return 0; 249 } 250 EOF 251 252 $ cat >example/BUILD.bazel <<EOF 253 cc_binary( 254 name = "hello", 255 srcs = ["hello.c"], 256 ) 257 EOF 258 259 $ bazel build \ 260 --crosstool_top=//tools:clang-toolchain \ 261 --cpu=x86_64 \ 262 --compiler=clang \ 263 --host_cpu=x86_64 \ 264 -s \ 265 //example:hello 266 267You should see an invocation of ``tools/bin/clang`` in the output. 268 269* ``--crosstool_top`` should be the label for the ``cc_toolchain_suite`` target 270 defined earlier. 271* ``--cpu=x86_64`` should be the ``cpu`` attribute in ``cc_toolchain`` and in 272 the ``toolchain`` message in ``CROSSTOOL``. 273* ``--compiler=clang`` should be the ``toolchain_identifier`` attribute in 274 ``cc_toolchain`` and in the ``toolchain`` message in ``CROSSTOOL``. 275* ``--host_cpu`` should be the same as ``--cpu``. If we were cross-compiling, 276 it would be the ``cpu`` value for the execution platform (where actions are 277 performed), not the host platform (where Bazel is invoked). 278* ``-s`` prints commands. 279 280Step 6: Configure a Go workspace to use the toolchain 281~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 282 283In the ``WORSKPACE`` file for your Go project, import the 284``bazel_cc_toolchains`` repository. The way you do this may vary depending on 285where you've put ``bazel_cc_toolchains``. 286 287.. code:: bzl 288 289 load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") 290 291 git_repository( 292 name = "bazel_cc_toolchains", 293 remote = "https://github.com/jayconrod/bazel_cc_toolchains", 294 tag = "v1.0.0", 295 ) 296 297Create a file named ``.bazelrc`` in the root directory of your Go project 298(or add the code below to the end if already exists). Each line comprises a 299Bazel command (such as ``build``), an optional configuration name (``clang``) 300and a list of flags to be passed to Bazel when that configuration is used. 301If the configuration is omitted, the flags will be passed by default. 302 303.. code:: bash 304 305 $ cat >>.bazelrc <<EOF 306 build:clang --crosstool_top=@bazel_cc_toolchains//tools:clang-toolchain 307 build:clang --cpu=x86_64 308 build:clang --compiler=clang 309 build:clang --host_cpu=x86_64 310 EOF 311 312You can build with ``bazel build --config=clang ...``. 313 314Verify the toolchain is being used by compiling a "Hello world" cgo program. 315 316.. code:: bash 317 318 $ cat >hello.go <<EOF 319 package main 320 321 /* 322 #include <stdio.h> 323 324 void say_hello() { 325 printf("Hello, world!\n"); 326 } 327 */ 328 import "C" 329 330 func main() { 331 C.say_hello() 332 } 333 EOF 334 335 $ cat >BUILD.bazel <<EOF 336 load("@io_bazel_rules_go//go:def.bzl", "go_binary") 337 338 go_binary( 339 name = "hello", 340 srcs = ["hello.go"], 341 cgo = True, 342 ) 343 344 $ bazel build --config=clang -s //:hello 345 346You should see clang commands in Bazel's output. 347