...

Text file src/github.com/bazelbuild/rules_go/go/crosstool.rst

Documentation: github.com/bazelbuild/rules_go/go

     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