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