mirror of https://github.com/bazelbuild/rules_cc
Add custom C++ toolchain example.
Inspired by https://github.com/bazelbuild/bazel/issues/12942.
This commit is contained in:
parent
40548a2974
commit
541927eaa0
|
@ -0,0 +1,116 @@
|
|||
# Copyright 2021 The Bazel Authors. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Proof-of-concept example showing how to write a custom C++ toolchain.
|
||||
#
|
||||
# Important documentation:
|
||||
#
|
||||
# - https://docs.bazel.build/versions/master/platforms-intro.html#c
|
||||
# - https://docs.bazel.build/versions/master/tutorial/cc-toolchain-config.html
|
||||
# - https://docs.bazel.build/versions/master/be/c-cpp.html#cc_toolchain
|
||||
#
|
||||
# There are two ways to select C++ toolchains:
|
||||
#
|
||||
# - NEW (USE IF POSSIBLE): with the --platforms flag
|
||||
# - LEGACY: with the --crosstool_top and --cpu flags
|
||||
#
|
||||
# See https://docs.bazel.build/versions/master/platforms-intro.html#c for details.
|
||||
#
|
||||
# This example demonstrates both approaches.
|
||||
|
||||
# Load the Starlark logic defining the toolchain's behavior. For example: what
|
||||
# program runs to compile a source file and how its command line is
|
||||
# constructed. See toolchain_config.bzl for details.
|
||||
load(":toolchain_config.bzl", "cc_toolchain_config")
|
||||
|
||||
# The library we want to build. Building this calls two C++ actions: compile (.cc ->
|
||||
# .o) and archive (.o -> .a).
|
||||
cc_library(
|
||||
name = "buildme",
|
||||
srcs = ["buildme.cc"],
|
||||
)
|
||||
|
||||
# This example intentionally makes the cc_toolchain_config definition
|
||||
# simple. You could alternative add attributes to support multiple
|
||||
# cc_toolchain_config targets with finer customization.
|
||||
cc_toolchain_config(
|
||||
name = "toolchain_semantics",
|
||||
)
|
||||
|
||||
# Register the toolchain with Bazel. Most of these attribute just tell Bazel
|
||||
# where to find the files needed to run C++ commands. The toolchain_config
|
||||
# attribute registers the behavior specification declared above.
|
||||
cc_toolchain(
|
||||
name = "my_custom_toolchain",
|
||||
all_files = ":toolchain_files",
|
||||
ar_files = ":toolchain_files",
|
||||
compiler_files = ":toolchain_files",
|
||||
dwp_files = ":toolchain_files",
|
||||
linker_files = ":toolchain_files",
|
||||
objcopy_files = ":toolchain_files",
|
||||
strip_files = ":toolchain_files",
|
||||
toolchain_config = ":toolchain_semantics",
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "toolchain_files",
|
||||
srcs = [
|
||||
"sample_compiler",
|
||||
"sample_linker",
|
||||
],
|
||||
)
|
||||
|
||||
# Implements legacy toolchain selection.
|
||||
#
|
||||
# Setting --crosstool_top here registers the set of available
|
||||
# toolchains. Setting --cpu to one of the toolchain attribute's keys selects a
|
||||
#toolchain.
|
||||
cc_toolchain_suite(
|
||||
name = "legacy_selector",
|
||||
toolchains = {
|
||||
"x86": ":my_custom_toolchain",
|
||||
},
|
||||
)
|
||||
|
||||
# Implements platform-based (recommended) toolchain selection.
|
||||
#
|
||||
# See https://docs.bazel.build/versions/master/platforms-intro.html. The main
|
||||
# differences are:
|
||||
#
|
||||
# 1. --cpu / --crosstool_top are replaced by a platform() definition with
|
||||
# much more customizable properties. For example, a platform can specify
|
||||
# OS, device type (server, phone, tablet) or custom hardware extensions.
|
||||
# 2. All languages can support platform-based toolchains. A single --platforms
|
||||
# value can choose C++, Python, Scala, and all other toolchains in your
|
||||
# build. This is especially useful for multi-language builds.
|
||||
# 3. Platforms support features like incompatible target skipping:
|
||||
# https://docs.bazel.build/versions/master/platforms.html#skipping-incompatible-targets.
|
||||
toolchain(
|
||||
name = "platform_based_toolchain",
|
||||
# Trigger this toolchain for x86-compatible platforms.
|
||||
# See https://github.com/bazelbuild/platforms.
|
||||
target_compatible_with = ["@platforms//cpu:x86_64"],
|
||||
# Register this toolchain with platforms.
|
||||
toolchain = ":my_custom_toolchain",
|
||||
# The public interface for all C++ toolchains. Starlark rules that use C++
|
||||
# access the toolchain through this interface.
|
||||
toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
|
||||
)
|
||||
|
||||
# Define a platform matching any x86-compatible toolchain. See
|
||||
# https://docs.bazel.build/versions/master/platforms.html.
|
||||
platform(
|
||||
name = "x86_platform",
|
||||
constraint_values = ["@platforms//cpu:x86_64"],
|
||||
)
|
|
@ -0,0 +1,78 @@
|
|||
# Writing a custom C++ toolchain
|
||||
|
||||
This example shows how to define and use a simple custom C++ toolchain.
|
||||
|
||||
Output is non-functional: simple scripts replace compilation and linking
|
||||
with "I compiled!" and "I linked!" messages.
|
||||
|
||||
[BUILD](BUILD) provides detailed implementation walkthrough. The fundamental
|
||||
sequence is:
|
||||
|
||||
1. Define the toolchain
|
||||
1. Define how to invoke the toolchain.
|
||||
|
||||
`1` is C++-specific: the logic and structure depends specifically on C++'s
|
||||
language model. Other languages have their own models.
|
||||
|
||||
`2` supports two variations. `--crosstool_top` / `--cput`, the legacy variation,
|
||||
is C++-specific. `--platforms`, the modern variation, is much more generic and
|
||||
supports all languages and features like [incompatible target
|
||||
skipping](https://docs.bazel.build/versions/master/platforms.html#skipping-incompatible-targets). See
|
||||
[Building with
|
||||
Platforms](https://docs.bazel.build/versions/master/platforms-intro.html) and
|
||||
its [C++
|
||||
annotations](https://docs.bazel.build/versions/master/platforms-intro.html#c) for
|
||||
full review.
|
||||
|
||||
## Building with the default toolchain
|
||||
|
||||
```
|
||||
$ bazel clean
|
||||
$ bazel build //examples/custom_toolchain:buildme
|
||||
$ file bazel-bin/examples/custom_toolchain/libbuildme.a
|
||||
bazel-bin/examples/custom_toolchain/libbuildme.a: current ar archive
|
||||
```
|
||||
|
||||
## Building with platforms
|
||||
|
||||
This mode requires `--incompatible_enable_cc_toolchain_resolution`. Without this
|
||||
flag, `--platforms` and `--extra_toolchains` are ignored and the default
|
||||
toolchain triggers.
|
||||
|
||||
```
|
||||
$ bazel clean
|
||||
$ bazel build //examples/custom_toolchain:buildme --platforms=//examples/custom_toolchain:x86_platform --extra_toolchains=//examples/custom_toolchain:platform_based_toolchain --incompatible_enable_cc_toolchain_resolution
|
||||
DEBUG: /usr/local/google/home/gregce/bazel/rules_cc/examples/custom_toolchain/toolchain_config.bzl:17:10: Invoking my custom toolchain!
|
||||
INFO: From Compiling examples/custom_toolchain/buildme.cc:
|
||||
examples/custom_toolchain/sample_compiler: running sample cc_library compiler (produces .o output).
|
||||
INFO: From Linking examples/custom_toolchain/libbuildme.a:
|
||||
examples/custom_toolchain/sample_linker: running sample cc_library linker (produces .a output).
|
||||
|
||||
$ cat bazel-bin/examples/custom_toolchain/libbuildme.a
|
||||
examples/custom_toolchain/sample_linker: sample output
|
||||
```
|
||||
|
||||
This example uses a long command line for demonstration purposes. A real project
|
||||
would [register toolchains](https://docs.bazel.build/versions/master/toolchains.html#registering-and-building-with-toolchains)
|
||||
in `WORKSPACE` and auto-set
|
||||
`--incompatible_enable_cc_toolchain_resolution`. That reduces the command to:
|
||||
|
||||
```
|
||||
$ bazel build //examples/custom_toolchain:buildme --platforms=//examples/custom_toolchain:x86_platform
|
||||
```
|
||||
|
||||
## Building with legacy toolchain selection:
|
||||
|
||||
```
|
||||
$ bazel clean
|
||||
$ bazel build //examples/custom_toolchain:buildme --crosstool_top=//examples/custom_toolchain:legacy_selector --cpu=x86
|
||||
DEBUG: /usr/local/google/home/gregce/bazel/rules_cc/examples/custom_toolchain/toolchain_config.bzl:17:10: Invoking my custom toolchain!
|
||||
INFO: From Compiling examples/custom_toolchain/buildme.cc:
|
||||
examples/custom_toolchain/sample_compiler: running sample cc_library compiler (produces .o output).
|
||||
INFO: From Linking examples/custom_toolchain/libbuildme.a:
|
||||
examples/custom_toolchain/sample_linker: running sample cc_library linker (produces .a output).
|
||||
|
||||
$ cat bazel-bin/examples/custom_toolchain/libbuildme.a
|
||||
examples/custom_toolchain/sample_linker: sample output
|
||||
```
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
int some_function() {
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Sample script demonstrating custom C++ toolchain selection: handles
|
||||
# the command that translates a cc_library's .cc (source file) into .o (object
|
||||
# file).
|
||||
|
||||
echo "$0: running sample cc_library compiler (produces .o output)."
|
||||
|
||||
# https://docs.bazel.build/versions/master/cc-toolchain-config-reference.html
|
||||
# defines fancier ways to generate custom command lines. This script just shows
|
||||
# the default, which looks like:
|
||||
#
|
||||
# examples/custom_toolchain/sample_compiler <various compiler flags> -o bazel-out/x86-fastbuild/bin/examples/custom_toolchain/_objs/buildme/buildme.o.
|
||||
|
||||
# The .o is the last parameter.
|
||||
OBJECT_FILE=${@: -1}
|
||||
# Swap out .o for .d to get expected .d (source dependency output).
|
||||
DOTD_FILE=${OBJECT_FILE%?}d
|
||||
|
||||
echo "$0: sample .o output" > $OBJECT_FILE
|
||||
echo "sample .d output ($0)" > $DOTD_FILE
|
|
@ -0,0 +1,23 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Sample script demonstrating custom C++ toolchain selection: handles
|
||||
# the command that translates a cc_library's .o (object file) into
|
||||
# .a (archive).
|
||||
|
||||
echo "$0: running sample cc_library linker (produces .a output)."
|
||||
|
||||
# https://docs.bazel.build/versions/master/cc-toolchain-config-reference.html
|
||||
# defines fancier ways to generate custom command lines. This script just shows
|
||||
# the default, which looks like:
|
||||
#
|
||||
# examples/custom_toolchain/sample_linker @bazel-out/x86-fastbuild/bin/examples/custom_toolchain/libbuildme.a-2.params.
|
||||
|
||||
# Get "@bazel-out/.../libbuildme.a-2.params".
|
||||
PARAMS_FILE=${@: -1}
|
||||
# Remove the "@" prefix.
|
||||
OUTFILE=${PARAMS_FILE#?}
|
||||
# Replace "libbuildme.a-2.params" with "libbuildme.a".
|
||||
OUTFILE=${OUTFILE%-*}
|
||||
|
||||
echo "$0: sample output" > $OUTFILE
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
# Sample Starlark definition defining a C++ toolchain's behavior.
|
||||
#
|
||||
# When you build a cc_* rule, this logic defines what programs run for what
|
||||
# build steps (e.g. compile / link / archive) and how their command lines are
|
||||
# structured.
|
||||
#
|
||||
# This is a proof-of-concept simple implementation. It doesn't construct fancy
|
||||
# command lines and uses mock shell scripts to compile and link
|
||||
# ("sample_compiler" and "sample_linker"). See
|
||||
# https://docs.bazel.build/versions/master/cc-toolchain-config-reference.html and
|
||||
# https://docs.bazel.build/versions/master/tutorial/cc-toolchain-config.html for
|
||||
# advanced usage.
|
||||
|
||||
load("@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl", "tool_path")
|
||||
|
||||
def _impl(ctx):
|
||||
print("Invoking my custom toolchain!")
|
||||
|
||||
tool_paths = [
|
||||
tool_path(
|
||||
name = "ar",
|
||||
path = "sample_linker",
|
||||
),
|
||||
tool_path(
|
||||
name = "cpp",
|
||||
path = "not_used_in_this_example",
|
||||
),
|
||||
tool_path(
|
||||
name = "gcc",
|
||||
path = "sample_compiler",
|
||||
),
|
||||
tool_path(
|
||||
name = "gcov",
|
||||
path = "not_used_in_this_example",
|
||||
),
|
||||
tool_path(
|
||||
name = "ld",
|
||||
path = "sample_linker",
|
||||
),
|
||||
tool_path(
|
||||
name = "nm",
|
||||
path = "not_used_in_this_example",
|
||||
),
|
||||
tool_path(
|
||||
name = "objdump",
|
||||
path = "not_used_in_this_example",
|
||||
),
|
||||
tool_path(
|
||||
name = "strip",
|
||||
path = "not_used_in_this_example",
|
||||
),
|
||||
]
|
||||
|
||||
# Documented at
|
||||
# https://docs.bazel.build/versions/master/skylark/lib/cc_common.html#create_cc_toolchain_config_info.
|
||||
#
|
||||
# create_cc_toolchain_config_info is the public interface for registering
|
||||
# C++ toolchain behavior.
|
||||
return cc_common.create_cc_toolchain_config_info(
|
||||
ctx = ctx,
|
||||
toolchain_identifier = "custom-toolchain-identifier",
|
||||
host_system_name = "local",
|
||||
target_system_name = "local",
|
||||
target_cpu = "sample_cpu",
|
||||
target_libc = "unknown",
|
||||
compiler = "gcc",
|
||||
abi_version = "unknown",
|
||||
abi_libc_version = "unknown",
|
||||
tool_paths = tool_paths,
|
||||
)
|
||||
|
||||
cc_toolchain_config = rule(
|
||||
implementation = _impl,
|
||||
# You can alternatively define attributes here that make it possible to
|
||||
# instantiate different cc_toolchain_config targets with different behavior.
|
||||
attrs = {},
|
||||
provides = [CcToolchainConfigInfo],
|
||||
)
|
Loading…
Reference in New Issue