Miguel Young de la Sota | 4bfbba3 | 2022-03-24 16:12:14 -0400 | [diff] [blame] | 1 | # Copyright lowRISC contributors. |
| 2 | # Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| 3 | # SPDX-License-Identifier: Apache-2.0 |
| 4 | |
| 5 | """Aspects and rules for making cc_* libraries emit more outputs.""" |
| 6 | |
| 7 | load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES", "C_COMPILE_ACTION_NAME") |
Miguel Young de la Sota | 4b01c7a | 2022-04-21 17:25:52 -0400 | [diff] [blame] | 8 | load("@rules_cc//cc:find_cc_toolchain.bzl", "find_cc_toolchain") |
Miguel Young de la Sota | 4bfbba3 | 2022-03-24 16:12:14 -0400 | [diff] [blame] | 9 | load("//rules:rv.bzl", "rv_rule") |
| 10 | |
| 11 | CcSideProductInfo = provider(fields = ["files"]) |
| 12 | |
Miguel Young de la Sota | 7bb7fe7 | 2022-04-01 16:40:02 -0400 | [diff] [blame] | 13 | def _is_c_or_cc(file): |
| 14 | return file.extension in [ |
| 15 | "c", |
| 16 | # We only use .cc, but to avoid nasty surprises we support all five |
| 17 | # C++ file extensions. |
| 18 | "cc", |
| 19 | "cpp", |
| 20 | "cxx", |
| 21 | "c++", |
| 22 | "C", |
| 23 | ] |
| 24 | |
| 25 | def _cc_compile_different_output(name, target, ctx, extension, flags, process_all_files = False): |
Miguel Young de la Sota | 4bfbba3 | 2022-03-24 16:12:14 -0400 | [diff] [blame] | 26 | """ |
| 27 | Helper macro for implementing the .s and .ll outputting libraries. |
| 28 | |
| 29 | In addition to the usual target and ctx inputs for an aspect, it also |
| 30 | takes an extension to add to each source file of the rule being analyzed, |
| 31 | and flags to add to the compiler arguments to get the output we want. |
| 32 | """ |
| 33 | if ctx.rule.kind not in ["cc_library", "cc_binary", "cc_test"]: |
| 34 | return [CcSideProductInfo(files = depset([]))] |
| 35 | |
| 36 | # This filters out both headers and assembly source inputs, neither of which |
| 37 | # make sense to generate an .s output for. |
| 38 | translation_units = [ |
| 39 | src.files.to_list()[0] |
| 40 | for src in ctx.rule.attr.srcs |
Miguel Young de la Sota | 7bb7fe7 | 2022-04-01 16:40:02 -0400 | [diff] [blame] | 41 | if process_all_files or _is_c_or_cc(src.files.to_list()[0]) |
Miguel Young de la Sota | 4bfbba3 | 2022-03-24 16:12:14 -0400 | [diff] [blame] | 42 | ] |
| 43 | |
| 44 | transitive = [] |
| 45 | for dep in ctx.rule.attr.deps: |
| 46 | if CcSideProductInfo in dep: |
| 47 | transitive.append(dep[CcSideProductInfo].files) |
| 48 | |
| 49 | # Libraries consisting of just headers or assembly files have nothing |
| 50 | # useful to contribute to the output. |
| 51 | if len(translation_units) == 0: |
| 52 | return [CcSideProductInfo(files = depset( |
| 53 | transitive = transitive, |
| 54 | ))] |
| 55 | |
| 56 | if CcInfo in target: |
| 57 | cc_info = target[CcInfo] |
| 58 | else: |
| 59 | # Some rules, like cc_binary, do NOT produce a CcInfo provider. Therefore, |
| 60 | # we need to build one from its dependencies. |
| 61 | cc_info = cc_common.merge_cc_infos( |
| 62 | direct_cc_infos = [dep[CcInfo] for dep in ctx.rule.attr.deps if CcInfo in dep], |
| 63 | ) |
| 64 | cc_compile_ctx = cc_info.compilation_context |
| 65 | |
Miguel Young de la Sota | 4b01c7a | 2022-04-21 17:25:52 -0400 | [diff] [blame] | 66 | cc_toolchain = find_cc_toolchain(ctx).cc |
Miguel Young de la Sota | 4bfbba3 | 2022-03-24 16:12:14 -0400 | [diff] [blame] | 67 | feature_configuration = cc_common.configure_features( |
| 68 | ctx = ctx, |
| 69 | cc_toolchain = cc_toolchain, |
| 70 | requested_features = ctx.features, |
| 71 | unsupported_features = ctx.disabled_features, |
| 72 | ) |
| 73 | |
| 74 | c_compiler_path = cc_common.get_tool_for_action( |
| 75 | feature_configuration = feature_configuration, |
| 76 | action_name = ACTION_NAMES.c_compile, |
| 77 | ) |
| 78 | |
| 79 | outputs = [] |
| 80 | for source_file in translation_units: |
| 81 | output_file = ctx.actions.declare_file( |
| 82 | # Some source files in the repo are currently pulled in by multiple |
| 83 | # rules, and this is allowed in general, although not a good idea. |
Miguel Young de la Sota | 9fd4883 | 2022-03-25 17:35:06 -0400 | [diff] [blame] | 84 | # |
Miguel Young de la Sota | 4bfbba3 | 2022-03-24 16:12:14 -0400 | [diff] [blame] | 85 | # Adding the rule name in front of the file name helps mitigate this |
| 86 | # issue. |
| 87 | "{}.{}.{}".format(target.label.name, source_file.basename, extension), |
| 88 | ) |
| 89 | outputs.append(output_file) |
| 90 | |
| 91 | # C files are treated specially, and have different flags applied |
| 92 | # (e.g. --std=c11). |
Miguel Young de la Sota | 98cfa22 | 2022-04-06 15:31:06 -0400 | [diff] [blame] | 93 | # |
Miguel Young de la Sota | 7bb7fe7 | 2022-04-01 16:40:02 -0400 | [diff] [blame] | 94 | # Things that are neither C or C++ TU files don't get any flags. |
Miguel Young de la Sota | 4bfbba3 | 2022-03-24 16:12:14 -0400 | [diff] [blame] | 95 | opts = ctx.fragments.cpp.copts + ctx.rule.attr.copts |
| 96 | if source_file.extension == "c": |
Miguel Young de la Sota | 9fd4883 | 2022-03-25 17:35:06 -0400 | [diff] [blame] | 97 | opts += ctx.fragments.cpp.conlyopts |
Miguel Young de la Sota | 7bb7fe7 | 2022-04-01 16:40:02 -0400 | [diff] [blame] | 98 | elif _is_c_or_cc(source_file): |
Miguel Young de la Sota | 4bfbba3 | 2022-03-24 16:12:14 -0400 | [diff] [blame] | 99 | opts += ctx.fragments.cpp.cxxopts + ctx.rule.attr.cxxopts |
| 100 | |
| 101 | c_compile_variables = cc_common.create_compile_variables( |
| 102 | feature_configuration = feature_configuration, |
| 103 | cc_toolchain = cc_toolchain, |
| 104 | source_file = source_file.path, |
Miguel Young de la Sota | 4bfbba3 | 2022-03-24 16:12:14 -0400 | [diff] [blame] | 105 | user_compile_flags = opts + flags, |
| 106 | include_directories = depset( |
| 107 | direct = [src.dirname for src in cc_compile_ctx.headers.to_list()], |
| 108 | transitive = [cc_compile_ctx.includes], |
| 109 | ), |
| 110 | quote_include_directories = cc_compile_ctx.quote_includes, |
| 111 | system_include_directories = cc_compile_ctx.system_includes, |
| 112 | framework_include_directories = cc_compile_ctx.framework_includes, |
| 113 | preprocessor_defines = depset( |
| 114 | direct = ctx.rule.attr.local_defines, |
| 115 | transitive = [cc_compile_ctx.defines], |
| 116 | ), |
| 117 | ) |
| 118 | |
| 119 | command_line = cc_common.get_memory_inefficient_command_line( |
| 120 | feature_configuration = feature_configuration, |
| 121 | action_name = ACTION_NAMES.c_compile, |
| 122 | variables = c_compile_variables, |
| 123 | ) |
| 124 | env = cc_common.get_environment_variables( |
| 125 | feature_configuration = feature_configuration, |
| 126 | action_name = ACTION_NAMES.c_compile, |
| 127 | variables = c_compile_variables, |
| 128 | ) |
| 129 | |
Miguel Young de la Sota | 7bb7fe7 | 2022-04-01 16:40:02 -0400 | [diff] [blame] | 130 | if hasattr(ctx.file, "_clang_format") and source_file.extension != "S": |
| 131 | oldenv = env |
| 132 | env = {"CLANG_FORMAT": ctx.file._clang_format.path} |
| 133 | for k, v in oldenv.items(): |
| 134 | env[k] = v |
| 135 | |
Miguel Young de la Sota | 9fd4883 | 2022-03-25 17:35:06 -0400 | [diff] [blame] | 136 | ctx.actions.run_shell( |
Miguel Young de la Sota | 7bb7fe7 | 2022-04-01 16:40:02 -0400 | [diff] [blame] | 137 | mnemonic = name, |
Miguel Young de la Sota | 9fd4883 | 2022-03-25 17:35:06 -0400 | [diff] [blame] | 138 | tools = [ |
| 139 | ctx.file._cleanup_script, |
| 140 | ], |
| 141 | arguments = [ |
| 142 | c_compiler_path, |
| 143 | ctx.file._cleanup_script.path, |
| 144 | output_file.path, |
| 145 | ] + command_line, |
Miguel Young de la Sota | 4bfbba3 | 2022-03-24 16:12:14 -0400 | [diff] [blame] | 146 | env = env, |
| 147 | inputs = depset( |
| 148 | [source_file], |
| 149 | transitive = [ |
| 150 | cc_toolchain.all_files, |
| 151 | cc_compile_ctx.headers, |
| 152 | ], |
| 153 | ), |
| 154 | outputs = [output_file], |
Miguel Young de la Sota | 9fd4883 | 2022-03-25 17:35:06 -0400 | [diff] [blame] | 155 | command = """ |
| 156 | CC=$1; shift |
Miguel Young de la Sota | 7bb7fe7 | 2022-04-01 16:40:02 -0400 | [diff] [blame] | 157 | CLEANUP=$1; shift |
Miguel Young de la Sota | 9fd4883 | 2022-03-25 17:35:06 -0400 | [diff] [blame] | 158 | OUT=$1; shift |
Miguel Young de la Sota | 7bb7fe7 | 2022-04-01 16:40:02 -0400 | [diff] [blame] | 159 | $CC -o - $@ 2> /dev/null | $CLEANUP > $OUT |
Miguel Young de la Sota | 9fd4883 | 2022-03-25 17:35:06 -0400 | [diff] [blame] | 160 | """, |
Miguel Young de la Sota | 4bfbba3 | 2022-03-24 16:12:14 -0400 | [diff] [blame] | 161 | ) |
| 162 | |
| 163 | return [CcSideProductInfo(files = depset( |
| 164 | direct = outputs, |
| 165 | transitive = transitive, |
| 166 | ))] |
| 167 | |
Miguel Young de la Sota | 7bb7fe7 | 2022-04-01 16:40:02 -0400 | [diff] [blame] | 168 | def _cc_preprocess_aspect_impl(target, ctx): |
| 169 | return _cc_compile_different_output("Preprocess", target, ctx, "i", ["-E"], process_all_files = True) |
| 170 | |
| 171 | cc_preprocess_aspect = aspect( |
| 172 | implementation = _cc_preprocess_aspect_impl, |
| 173 | doc = """ |
| 174 | An aspect that provides a CcSideProductInfo containing the preprocessed outputs |
| 175 | of every C/C++ translation unit in the sources of the rule it is applied to and |
| 176 | all of its dependencies. |
| 177 | """, |
| 178 | attrs = { |
| 179 | "_cleanup_script": attr.label( |
| 180 | allow_single_file = True, |
| 181 | default = Label("//rules/scripts:clean_up_cpp_output.sh"), |
| 182 | ), |
| 183 | "_clang_format": attr.label( |
Timothy Trippel | 41b46fe | 2022-08-17 23:01:25 -0700 | [diff] [blame] | 184 | default = "@lowrisc_rv32imcb_files//:bin/clang-format", |
Miguel Young de la Sota | 7bb7fe7 | 2022-04-01 16:40:02 -0400 | [diff] [blame] | 185 | allow_single_file = True, |
| 186 | cfg = "host", |
| 187 | executable = True, |
| 188 | ), |
| 189 | "_cc_toolchain": attr.label( |
| 190 | default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"), |
| 191 | ), |
| 192 | }, |
| 193 | attr_aspects = ["deps"], |
| 194 | provides = [CcSideProductInfo], |
| 195 | toolchains = ["@rules_cc//cc:toolchain_type"], |
| 196 | incompatible_use_toolchain_transition = True, |
| 197 | fragments = ["cpp"], |
| 198 | host_fragments = ["cpp"], |
| 199 | ) |
| 200 | |
Miguel Young de la Sota | 4bfbba3 | 2022-03-24 16:12:14 -0400 | [diff] [blame] | 201 | def _cc_assembly_aspect_impl(target, ctx): |
Miguel Young de la Sota | 7bb7fe7 | 2022-04-01 16:40:02 -0400 | [diff] [blame] | 202 | return _cc_compile_different_output("AsmOutput", target, ctx, "s", ["-S"]) |
Miguel Young de la Sota | 4bfbba3 | 2022-03-24 16:12:14 -0400 | [diff] [blame] | 203 | |
| 204 | cc_asm_aspect = aspect( |
| 205 | implementation = _cc_assembly_aspect_impl, |
| 206 | doc = """ |
| 207 | An aspect that provides a CcSideProductInfo containing the assembly file outputs |
| 208 | of every C/C++ translation unit in the sources of the rule it is applied to and |
| 209 | all of its dependencies. |
| 210 | """, |
| 211 | attrs = { |
Miguel Young de la Sota | 9fd4883 | 2022-03-25 17:35:06 -0400 | [diff] [blame] | 212 | "_cleanup_script": attr.label( |
| 213 | allow_single_file = True, |
| 214 | default = Label("//rules/scripts:expand_tabs.sh"), |
| 215 | ), |
Miguel Young de la Sota | 4bfbba3 | 2022-03-24 16:12:14 -0400 | [diff] [blame] | 216 | "_cc_toolchain": attr.label( |
| 217 | default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"), |
| 218 | ), |
| 219 | }, |
| 220 | attr_aspects = ["deps"], |
| 221 | provides = [CcSideProductInfo], |
| 222 | toolchains = ["@rules_cc//cc:toolchain_type"], |
| 223 | incompatible_use_toolchain_transition = True, |
| 224 | fragments = ["cpp"], |
| 225 | host_fragments = ["cpp"], |
| 226 | ) |
| 227 | |
| 228 | def _cc_llvm_aspect_impl(target, ctx): |
Miguel Young de la Sota | 4b01c7a | 2022-04-21 17:25:52 -0400 | [diff] [blame] | 229 | cc_toolchain = find_cc_toolchain(ctx).cc |
Miguel Young de la Sota | 4bfbba3 | 2022-03-24 16:12:14 -0400 | [diff] [blame] | 230 | if cc_toolchain.compiler.find("clang") == -1: |
| 231 | return CcSideProductInfo(files = depset()) |
Miguel Young de la Sota | 7bb7fe7 | 2022-04-01 16:40:02 -0400 | [diff] [blame] | 232 | return _cc_compile_different_output("LLVMOutput", target, ctx, "ll", ["-S", "-emit-llvm"]) |
Miguel Young de la Sota | 4bfbba3 | 2022-03-24 16:12:14 -0400 | [diff] [blame] | 233 | |
| 234 | cc_llvm_aspect = aspect( |
| 235 | implementation = _cc_llvm_aspect_impl, |
| 236 | doc = """ |
| 237 | An aspect that provides a CcSideProductInfo containing the LLVM IR file outputs |
| 238 | of every C/C++ translation unit in the sources of the rule it is applied to and |
| 239 | all of its dependencies. |
| 240 | |
| 241 | If the current compiler does not appear to be clang, it outputs nothing instead. |
| 242 | """, |
| 243 | attrs = { |
Miguel Young de la Sota | 9fd4883 | 2022-03-25 17:35:06 -0400 | [diff] [blame] | 244 | "_cleanup_script": attr.label( |
| 245 | allow_single_file = True, |
| 246 | default = Label("//rules/scripts:expand_tabs.sh"), |
| 247 | ), |
Miguel Young de la Sota | 4bfbba3 | 2022-03-24 16:12:14 -0400 | [diff] [blame] | 248 | "_cc_toolchain": attr.label( |
| 249 | default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"), |
| 250 | ), |
| 251 | }, |
| 252 | attr_aspects = ["deps"], |
| 253 | provides = [CcSideProductInfo], |
| 254 | toolchains = ["@rules_cc//cc:toolchain_type"], |
| 255 | incompatible_use_toolchain_transition = True, |
| 256 | fragments = ["cpp"], |
| 257 | host_fragments = ["cpp"], |
| 258 | ) |
| 259 | |
| 260 | MapFile = provider(fields = ["map_file"]) |
| 261 | |
| 262 | def _cc_relink_with_linkmap_aspect_impl(target, ctx): |
| 263 | # As mentioned above, there is no CcInfo in a cc_binary, so we're forced |
| 264 | # to rumage around the target's actions for its link action, and then |
| 265 | # re-run it, but capturing the map file as a side-product. |
| 266 | link_action = None |
| 267 | for action in target.actions: |
| 268 | if action.mnemonic == "CppLink": |
| 269 | link_action = action |
| 270 | break |
| 271 | if not link_action: |
| 272 | return [MapFile(map_file = None)] |
| 273 | |
| 274 | output_file = ctx.actions.declare_file(target.label.name + ".ldmap") |
| 275 | |
Miguel Young de la Sota | 4b01c7a | 2022-04-21 17:25:52 -0400 | [diff] [blame] | 276 | cc_toolchain = find_cc_toolchain(ctx).cc |
Miguel Young de la Sota | 4bfbba3 | 2022-03-24 16:12:14 -0400 | [diff] [blame] | 277 | feature_configuration = cc_common.configure_features( |
| 278 | ctx = ctx, |
| 279 | cc_toolchain = cc_toolchain, |
| 280 | requested_features = ctx.features, |
| 281 | unsupported_features = ctx.disabled_features, |
| 282 | ) |
| 283 | linker_path = cc_common.get_tool_for_action( |
| 284 | feature_configuration = feature_configuration, |
| 285 | action_name = ACTION_NAMES.cpp_link_executable, |
| 286 | ) |
| 287 | |
| 288 | ctx.actions.run( |
Miguel Young de la Sota | 7bb7fe7 | 2022-04-01 16:40:02 -0400 | [diff] [blame] | 289 | mnemonic = "LinkMapFile", |
Miguel Young de la Sota | 4bfbba3 | 2022-03-24 16:12:14 -0400 | [diff] [blame] | 290 | executable = linker_path, |
| 291 | arguments = link_action.argv[1:] + ["-Wl,-Map=" + output_file.path], |
| 292 | env = link_action.env, |
| 293 | inputs = link_action.inputs, |
| 294 | outputs = [output_file], |
| 295 | ) |
| 296 | |
| 297 | return [MapFile(map_file = output_file)] |
| 298 | |
| 299 | cc_relink_with_linkmap_aspect = aspect( |
| 300 | implementation = _cc_relink_with_linkmap_aspect_impl, |
| 301 | doc = """ |
| 302 | An aspect to apply to a cc_binary rule that rebuilds its linker invocation and |
| 303 | re-runs it, capturing the map file as an output. |
| 304 | |
| 305 | This needs to exist because cc_binary currently does not allow us to specify that |
| 306 | it may emit multiple outputs. |
| 307 | """, |
| 308 | attrs = { |
| 309 | "_cc_toolchain": attr.label( |
| 310 | default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"), |
| 311 | ), |
| 312 | }, |
| 313 | provides = [MapFile], |
| 314 | toolchains = ["@rules_cc//cc:toolchain_type"], |
| 315 | incompatible_use_toolchain_transition = True, |
| 316 | fragments = ["cpp"], |
| 317 | host_fragments = ["cpp"], |
| 318 | ) |
| 319 | |
Miguel Young de la Sota | 7bb7fe7 | 2022-04-01 16:40:02 -0400 | [diff] [blame] | 320 | def _rv_preprocess_impl(ctx): |
| 321 | return [DefaultInfo( |
| 322 | files = ctx.attr.target[CcSideProductInfo].files, |
| 323 | data_runfiles = ctx.runfiles(transitive_files = ctx.attr.target[CcSideProductInfo].files), |
| 324 | )] |
| 325 | |
| 326 | rv_preprocess = rv_rule( |
| 327 | implementation = _rv_preprocess_impl, |
| 328 | attrs = {"target": attr.label(aspects = [cc_preprocess_aspect])}, |
| 329 | ) |
| 330 | |
Miguel Young de la Sota | 4bfbba3 | 2022-03-24 16:12:14 -0400 | [diff] [blame] | 331 | def _rv_asm_impl(ctx): |
| 332 | return [DefaultInfo( |
| 333 | files = ctx.attr.target[CcSideProductInfo].files, |
| 334 | data_runfiles = ctx.runfiles(transitive_files = ctx.attr.target[CcSideProductInfo].files), |
| 335 | )] |
| 336 | |
| 337 | rv_asm = rv_rule( |
| 338 | implementation = _rv_asm_impl, |
| 339 | attrs = {"target": attr.label(aspects = [cc_asm_aspect])}, |
| 340 | ) |
| 341 | |
| 342 | def _llvm_ir_impl(ctx): |
| 343 | return [DefaultInfo( |
| 344 | files = ctx.attr.target[CcSideProductInfo].files, |
| 345 | data_runfiles = ctx.runfiles(transitive_files = ctx.attr.target[CcSideProductInfo].files), |
| 346 | )] |
| 347 | |
| 348 | rv_llvm_ir = rv_rule( |
| 349 | implementation = _rv_asm_impl, |
| 350 | attrs = {"target": attr.label(aspects = [cc_llvm_aspect])}, |
| 351 | ) |
| 352 | |
| 353 | def _cc_relink_with_linkmap_impl(ctx): |
| 354 | return [DefaultInfo( |
| 355 | files = depset([ctx.attr.target[MapFile].map_file]), |
| 356 | data_runfiles = ctx.runfiles(files = [ctx.attr.target[MapFile].map_file]), |
| 357 | )] |
| 358 | |
| 359 | rv_relink_with_linkmap = rv_rule( |
| 360 | implementation = _cc_relink_with_linkmap_impl, |
| 361 | attrs = {"target": attr.label(aspects = [cc_relink_with_linkmap_aspect])}, |
| 362 | ) |