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