1""" 2This file specifies a clang toolchain that can run on a Linux host which doesn't depend on any 3installed packages from the host machine. 4 5See download_linux_amd64_toolchain.bzl for more details on the creation of the toolchain. 6 7It uses the usr subfolder of the built toolchain as a sysroot 8 9It follows the example of: 10 - https://docs.bazel.build/versions/4.2.1/tutorial/cc-toolchain-config.html 11 - https://github.com/emscripten-core/emsdk/blob/7f39d100d8cd207094decea907121df72065517e/bazel/emscripten_toolchain/crosstool.bzl 12""" 13 14# https://github.com/bazelbuild/bazel/blob/master/tools/build_defs/cc/action_names.bzl 15load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES") 16 17# https://github.com/bazelbuild/bazel/blob/master/tools/cpp/cc_toolchain_config_lib.bzl 18load( 19 "@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl", 20 "action_config", 21 "feature", 22 "flag_group", 23 "flag_set", 24 "tool", 25 "variable_with_value", 26) 27load(":clang_layering_check.bzl", "make_layering_check_features") 28 29# The location of the created clang toolchain. 30EXTERNAL_TOOLCHAIN = "external/clang_linux_amd64" 31 32def _linux_amd64_toolchain_info(ctx): 33 action_configs = _make_action_configs() 34 features = [] 35 features += _make_default_flags() 36 features += make_layering_check_features() 37 features += _make_diagnostic_flags() 38 features += _make_iwyu_flags() 39 40 # https://bazel.build/rules/lib/cc_common#create_cc_toolchain_config_info 41 # Note, this rule is defined in Java code, not Starlark 42 # https://cs.opensource.google/bazel/bazel/+/master:src/main/java/com/google/devtools/build/lib/starlarkbuildapi/cpp/CcModuleApi.java 43 return cc_common.create_cc_toolchain_config_info( 44 ctx = ctx, 45 features = features, 46 action_configs = action_configs, 47 # This is important because the linker will complain if the libc shared libraries are not 48 # under this directory. Because we extract the libc libraries to 49 # EXTERNAL_TOOLCHAIN/lib, and the various headers and shared libraries to 50 # EXTERNAL_TOOLCHAIN/usr, we make the top level folder the sysroot so the linker can 51 # find the referenced libraries (e.g. EXTERNAL_TOOLCHAIN/usr/lib/x86_64-linux-gnu/libc.so 52 # is just a text file that refers to "/lib/x86_64-linux-gnu/libc.so.6" and 53 # "/lib64/ld-linux-x86-64.so.2" which will use the sysroot as the root). 54 builtin_sysroot = EXTERNAL_TOOLCHAIN, 55 # These are required, but do nothing 56 compiler = "", 57 target_cpu = "", 58 target_libc = "", 59 target_system_name = "", 60 toolchain_identifier = "", 61 ) 62 63provide_linux_amd64_toolchain_config = rule( 64 attrs = {}, 65 provides = [CcToolchainConfigInfo], 66 implementation = _linux_amd64_toolchain_info, 67) 68 69def _make_action_configs(): 70 """ 71 This function sets up the tools needed to perform the various compile/link actions. 72 73 Bazel normally restricts us to referring to (and therefore running) executables/scripts 74 that are in this directory (That is EXEC_ROOT/toolchain). However, the executables we want 75 to run are brought in via WORKSPACE.bazel and are located in EXEC_ROOT/external/clang.... 76 Therefore, we make use of "trampoline scripts" that will call the binaries from the 77 toolchain directory. 78 79 These action_configs also let us dynamically specify arguments from the Bazel 80 environment if necessary (see cpp_link_static_library_action). 81 """ 82 83 # https://cs.opensource.google/bazel/bazel/+/master:tools/cpp/cc_toolchain_config_lib.bzl;l=435;drc=3b9e6f201a9a3465720aad8712ab7bcdeaf2e5da 84 clang_tool = tool(path = "linux_trampolines/clang_trampoline_linux.sh") 85 ar_tool = tool(path = "linux_trampolines/ar_trampoline_linux.sh") 86 87 # https://cs.opensource.google/bazel/bazel/+/master:tools/cpp/cc_toolchain_config_lib.bzl;l=488;drc=3b9e6f201a9a3465720aad8712ab7bcdeaf2e5da 88 assemble_action = action_config( 89 action_name = ACTION_NAMES.assemble, 90 tools = [clang_tool], 91 ) 92 c_compile_action = action_config( 93 action_name = ACTION_NAMES.c_compile, 94 tools = [clang_tool], 95 ) 96 cpp_compile_action = action_config( 97 action_name = ACTION_NAMES.cpp_compile, 98 tools = [clang_tool], 99 ) 100 linkstamp_compile_action = action_config( 101 action_name = ACTION_NAMES.linkstamp_compile, 102 tools = [clang_tool], 103 ) 104 preprocess_assemble_action = action_config( 105 action_name = ACTION_NAMES.preprocess_assemble, 106 tools = [clang_tool], 107 ) 108 109 cpp_link_dynamic_library_action = action_config( 110 action_name = ACTION_NAMES.cpp_link_dynamic_library, 111 tools = [clang_tool], 112 ) 113 cpp_link_executable_action = action_config( 114 action_name = ACTION_NAMES.cpp_link_executable, 115 # Bazel assumes it is talking to clang when building an executable. There are 116 # "-Wl" flags on the command: https://releases.llvm.org/6.0.1/tools/clang/docs/ClangCommandLineReference.html#cmdoption-clang-Wl 117 tools = [clang_tool], 118 ) 119 cpp_link_nodeps_dynamic_library_action = action_config( 120 action_name = ACTION_NAMES.cpp_link_nodeps_dynamic_library, 121 tools = [clang_tool], 122 ) 123 124 # This is the same rule as 125 # https://github.com/emscripten-core/emsdk/blob/7f39d100d8cd207094decea907121df72065517e/bazel/emscripten_toolchain/crosstool.bzl#L143 126 # By default, there are no flags or libraries passed to the llvm-ar tool, so 127 # we need to specify them. The variables mentioned by expand_if_available are defined 128 # https://bazel.build/docs/cc-toolchain-config-reference#cctoolchainconfiginfo-build-variables 129 cpp_link_static_library_action = action_config( 130 action_name = ACTION_NAMES.cpp_link_static_library, 131 flag_sets = [ 132 flag_set( 133 flag_groups = [ 134 flag_group( 135 # https://llvm.org/docs/CommandGuide/llvm-ar.html 136 # replace existing files or insert them if they already exist, 137 # create the file if it doesn't already exist 138 # symbol table should be added 139 # Deterministic timestamps should be used 140 flags = ["rcsD", "%{output_execpath}"], 141 # Despite the name, output_execpath just refers to linker output, 142 # e.g. libFoo.a 143 expand_if_available = "output_execpath", 144 ), 145 ], 146 ), 147 flag_set( 148 flag_groups = [ 149 flag_group( 150 iterate_over = "libraries_to_link", 151 flag_groups = [ 152 flag_group( 153 flags = ["%{libraries_to_link.name}"], 154 expand_if_equal = variable_with_value( 155 name = "libraries_to_link.type", 156 value = "object_file", 157 ), 158 ), 159 flag_group( 160 flags = ["%{libraries_to_link.object_files}"], 161 iterate_over = "libraries_to_link.object_files", 162 expand_if_equal = variable_with_value( 163 name = "libraries_to_link.type", 164 value = "object_file_group", 165 ), 166 ), 167 ], 168 expand_if_available = "libraries_to_link", 169 ), 170 ], 171 ), 172 flag_set( 173 flag_groups = [ 174 flag_group( 175 flags = ["@%{linker_param_file}"], 176 expand_if_available = "linker_param_file", 177 ), 178 ], 179 ), 180 ], 181 tools = [ar_tool], 182 ) 183 184 action_configs = [ 185 assemble_action, 186 c_compile_action, 187 cpp_compile_action, 188 cpp_link_dynamic_library_action, 189 cpp_link_executable_action, 190 cpp_link_nodeps_dynamic_library_action, 191 cpp_link_static_library_action, 192 linkstamp_compile_action, 193 preprocess_assemble_action, 194 ] 195 return action_configs 196 197def _make_default_flags(): 198 """Here we define the flags for certain actions that are always applied. 199 200 For any flag that might be conditionally applied, it should be defined in //bazel/copts.bzl. 201 202 Flags that are set here will be unconditionally applied to everything we compile with 203 this toolchain, even third_party deps. 204 """ 205 206 # Note: These values must be kept in sync with those defined in cmake_exporter.go. 207 cxx_compile_includes = flag_set( 208 actions = [ 209 ACTION_NAMES.c_compile, 210 ACTION_NAMES.cpp_compile, 211 ], 212 flag_groups = [ 213 flag_group( 214 flags = [ 215 # THIS ORDER MATTERS GREATLY. If these are in the wrong order, the 216 # #include_next directives will fail to find the files, causing a compilation 217 # error (or, without -no-canonical-prefixes, a mysterious case where files 218 # are included with an absolute path and fail the build). 219 "-isystem", 220 EXTERNAL_TOOLCHAIN + "/include/c++/v1", 221 # https://github.com/llvm/llvm-project/issues/57104 222 "-isystem", 223 EXTERNAL_TOOLCHAIN + "/include/x86_64-unknown-linux-gnu/c++/v1/", 224 "-isystem", 225 EXTERNAL_TOOLCHAIN + "/usr/include", 226 "-isystem", 227 EXTERNAL_TOOLCHAIN + "/lib/clang/15.0.1/include", 228 "-isystem", 229 EXTERNAL_TOOLCHAIN + "/usr/include/x86_64-linux-gnu", 230 # We do not want clang to search in absolute paths for files. This makes 231 # Bazel think we are using an outside resource and fail the compile. 232 "-no-canonical-prefixes", 233 ], 234 ), 235 ], 236 ) 237 238 cpp_compile_flags = flag_set( 239 actions = [ 240 ACTION_NAMES.cpp_compile, 241 ], 242 flag_groups = [ 243 flag_group( 244 flags = [ 245 "-std=c++17", 246 "-stdlib=libc++", 247 ], 248 ), 249 ], 250 ) 251 252 link_exe_flags = flag_set( 253 actions = [ 254 ACTION_NAMES.cpp_link_executable, 255 ACTION_NAMES.cpp_link_dynamic_library, 256 ACTION_NAMES.cpp_link_nodeps_dynamic_library, 257 ], 258 flag_groups = [ 259 flag_group( 260 flags = [ 261 "-fuse-ld=lld", 262 # We chose to use the llvm runtime, not the gcc one because it is already 263 # included in the clang binary 264 "--rtlib=compiler-rt", 265 "-std=c++17", 266 "-stdlib=libc++", 267 # We statically include these libc++ libraries so they do not need to be 268 # on a developer's machine (they can be tricky to get). 269 EXTERNAL_TOOLCHAIN + "/lib/x86_64-unknown-linux-gnu/libc++.a", 270 EXTERNAL_TOOLCHAIN + "/lib/x86_64-unknown-linux-gnu/libc++abi.a", 271 EXTERNAL_TOOLCHAIN + "/lib/x86_64-unknown-linux-gnu/libunwind.a", 272 # Dynamically Link in the other parts of glibc (not needed in glibc 2.34+) 273 "-lpthread", 274 "-lm", 275 "-ldl", 276 ], 277 ), 278 ], 279 ) 280 return [feature( 281 "default_flags", 282 enabled = True, 283 flag_sets = [ 284 cxx_compile_includes, 285 cpp_compile_flags, 286 link_exe_flags, 287 ], 288 )] 289 290def _make_diagnostic_flags(): 291 """Here we define the flags that can be turned on via features to yield debug info.""" 292 cxx_diagnostic = flag_set( 293 actions = [ 294 ACTION_NAMES.c_compile, 295 ACTION_NAMES.cpp_compile, 296 ], 297 flag_groups = [ 298 flag_group( 299 flags = [ 300 "--trace-includes", 301 "-v", 302 ], 303 ), 304 ], 305 ) 306 307 link_diagnostic = flag_set( 308 actions = [ACTION_NAMES.cpp_link_executable], 309 flag_groups = [ 310 flag_group( 311 flags = [ 312 "-Wl,--verbose", 313 "-v", 314 ], 315 ), 316 ], 317 ) 318 319 link_search_dirs = flag_set( 320 actions = [ACTION_NAMES.cpp_link_executable], 321 flag_groups = [ 322 flag_group( 323 flags = [ 324 "--print-search-dirs", 325 ], 326 ), 327 ], 328 ) 329 return [ 330 # Running a Bazel command with --features diagnostic will cause the compilation and 331 # link steps to be more verbose. 332 feature( 333 "diagnostic", 334 enabled = False, 335 flag_sets = [ 336 cxx_diagnostic, 337 link_diagnostic, 338 ], 339 ), 340 feature( 341 "diagnostic_link", 342 enabled = False, 343 flag_sets = [ 344 link_diagnostic, 345 ], 346 ), 347 # Running a Bazel command with --features print_search_dirs will cause the link to fail 348 # but directories searched for libraries, etc will be displayed. 349 feature( 350 "print_search_dirs", 351 enabled = False, 352 flag_sets = [ 353 link_search_dirs, 354 ], 355 ), 356 ] 357 358def _make_iwyu_flags(): 359 """Here we define the flags that signal whether or not to enforce IWYU.""" 360 361 # https://bazel.build/docs/cc-toolchain-config-reference#features 362 opt_file_into_iwyu = flag_set( 363 actions = [ 364 ACTION_NAMES.c_compile, 365 ACTION_NAMES.cpp_compile, 366 ], 367 flag_groups = [ 368 flag_group( 369 flags = [ 370 # This define does not impact compilation, but it acts as a signal to the 371 # clang_trampoline.sh whether to maybe check the file with include-what-you-use 372 # A define was chosen because it is ignored by clang and IWYU, but can be 373 # easily found with bash. 374 # The clang_trampoline.sh file has a list of allowed subdirectories for which 375 # IWYU should be enforced, allowing us to slowly opt more and more directories 376 # in over time. 377 "-DSKIA_ENFORCE_IWYU", 378 ], 379 ), 380 ], 381 ) 382 383 return [ 384 feature( 385 "skia_enforce_iwyu", 386 enabled = False, 387 flag_sets = [ 388 opt_file_into_iwyu, 389 ], 390 ), 391 ] 392