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.
View as plain text