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