• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2
3# Copyright 2022 The Pigweed Authors
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may not
6# use this file except in compliance with the License. You may obtain a copy of
7# the License at
8#
9#     https://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations under
15# the License.
16""" An internal set of tools for creating embedded CC targets. """
17
18load("@rules_cc//cc:action_names.bzl", "C_COMPILE_ACTION_NAME")
19load("@rules_cc//cc:toolchain_utils.bzl", "find_cpp_toolchain")
20
21DEBUGGING = [
22    "-g",
23]
24
25# Standard compiler flags to reduce output binary size.
26REDUCED_SIZE_COPTS = [
27    "-fno-common",
28    "-fno-exceptions",
29    "-ffunction-sections",
30    "-fdata-sections",
31]
32
33STRICT_WARNINGS_COPTS = [
34    "-Wall",
35    "-Wextra",
36    # Make all warnings errors, except for the exemptions below.
37    "-Werror",
38    "-Wno-error=cpp",  # preprocessor #warning statement
39    "-Wno-error=deprecated-declarations",  # [[deprecated]] attribute
40]
41
42CPP17_COPTS = [
43    "-std=c++17",
44    "-fno-rtti",
45    "-Wnon-virtual-dtor",
46    # Allow uses of the register keyword, which may appear in C headers.
47    "-Wno-register",
48]
49
50DISABLE_PENDING_WORKAROUND_COPTS = [
51    "-Wno-private-header",
52]
53
54PW_DEFAULT_COPTS = (
55    DEBUGGING +
56    REDUCED_SIZE_COPTS +
57    STRICT_WARNINGS_COPTS +
58    DISABLE_PENDING_WORKAROUND_COPTS
59)
60
61KYTHE_COPTS = [
62    "-Wno-unknown-warning-option",
63]
64
65PW_DEFAULT_LINKOPTS = []
66
67def add_defaults(kwargs):
68    """Adds default arguments suitable for both C and C++ code to kwargs.
69
70    Args:
71        kwargs: cc_* arguments to be modified.
72    """
73
74    copts = PW_DEFAULT_COPTS + kwargs.get("copts", [])
75    kwargs["copts"] = select({
76        "//pw_build:kythe": copts + KYTHE_COPTS,
77        "//conditions:default": copts,
78    })
79    kwargs["linkopts"] = kwargs.get("linkopts", []) + PW_DEFAULT_LINKOPTS
80
81    # Set linkstatic to avoid building .so files.
82    kwargs["linkstatic"] = True
83
84    kwargs.setdefault("features", [])
85
86    # Crosstool--adding this line to features disables header modules, which
87    # don't work with -fno-rtti. Note: this is not a command-line argument,
88    # it's "minus use_header_modules".
89    kwargs["features"].append("-use_header_modules")
90
91def default_cc_and_c_kwargs(kwargs):
92    """Splits kwargs into C and C++ arguments adding defaults.
93
94    Args:
95        kwargs: cc_* arguments to be modified.
96
97    Returns:
98        A tuple of (cc_cxx_kwargs cc_c_kwargs)
99    """
100    add_defaults(kwargs)
101    kwargs.setdefault("srcs", [])
102
103    cc = dict(kwargs.items())
104    cc["srcs"] = [src for src in kwargs["srcs"] if not src.endswith(".c")]
105    cc["copts"] = cc["copts"] + CPP17_COPTS
106
107    c_srcs = [src for src in kwargs["srcs"] if src.endswith(".c")]
108
109    if c_srcs:
110        c = dict(kwargs.items())
111        c["name"] += "_c"
112        c["srcs"] = c_srcs + [src for src in kwargs["srcs"] if src.endswith(".h")]
113
114        cc["deps"] = cc.get("deps", []) + [":" + c["name"]]
115        return cc, c
116
117    return cc, None
118
119def add_cc_and_c_targets(target, kwargs):  # buildifier: disable=unnamed-macro
120    """Splits target into C and C++ targets adding defaults.
121
122    Args:
123        target: cc_* target to be split.
124        kwargs: cc_* arguments to be modified.
125    """
126    cc_kwargs, c_kwargs = default_cc_and_c_kwargs(kwargs)
127
128    if c_kwargs:
129        native.cc_library(**c_kwargs)
130
131    target(**cc_kwargs)
132
133def has_pw_assert_dep(deps):
134    """Checks if the given deps contain a pw_assert dependency
135
136    Args:
137        deps: List of dependencies
138
139    Returns:
140        True if the list contains a pw_assert dependency.
141    """
142    pw_assert_targets = ["//pw_assert", "//pw_assert:pw_assert"]
143    pw_assert_targets.append(["@pigweed" + t for t in pw_assert_targets])
144    for dep in deps:
145        if dep in pw_assert_targets:
146            return True
147    return False
148
149def _preprocess_linker_script_impl(ctx):
150    cc_toolchain = find_cpp_toolchain(ctx)
151    output_script = ctx.actions.declare_file(ctx.label.name + ".ld")
152    feature_configuration = cc_common.configure_features(
153        ctx = ctx,
154        cc_toolchain = cc_toolchain,
155        requested_features = ctx.features,
156        unsupported_features = ctx.disabled_features,
157    )
158    cxx_compiler_path = cc_common.get_tool_for_action(
159        feature_configuration = feature_configuration,
160        action_name = C_COMPILE_ACTION_NAME,
161    )
162    c_compile_variables = cc_common.create_compile_variables(
163        feature_configuration = feature_configuration,
164        cc_toolchain = cc_toolchain,
165        user_compile_flags = ctx.fragments.cpp.copts + ctx.fragments.cpp.conlyopts,
166    )
167    env = cc_common.get_environment_variables(
168        feature_configuration = feature_configuration,
169        action_name = C_COMPILE_ACTION_NAME,
170        variables = c_compile_variables,
171    )
172    ctx.actions.run(
173        outputs = [output_script],
174        inputs = depset(
175            [ctx.file.linker_script],
176            transitive = [cc_toolchain.all_files],
177        ),
178        executable = cxx_compiler_path,
179        arguments = [
180            "-E",
181            "-P",
182            "-xc",
183            ctx.file.linker_script.short_path,
184            "-o",
185            output_script.path,
186        ] + [
187            "-D" + d
188            for d in ctx.attr.defines
189        ] + ctx.attr.copts,
190        env = env,
191    )
192    return [DefaultInfo(files = depset([output_script]))]
193
194pw_linker_script = rule(
195    _preprocess_linker_script_impl,
196    attrs = {
197        "copts": attr.string_list(doc = "C compile options."),
198        "defines": attr.string_list(doc = "C preprocessor defines."),
199        "linker_script": attr.label(
200            mandatory = True,
201            allow_single_file = True,
202            doc = "Linker script to preprocess.",
203        ),
204        "_cc_toolchain": attr.label(default = Label("@bazel_tools//tools/cpp:current_cc_toolchain")),
205    },
206    toolchains = ["@bazel_tools//tools/cpp:toolchain_type"],
207    fragments = ["cpp"],
208)
209