1# pylint: disable=g-bad-file-header 2# Copyright 2016 The Bazel Authors. All rights reserved. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15"""Configuring the C++ toolchain on Windows.""" 16 17load( 18 ":lib_cc_configure.bzl", 19 "auto_configure_fail", 20 "auto_configure_warning", 21 "auto_configure_warning_maybe", 22 "escape_string", 23 "execute", 24 "resolve_labels", 25 "write_builtin_include_directory_paths", 26) 27 28def _get_path_env_var(repository_ctx, name): 29 """Returns a path from an environment variable. 30 31 Removes quotes, replaces '/' with '\', and strips trailing '\'s.""" 32 if name in repository_ctx.os.environ: 33 value = repository_ctx.os.environ[name] 34 if value[0] == "\"": 35 if len(value) == 1 or value[-1] != "\"": 36 auto_configure_fail("'%s' environment variable has no trailing quote" % name) 37 value = value[1:-1] 38 if "/" in value: 39 value = value.replace("/", "\\") 40 if value[-1] == "\\": 41 value = value.rstrip("\\") 42 return value 43 else: 44 return None 45 46def _get_temp_env(repository_ctx): 47 """Returns the value of TMP, or TEMP, or if both undefined then C:\\Windows.""" 48 tmp = _get_path_env_var(repository_ctx, "TMP") 49 if not tmp: 50 tmp = _get_path_env_var(repository_ctx, "TEMP") 51 if not tmp: 52 tmp = "C:\\Windows\\Temp" 53 auto_configure_warning( 54 "neither 'TMP' nor 'TEMP' environment variables are set, using '%s' as default" % tmp, 55 ) 56 return tmp 57 58def _get_escaped_windows_msys_starlark_content(repository_ctx, use_mingw = False): 59 """Return the content of msys cc toolchain rule.""" 60 msys_root = "" 61 bazel_sh = _get_path_env_var(repository_ctx, "BAZEL_SH") 62 if bazel_sh: 63 bazel_sh = bazel_sh.replace("\\", "/").lower() 64 tokens = bazel_sh.rsplit("/", 1) 65 if tokens[0].endswith("/usr/bin"): 66 msys_root = tokens[0][:len(tokens[0]) - len("usr/bin")] 67 elif tokens[0].endswith("/bin"): 68 msys_root = tokens[0][:len(tokens[0]) - len("bin")] 69 70 prefix = "mingw64" if use_mingw else "usr" 71 tool_path_prefix = escape_string(msys_root) + prefix 72 tool_bin_path = tool_path_prefix + "/bin" 73 tool_path = {} 74 75 for tool in ["ar", "compat-ld", "cpp", "dwp", "gcc", "gcov", "ld", "nm", "objcopy", "objdump", "strip"]: 76 if msys_root: 77 tool_path[tool] = tool_bin_path + "/" + tool 78 else: 79 tool_path[tool] = "msys_gcc_installation_error.bat" 80 tool_paths = ",\n ".join(['"%s": "%s"' % (k, v) for k, v in tool_path.items()]) 81 include_directories = (' "%s/",\n ' % tool_path_prefix) if msys_root else "" 82 return tool_paths, tool_bin_path, include_directories 83 84def _get_system_root(repository_ctx): 85 """Get System root path on Windows, default is C:\\Windows. Doesn't %-escape the result.""" 86 systemroot = _get_path_env_var(repository_ctx, "SYSTEMROOT") 87 if not systemroot: 88 systemroot = "C:\\Windows" 89 auto_configure_warning_maybe( 90 repository_ctx, 91 "SYSTEMROOT is not set, using default SYSTEMROOT=C:\\Windows", 92 ) 93 return escape_string(systemroot) 94 95def _add_system_root(repository_ctx, env): 96 """Running VCVARSALL.BAT and VCVARSQUERYREGISTRY.BAT need %SYSTEMROOT%\\\\system32 in PATH.""" 97 if "PATH" not in env: 98 env["PATH"] = "" 99 env["PATH"] = env["PATH"] + ";" + _get_system_root(repository_ctx) + "\\system32" 100 return env 101 102def _find_vc_path(repository_ctx): 103 """Find Visual C++ build tools install path. Doesn't %-escape the result.""" 104 105 # 1. Check if BAZEL_VC or BAZEL_VS is already set by user. 106 bazel_vc = _get_path_env_var(repository_ctx, "BAZEL_VC") 107 if bazel_vc: 108 if repository_ctx.path(bazel_vc).exists: 109 return bazel_vc 110 else: 111 auto_configure_warning_maybe( 112 repository_ctx, 113 "%BAZEL_VC% is set to non-existent path, ignoring.", 114 ) 115 116 bazel_vs = _get_path_env_var(repository_ctx, "BAZEL_VS") 117 if bazel_vs: 118 if repository_ctx.path(bazel_vs).exists: 119 bazel_vc = bazel_vs + "\\VC" 120 if repository_ctx.path(bazel_vc).exists: 121 return bazel_vc 122 else: 123 auto_configure_warning_maybe( 124 repository_ctx, 125 "No 'VC' directory found under %BAZEL_VS%, ignoring.", 126 ) 127 else: 128 auto_configure_warning_maybe( 129 repository_ctx, 130 "%BAZEL_VS% is set to non-existent path, ignoring.", 131 ) 132 133 auto_configure_warning_maybe( 134 repository_ctx, 135 "Neither %BAZEL_VC% nor %BAZEL_VS% are set, start looking for the latest Visual C++" + 136 " installed.", 137 ) 138 139 # 2. Check if VS%VS_VERSION%COMNTOOLS is set, if true then try to find and use 140 # vcvarsqueryregistry.bat / VsDevCmd.bat to detect VC++. 141 auto_configure_warning_maybe(repository_ctx, "Looking for VS%VERSION%COMNTOOLS environment variables, " + 142 "eg. VS140COMNTOOLS") 143 for vscommontools_env, script in [ 144 ("VS160COMNTOOLS", "VsDevCmd.bat"), 145 ("VS150COMNTOOLS", "VsDevCmd.bat"), 146 ("VS140COMNTOOLS", "vcvarsqueryregistry.bat"), 147 ("VS120COMNTOOLS", "vcvarsqueryregistry.bat"), 148 ("VS110COMNTOOLS", "vcvarsqueryregistry.bat"), 149 ("VS100COMNTOOLS", "vcvarsqueryregistry.bat"), 150 ("VS90COMNTOOLS", "vcvarsqueryregistry.bat"), 151 ]: 152 if vscommontools_env not in repository_ctx.os.environ: 153 continue 154 script = _get_path_env_var(repository_ctx, vscommontools_env) + "\\" + script 155 if not repository_ctx.path(script).exists: 156 continue 157 repository_ctx.file( 158 "get_vc_dir.bat", 159 "@echo off\n" + 160 "call \"" + script + "\"\n" + 161 "echo %VCINSTALLDIR%", 162 True, 163 ) 164 env = _add_system_root(repository_ctx, repository_ctx.os.environ) 165 vc_dir = execute(repository_ctx, ["./get_vc_dir.bat"], environment = env) 166 167 auto_configure_warning_maybe(repository_ctx, "Visual C++ build tools found at %s" % vc_dir) 168 return vc_dir 169 170 # 3. User might have purged all environment variables. If so, look for Visual C++ in registry. 171 # Works for Visual Studio 2017 and older. (Does not work for Visual Studio 2019 Preview.) 172 # TODO(laszlocsomor): check if "16.0" also has this registry key, after VS 2019 is released. 173 auto_configure_warning_maybe(repository_ctx, "Looking for Visual C++ through registry") 174 reg_binary = _get_system_root(repository_ctx) + "\\system32\\reg.exe" 175 vc_dir = None 176 for key, suffix in (("VC7", ""), ("VS7", "\\VC")): 177 for version in ["15.0", "14.0", "12.0", "11.0", "10.0", "9.0", "8.0"]: 178 if vc_dir: 179 break 180 result = repository_ctx.execute([reg_binary, "query", "HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\VisualStudio\\SxS\\" + key, "/v", version]) 181 auto_configure_warning_maybe(repository_ctx, "registry query result for VC %s:\n\nSTDOUT(start)\n%s\nSTDOUT(end)\nSTDERR(start):\n%s\nSTDERR(end)\n" % 182 (version, result.stdout, result.stderr)) 183 if not result.stderr: 184 for line in result.stdout.split("\n"): 185 line = line.strip() 186 if line.startswith(version) and line.find("REG_SZ") != -1: 187 vc_dir = line[line.find("REG_SZ") + len("REG_SZ"):].strip() + suffix 188 if vc_dir: 189 auto_configure_warning_maybe(repository_ctx, "Visual C++ build tools found at %s" % vc_dir) 190 return vc_dir 191 192 # 4. Check default directories for VC installation 193 auto_configure_warning_maybe(repository_ctx, "Looking for default Visual C++ installation directory") 194 program_files_dir = _get_path_env_var(repository_ctx, "PROGRAMFILES(X86)") 195 if not program_files_dir: 196 program_files_dir = "C:\\Program Files (x86)" 197 auto_configure_warning_maybe( 198 repository_ctx, 199 "'PROGRAMFILES(X86)' environment variable is not set, using '%s' as default" % program_files_dir, 200 ) 201 for path in [ 202 "Microsoft Visual Studio\\2019\\Preview\\VC", 203 "Microsoft Visual Studio\\2019\\BuildTools\\VC", 204 "Microsoft Visual Studio\\2019\\Community\\VC", 205 "Microsoft Visual Studio\\2019\\Professional\\VC", 206 "Microsoft Visual Studio\\2019\\Enterprise\\VC", 207 "Microsoft Visual Studio\\2017\\BuildTools\\VC", 208 "Microsoft Visual Studio\\2017\\Community\\VC", 209 "Microsoft Visual Studio\\2017\\Professional\\VC", 210 "Microsoft Visual Studio\\2017\\Enterprise\\VC", 211 "Microsoft Visual Studio 14.0\\VC", 212 ]: 213 path = program_files_dir + "\\" + path 214 if repository_ctx.path(path).exists: 215 vc_dir = path 216 break 217 218 if not vc_dir: 219 auto_configure_warning_maybe(repository_ctx, "Visual C++ build tools not found.") 220 return None 221 auto_configure_warning_maybe(repository_ctx, "Visual C++ build tools found at %s" % vc_dir) 222 return vc_dir 223 224def _is_vs_2017_or_2019(vc_path): 225 """Check if the installed VS version is Visual Studio 2017.""" 226 227 # In VS 2017 and 2019, the location of VC is like: 228 # C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\ 229 # In VS 2015 or older version, it is like: 230 # C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\ 231 return vc_path.find("2017") != -1 or vc_path.find("2019") != -1 232 233def _find_vcvars_bat_script(repository_ctx, vc_path): 234 """Find batch script to set up environment variables for VC. Doesn't %-escape the result.""" 235 if _is_vs_2017_or_2019(vc_path): 236 vcvars_script = vc_path + "\\Auxiliary\\Build\\VCVARSALL.BAT" 237 else: 238 vcvars_script = vc_path + "\\VCVARSALL.BAT" 239 240 if not repository_ctx.path(vcvars_script).exists: 241 return None 242 243 return vcvars_script 244 245def _is_support_vcvars_ver(vc_full_version): 246 """-vcvars_ver option is supported from version 14.11.25503 (VS 2017 version 15.3).""" 247 version = [int(i) for i in vc_full_version.split(".")] 248 min_version = [14, 11, 25503] 249 return version >= min_version 250 251def _is_support_winsdk_selection(repository_ctx, vc_path): 252 """Windows SDK selection is supported with VC 2017 / 2019 or with full VS 2015 installation.""" 253 if _is_vs_2017_or_2019(vc_path): 254 return True 255 256 # By checking the source code of VCVARSALL.BAT in VC 2015, we know that 257 # when devenv.exe or wdexpress.exe exists, VCVARSALL.BAT supports Windows SDK selection. 258 vc_common_ide = repository_ctx.path(vc_path).dirname.get_child("Common7").get_child("IDE") 259 for tool in ["devenv.exe", "wdexpress.exe"]: 260 if vc_common_ide.get_child(tool).exists: 261 return True 262 return False 263 264def setup_vc_env_vars(repository_ctx, vc_path, envvars = [], allow_empty = False, escape = True): 265 """Get environment variables set by VCVARSALL.BAT script. Doesn't %-escape the result! 266 267 Args: 268 repository_ctx: the repository_ctx object 269 vc_path: Visual C++ root directory 270 envvars: list of envvars to retrieve; default is ["PATH", "INCLUDE", "LIB", "WINDOWSSDKDIR"] 271 allow_empty: allow unset envvars; if False then report errors for those 272 escape: if True, escape "\" as "\\" and "%" as "%%" in the envvar values 273 274 Returns: 275 dictionary of the envvars 276 """ 277 if not envvars: 278 envvars = ["PATH", "INCLUDE", "LIB", "WINDOWSSDKDIR"] 279 280 vcvars_script = _find_vcvars_bat_script(repository_ctx, vc_path) 281 if not vcvars_script: 282 auto_configure_fail("Cannot find VCVARSALL.BAT script under %s" % vc_path) 283 284 # Getting Windows SDK version set by user. 285 # Only supports VC 2017 & 2019 and VC 2015 with full VS installation. 286 winsdk_version = _get_winsdk_full_version(repository_ctx) 287 if winsdk_version and not _is_support_winsdk_selection(repository_ctx, vc_path): 288 auto_configure_warning(("BAZEL_WINSDK_FULL_VERSION=%s is ignored, " + 289 "because standalone Visual C++ Build Tools 2015 doesn't support specifying Windows " + 290 "SDK version, please install the full VS 2015 or use VC 2017/2019.") % winsdk_version) 291 winsdk_version = "" 292 293 # Get VC version set by user. Only supports VC 2017 & 2019. 294 vcvars_ver = "" 295 if _is_vs_2017_or_2019(vc_path): 296 full_version = _get_vc_full_version(repository_ctx, vc_path) 297 298 # Because VCVARSALL.BAT is from the latest VC installed, so we check if the latest 299 # version supports -vcvars_ver or not. 300 if _is_support_vcvars_ver(_get_latest_subversion(repository_ctx, vc_path)): 301 vcvars_ver = "-vcvars_ver=" + full_version 302 303 cmd = "\"%s\" amd64 %s %s" % (vcvars_script, winsdk_version, vcvars_ver) 304 print_envvars = ",".join(["{k}=%{k}%".format(k = k) for k in envvars]) 305 repository_ctx.file( 306 "get_env.bat", 307 "@echo off\n" + 308 ("call %s > NUL \n" % cmd) + ("echo %s \n" % print_envvars), 309 True, 310 ) 311 env = _add_system_root(repository_ctx, {k: "" for k in envvars}) 312 envs = execute(repository_ctx, ["./get_env.bat"], environment = env).split(",") 313 env_map = {} 314 for env in envs: 315 key, value = env.split("=", 1) 316 env_map[key] = escape_string(value.replace("\\", "\\\\")) if escape else value 317 if not allow_empty: 318 _check_env_vars(env_map, cmd, expected = envvars) 319 return env_map 320 321def _check_env_vars(env_map, cmd, expected): 322 for env in expected: 323 if not env_map.get(env): 324 auto_configure_fail( 325 "Setting up VC environment variables failed, %s is not set by the following command:\n %s" % (env, cmd), 326 ) 327 328def _get_latest_subversion(repository_ctx, vc_path): 329 """Get the latest subversion of a VS 2017/2019 installation. 330 331 For VS 2017 & 2019, there could be multiple versions of VC build tools. 332 The directories are like: 333 <vc_path>\\Tools\\MSVC\\14.10.24930\\bin\\HostX64\\x64 334 <vc_path>\\Tools\\MSVC\\14.16.27023\\bin\\HostX64\\x64 335 This function should return 14.16.27023 in this case.""" 336 versions = [path.basename for path in repository_ctx.path(vc_path + "\\Tools\\MSVC").readdir()] 337 if len(versions) < 1: 338 auto_configure_warning_maybe(repository_ctx, "Cannot find any VC installation under BAZEL_VC(%s)" % vc_path) 339 return None 340 341 # Parse the version string into integers, then sort the integers to prevent textual sorting. 342 version_list = [] 343 for version in versions: 344 parts = [int(i) for i in version.split(".")] 345 version_list.append((parts, version)) 346 347 version_list = sorted(version_list) 348 latest_version = version_list[-1][1] 349 350 auto_configure_warning_maybe(repository_ctx, "Found the following VC verisons:\n%s\n\nChoosing the latest version = %s" % ("\n".join(versions), latest_version)) 351 return latest_version 352 353def _get_vc_full_version(repository_ctx, vc_path): 354 """Return the value of BAZEL_VC_FULL_VERSION if defined, otherwise the latest version.""" 355 if "BAZEL_VC_FULL_VERSION" in repository_ctx.os.environ: 356 return repository_ctx.os.environ["BAZEL_VC_FULL_VERSION"] 357 return _get_latest_subversion(repository_ctx, vc_path) 358 359def _get_winsdk_full_version(repository_ctx): 360 """Return the value of BAZEL_WINSDK_FULL_VERSION if defined, otherwise an empty string.""" 361 return repository_ctx.os.environ.get("BAZEL_WINSDK_FULL_VERSION", default = "") 362 363def _find_msvc_tool(repository_ctx, vc_path, tool): 364 """Find the exact path of a specific build tool in MSVC. Doesn't %-escape the result.""" 365 tool_path = None 366 if _is_vs_2017_or_2019(vc_path): 367 full_version = _get_vc_full_version(repository_ctx, vc_path) 368 if full_version: 369 tool_path = "%s\\Tools\\MSVC\\%s\\bin\\HostX64\\x64\\%s" % (vc_path, full_version, tool) 370 else: 371 # For VS 2015 and older version, the tools are under: 372 # C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\amd64 373 tool_path = vc_path + "\\bin\\amd64\\" + tool 374 375 if not tool_path or not repository_ctx.path(tool_path).exists: 376 return None 377 378 return tool_path.replace("\\", "/") 379 380def _find_missing_vc_tools(repository_ctx, vc_path): 381 """Check if any required tool is missing under given VC path.""" 382 missing_tools = [] 383 if not _find_vcvars_bat_script(repository_ctx, vc_path): 384 missing_tools.append("VCVARSALL.BAT") 385 386 for tool in ["cl.exe", "link.exe", "lib.exe", "ml64.exe"]: 387 if not _find_msvc_tool(repository_ctx, vc_path, tool): 388 missing_tools.append(tool) 389 390 return missing_tools 391 392def _is_support_debug_fastlink(repository_ctx, linker): 393 """Run linker alone to see if it supports /DEBUG:FASTLINK.""" 394 if _use_clang_cl(repository_ctx): 395 # LLVM's lld-link.exe doesn't support /DEBUG:FASTLINK. 396 return False 397 result = execute(repository_ctx, [linker], expect_failure = True) 398 return result.find("/DEBUG[:{FASTLINK|FULL|NONE}]") != -1 399 400def _find_llvm_path(repository_ctx): 401 """Find LLVM install path.""" 402 403 # 1. Check if BAZEL_LLVM is already set by user. 404 bazel_llvm = _get_path_env_var(repository_ctx, "BAZEL_LLVM") 405 if bazel_llvm: 406 return bazel_llvm 407 408 auto_configure_warning_maybe(repository_ctx, "'BAZEL_LLVM' is not set, " + 409 "start looking for LLVM installation on machine.") 410 411 # 2. Look for LLVM installation through registry. 412 auto_configure_warning_maybe(repository_ctx, "Looking for LLVM installation through registry") 413 reg_binary = _get_system_root(repository_ctx) + "\\system32\\reg.exe" 414 llvm_dir = None 415 result = repository_ctx.execute([reg_binary, "query", "HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\LLVM\\LLVM"]) 416 auto_configure_warning_maybe(repository_ctx, "registry query result for LLVM:\n\nSTDOUT(start)\n%s\nSTDOUT(end)\nSTDERR(start):\n%s\nSTDERR(end)\n" % 417 (result.stdout, result.stderr)) 418 if not result.stderr: 419 for line in result.stdout.split("\n"): 420 line = line.strip() 421 if line.startswith("(Default)") and line.find("REG_SZ") != -1: 422 llvm_dir = line[line.find("REG_SZ") + len("REG_SZ"):].strip() 423 if llvm_dir: 424 auto_configure_warning_maybe(repository_ctx, "LLVM installation found at %s" % llvm_dir) 425 return llvm_dir 426 427 # 3. Check default directories for LLVM installation 428 auto_configure_warning_maybe(repository_ctx, "Looking for default LLVM installation directory") 429 program_files_dir = _get_path_env_var(repository_ctx, "PROGRAMFILES") 430 if not program_files_dir: 431 program_files_dir = "C:\\Program Files" 432 auto_configure_warning_maybe( 433 repository_ctx, 434 "'PROGRAMFILES' environment variable is not set, using '%s' as default" % program_files_dir, 435 ) 436 path = program_files_dir + "\\LLVM" 437 if repository_ctx.path(path).exists: 438 llvm_dir = path 439 440 if not llvm_dir: 441 auto_configure_warning_maybe(repository_ctx, "LLVM installation not found.") 442 return None 443 auto_configure_warning_maybe(repository_ctx, "LLVM installation found at %s" % llvm_dir) 444 return llvm_dir 445 446def _find_llvm_tool(repository_ctx, llvm_path, tool): 447 """Find the exact path of a specific build tool in LLVM. Doesn't %-escape the result.""" 448 tool_path = llvm_path + "\\bin\\" + tool 449 450 if not repository_ctx.path(tool_path).exists: 451 return None 452 453 return tool_path.replace("\\", "/") 454 455def _use_clang_cl(repository_ctx): 456 """Returns True if USE_CLANG_CL is set to 1.""" 457 return repository_ctx.os.environ.get("USE_CLANG_CL", default = "0") == "1" 458 459def _find_missing_llvm_tools(repository_ctx, llvm_path): 460 """Check if any required tool is missing under given LLVM path.""" 461 missing_tools = [] 462 for tool in ["clang-cl.exe", "lld-link.exe", "llvm-lib.exe"]: 463 if not _find_llvm_tool(repository_ctx, llvm_path, tool): 464 missing_tools.append(tool) 465 466 return missing_tools 467 468def _get_clang_version(repository_ctx, clang_cl): 469 result = repository_ctx.execute([clang_cl, "-v"]) 470 if result.return_code != 0: 471 auto_configure_fail("Failed to get clang version by running \"%s -v\"" % clang_cl) 472 473 # Stderr should look like "clang version X.X.X ..." 474 return result.stderr.splitlines()[0].split(" ")[2] 475 476def _get_msys_mingw_vars(repository_ctx): 477 """Get the variables we need to populate the msys/mingw toolchains.""" 478 tool_paths, tool_bin_path, inc_dir_msys = _get_escaped_windows_msys_starlark_content(repository_ctx) 479 tool_paths_mingw, tool_bin_path_mingw, inc_dir_mingw = _get_escaped_windows_msys_starlark_content(repository_ctx, use_mingw = True) 480 write_builtin_include_directory_paths(repository_ctx, "mingw", [inc_dir_mingw], file_suffix = "_mingw") 481 msys_mingw_vars = { 482 "%{cxx_builtin_include_directories}": inc_dir_msys, 483 "%{mingw_cxx_builtin_include_directories}": inc_dir_mingw, 484 "%{mingw_tool_bin_path}": tool_bin_path_mingw, 485 "%{mingw_tool_paths}": tool_paths_mingw, 486 "%{tool_bin_path}": tool_bin_path, 487 "%{tool_paths}": tool_paths, 488 } 489 return msys_mingw_vars 490 491def _get_msvc_vars(repository_ctx, paths): 492 """Get the variables we need to populate the MSVC toolchains.""" 493 msvc_vars = dict() 494 vc_path = _find_vc_path(repository_ctx) 495 missing_tools = None 496 if not vc_path: 497 repository_ctx.template( 498 "vc_installation_error.bat", 499 paths["@rules_cc//cc/private/toolchain:vc_installation_error.bat.tpl"], 500 {"%{vc_error_message}": ""}, 501 ) 502 else: 503 missing_tools = _find_missing_vc_tools(repository_ctx, vc_path) 504 if missing_tools: 505 message = "\r\n".join([ 506 "echo. 1>&2", 507 "echo Visual C++ build tools seems to be installed at %s 1>&2" % vc_path, 508 "echo But Bazel can't find the following tools: 1>&2", 509 "echo %s 1>&2" % ", ".join(missing_tools), 510 "echo. 1>&2", 511 ]) 512 repository_ctx.template( 513 "vc_installation_error.bat", 514 paths["@rules_cc//cc/private/toolchain:vc_installation_error.bat.tpl"], 515 {"%{vc_error_message}": message}, 516 ) 517 518 if not vc_path or missing_tools: 519 write_builtin_include_directory_paths(repository_ctx, "msvc", [], file_suffix = "_msvc") 520 msvc_vars = { 521 "%{dbg_mode_debug_flag}": "/DEBUG", 522 "%{fastbuild_mode_debug_flag}": "/DEBUG", 523 "%{msvc_cl_path}": "vc_installation_error.bat", 524 "%{msvc_cxx_builtin_include_directories}": "", 525 "%{msvc_env_include}": "msvc_not_found", 526 "%{msvc_env_lib}": "msvc_not_found", 527 "%{msvc_env_path}": "msvc_not_found", 528 "%{msvc_env_tmp}": "msvc_not_found", 529 "%{msvc_lib_path}": "vc_installation_error.bat", 530 "%{msvc_link_path}": "vc_installation_error.bat", 531 "%{msvc_ml_path}": "vc_installation_error.bat", 532 } 533 return msvc_vars 534 535 env = setup_vc_env_vars(repository_ctx, vc_path) 536 escaped_paths = escape_string(env["PATH"]) 537 escaped_include_paths = escape_string(env["INCLUDE"]) 538 escaped_lib_paths = escape_string(env["LIB"]) 539 escaped_tmp_dir = escape_string(_get_temp_env(repository_ctx).replace("\\", "\\\\")) 540 541 llvm_path = "" 542 if _use_clang_cl(repository_ctx): 543 llvm_path = _find_llvm_path(repository_ctx) 544 if not llvm_path: 545 auto_configure_fail("\nUSE_CLANG_CL is set to 1, but Bazel cannot find Clang installation on your system.\n" + 546 "Please install Clang via http://releases.llvm.org/download.html\n") 547 cl_path = _find_llvm_tool(repository_ctx, llvm_path, "clang-cl.exe") 548 link_path = _find_llvm_tool(repository_ctx, llvm_path, "lld-link.exe") 549 if not link_path: 550 link_path = _find_msvc_tool(repository_ctx, vc_path, "link.exe") 551 lib_path = _find_llvm_tool(repository_ctx, llvm_path, "llvm-lib.exe") 552 if not lib_path: 553 lib_path = _find_msvc_tool(repository_ctx, vc_path, "lib.exe") 554 else: 555 cl_path = _find_msvc_tool(repository_ctx, vc_path, "cl.exe") 556 link_path = _find_msvc_tool(repository_ctx, vc_path, "link.exe") 557 lib_path = _find_msvc_tool(repository_ctx, vc_path, "lib.exe") 558 559 msvc_ml_path = _find_msvc_tool(repository_ctx, vc_path, "ml64.exe") 560 escaped_cxx_include_directories = [] 561 562 for path in escaped_include_paths.split(";"): 563 if path: 564 escaped_cxx_include_directories.append("\"%s\"" % path) 565 if llvm_path: 566 clang_version = _get_clang_version(repository_ctx, cl_path) 567 clang_dir = llvm_path + "\\lib\\clang\\" + clang_version 568 clang_include_path = (clang_dir + "\\include").replace("\\", "\\\\") 569 escaped_cxx_include_directories.append("\"%s\"" % clang_include_path) 570 clang_lib_path = (clang_dir + "\\lib\\windows").replace("\\", "\\\\") 571 escaped_lib_paths = escaped_lib_paths + ";" + clang_lib_path 572 573 support_debug_fastlink = _is_support_debug_fastlink(repository_ctx, link_path) 574 575 write_builtin_include_directory_paths(repository_ctx, "msvc", escaped_cxx_include_directories, file_suffix = "_msvc") 576 msvc_vars = { 577 "%{dbg_mode_debug_flag}": "/DEBUG:FULL" if support_debug_fastlink else "/DEBUG", 578 "%{fastbuild_mode_debug_flag}": "/DEBUG:FASTLINK" if support_debug_fastlink else "/DEBUG", 579 "%{msvc_cl_path}": cl_path, 580 "%{msvc_cxx_builtin_include_directories}": " " + ",\n ".join(escaped_cxx_include_directories), 581 "%{msvc_env_include}": escaped_include_paths, 582 "%{msvc_env_lib}": escaped_lib_paths, 583 "%{msvc_env_path}": escaped_paths, 584 "%{msvc_env_tmp}": escaped_tmp_dir, 585 "%{msvc_lib_path}": lib_path, 586 "%{msvc_link_path}": link_path, 587 "%{msvc_ml_path}": msvc_ml_path, 588 } 589 return msvc_vars 590 591def _get_clang_cl_vars(repository_ctx, paths, msvc_vars): 592 """Get the variables we need to populate the clang-cl toolchains.""" 593 llvm_path = _find_llvm_path(repository_ctx) 594 error_script = None 595 if msvc_vars["%{msvc_cl_path}"] == "vc_installation_error.bat": 596 error_script = "vc_installation_error.bat" 597 elif not llvm_path: 598 repository_ctx.template( 599 "clang_installation_error.bat", 600 paths["@rules_cc//cc/private/toolchain:clang_installation_error.bat.tpl"], 601 {"%{clang_error_message}": ""}, 602 ) 603 error_script = "clang_installation_error.bat" 604 else: 605 missing_tools = _find_missing_llvm_tools(repository_ctx, llvm_path) 606 if missing_tools: 607 message = "\r\n".join([ 608 "echo. 1>&2", 609 "echo LLVM/Clang seems to be installed at %s 1>&2" % llvm_path, 610 "echo But Bazel can't find the following tools: 1>&2", 611 "echo %s 1>&2" % ", ".join(missing_tools), 612 "echo. 1>&2", 613 ]) 614 repository_ctx.template( 615 "clang_installation_error.bat", 616 paths["@rules_cc//cc/private/toolchain:clang_installation_error.bat.tpl"], 617 {"%{clang_error_message}": message}, 618 ) 619 error_script = "clang_installation_error.bat" 620 621 if error_script: 622 write_builtin_include_directory_paths(repository_ctx, "clang-cl", [], file_suffix = "_clangcl") 623 clang_cl_vars = { 624 "%{clang_cl_cl_path}": error_script, 625 "%{clang_cl_cxx_builtin_include_directories}": "", 626 "%{clang_cl_dbg_mode_debug_flag}": "/DEBUG", 627 "%{clang_cl_env_include}": "clang_cl_not_found", 628 "%{clang_cl_env_lib}": "clang_cl_not_found", 629 "%{clang_cl_env_path}": "clang_cl_not_found", 630 "%{clang_cl_env_tmp}": "clang_cl_not_found", 631 "%{clang_cl_fastbuild_mode_debug_flag}": "/DEBUG", 632 "%{clang_cl_lib_path}": error_script, 633 "%{clang_cl_link_path}": error_script, 634 "%{clang_cl_ml_path}": error_script, 635 } 636 return clang_cl_vars 637 638 clang_cl_path = _find_llvm_tool(repository_ctx, llvm_path, "clang-cl.exe") 639 lld_link_path = _find_llvm_tool(repository_ctx, llvm_path, "lld-link.exe") 640 llvm_lib_path = _find_llvm_tool(repository_ctx, llvm_path, "llvm-lib.exe") 641 642 clang_version = _get_clang_version(repository_ctx, clang_cl_path) 643 clang_dir = llvm_path + "\\lib\\clang\\" + clang_version 644 clang_include_path = (clang_dir + "\\include").replace("\\", "\\\\") 645 clang_lib_path = (clang_dir + "\\lib\\windows").replace("\\", "\\\\") 646 647 clang_cl_include_directories = msvc_vars["%{msvc_cxx_builtin_include_directories}"] + (",\n \"%s\"" % clang_include_path) 648 write_builtin_include_directory_paths(repository_ctx, "clang-cl", [clang_cl_include_directories], file_suffix = "_clangcl") 649 clang_cl_vars = { 650 "%{clang_cl_cl_path}": clang_cl_path, 651 "%{clang_cl_cxx_builtin_include_directories}": clang_cl_include_directories, 652 # LLVM's lld-link.exe doesn't support /DEBUG:FASTLINK. 653 "%{clang_cl_dbg_mode_debug_flag}": "/DEBUG", 654 "%{clang_cl_env_include}": msvc_vars["%{msvc_env_include}"] + ";" + clang_include_path, 655 "%{clang_cl_env_lib}": msvc_vars["%{msvc_env_lib}"] + ";" + clang_lib_path, 656 "%{clang_cl_env_path}": msvc_vars["%{msvc_env_path}"], 657 "%{clang_cl_env_tmp}": msvc_vars["%{msvc_env_tmp}"], 658 "%{clang_cl_fastbuild_mode_debug_flag}": "/DEBUG", 659 "%{clang_cl_lib_path}": llvm_lib_path, 660 "%{clang_cl_link_path}": lld_link_path, 661 "%{clang_cl_ml_path}": msvc_vars["%{msvc_ml_path}"], 662 } 663 return clang_cl_vars 664 665def configure_windows_toolchain(repository_ctx): 666 """Configure C++ toolchain on Windows. 667 668 Args: 669 repository_ctx: The repository context. 670 """ 671 paths = resolve_labels(repository_ctx, [ 672 "@rules_cc//cc/private/toolchain:BUILD.windows.tpl", 673 "@rules_cc//cc/private/toolchain:windows_cc_toolchain_config.bzl", 674 "@rules_cc//cc/private/toolchain:armeabi_cc_toolchain_config.bzl", 675 "@rules_cc//cc/private/toolchain:vc_installation_error.bat.tpl", 676 "@rules_cc//cc/private/toolchain:msys_gcc_installation_error.bat", 677 "@rules_cc//cc/private/toolchain:clang_installation_error.bat.tpl", 678 ]) 679 680 repository_ctx.symlink( 681 paths["@rules_cc//cc/private/toolchain:windows_cc_toolchain_config.bzl"], 682 "windows_cc_toolchain_config.bzl", 683 ) 684 repository_ctx.symlink( 685 paths["@rules_cc//cc/private/toolchain:armeabi_cc_toolchain_config.bzl"], 686 "armeabi_cc_toolchain_config.bzl", 687 ) 688 repository_ctx.symlink( 689 paths["@rules_cc//cc/private/toolchain:msys_gcc_installation_error.bat"], 690 "msys_gcc_installation_error.bat", 691 ) 692 693 template_vars = dict() 694 msvc_vars = _get_msvc_vars(repository_ctx, paths) 695 template_vars.update(msvc_vars) 696 template_vars.update(_get_clang_cl_vars(repository_ctx, paths, msvc_vars)) 697 template_vars.update(_get_msys_mingw_vars(repository_ctx)) 698 699 repository_ctx.template( 700 "BUILD", 701 paths["@rules_cc//cc/private/toolchain:BUILD.windows.tpl"], 702 template_vars, 703 ) 704