xref: /aosp_15_r20/external/bazelbuild-rules_go/go/crosstool.rst (revision 9bb1b549b6a84214c53be0924760be030e66b93a)
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