Add toolchain support for clang-cl on Windows
Previously we [added clang-cl.exe support](https://github.com/bazelbuild/bazel/pull/6553) through `USE_CLANG_CL` environment variable. That is a "hack" on the existing MSVC toolchain to make it use Clang tools. This change add proper toolchain support for `clang-cl.exe` on Windows.
Because `clang-cl.exe` is MSVC compatible, we can just reuse the MSVC toolchain definition.
After this change, to select the clang-cl toolchain on Windows, you can use:
- Before Bazel 1.0, specify build flag `--compiler=clang-cl`, this is deprecated and will be disabled in Bazel 1.0.
- From Bazel 1.0 (or with `--incompatible_enable_cc_toolchain_resolution` flag). You have to add a platform target to your build file (eg. the top level BUILD file):
```
platform(
name = "windows-clang-cl",
constraint_values = [
"@platforms//cpu:x86_64",
"@platforms//os:windows",
"@bazel_tools//tools/cpp:clang-cl",
]
)
```
Then you can tell Bazel to use the clang-cl toolchain by specifying build flags
```
--extra_toolchains=@local_config_cc//:cc-toolchain-x64_windows-clang-cl --extra_execution_platforms=//:windows-clang-cl
```
or registering the platform and toolchain in your WORKSPACE file:
```
register_execution_platforms(
":windows-clang-cl"
)
register_toolchains(
"@local_config_cc//:cc-toolchain-x64_windows-clang-cl",
)
```
Related issue: [incompatible_enable_cc_toolchain_resolution: Turn on toolchain resolution for cc rules](https://github.com/bazelbuild/bazel/issues/7260)
Closes #8917.
PiperOrigin-RevId: 258961022
diff --git a/site/docs/windows.md b/site/docs/windows.md
index 3043b7b..1f3d445 100644
--- a/site/docs/windows.md
+++ b/site/docs/windows.md
@@ -117,9 +117,9 @@
See the [Build C++ section](#build_cpp) below.
<a name="build_cpp"></a>
-### Build C++
+### Build C++ with MSVC
-To build C++ targets, you need:
+To build C++ targets with MSVC, you need:
* The Visual C++ compiler.
@@ -212,6 +212,58 @@
To build and use Dynamically Linked Libraries (DLL files), see [this
example](https://github.com/bazelbuild/bazel/tree/master/examples/windows/dll).
+### Build C++ with Clang
+
+From 0.29.0, Bazel supports building with LLVM's MSVC-compatible compiler driver (`clang-cl.exe`).
+
+**Requirement**: To build with Clang, you have to install **both**
+[LLVM](http://releases.llvm.org/download.html) and Visual C++ Build tools, because although we use
+`clang-cl.exe` as compiler, we still need to link to Visual C++ libraries.
+
+Bazel can automatically detect LLVM installation on your system, or you can explicitly tell
+Bazel where LLVM is installed by `BAZEL_LLVM`.
+
+* `BAZEL_LLVM` the LLVM installation directory
+
+ ```
+ set BAZEL_LLVM=C:\Program Files\LLVM
+ ```
+
+To enable the Clang toolchain for building C++, there are several situations.
+
+* In bazel 0.28 and older: Clang is not supported.
+
+* In Bazel 0.29.0: You can enable the Clang toolchain by a build flag `--compiler=clang-cl`.
+ This is deprecated and will be removed in Bazel 1.0.
+
+* From Bazel 1.0: You have to add a platform target to your build file (eg. the top level BUILD file):
+ ```
+ platform(
+ name = "windows-clang-cl",
+ constraint_values = [
+ "@platforms//cpu:x86_64",
+ "@platforms//os:windows",
+ "@bazel_tools//tools/cpp:clang-cl",
+ ],
+ )
+ ```
+ Then you can enable the Clang toolchain by either of the following two ways:
+ * Specify the following build flags:
+ ```
+ --extra_toolchains=@local_config_cc//:cc-toolchain-x64_windows-clang-cl --extra_execution_platforms=//:windows-clang-cl
+ ```
+ * Register the platform and toolchain in your WORKSPACE file:
+ ```
+ register_execution_platforms(
+ ":windows-clang-cl"
+ )
+
+ register_toolchains(
+ "@local_config_cc//:cc-toolchain-x64_windows-clang-cl",
+ )
+ ```
+ The reason we have those two ways is because [--incompatible_enable_cc_toolchain_resolution](https://github.com/bazelbuild/bazel/issues/7260) flag.
+
### Build Java
There's no setup necessary.
diff --git a/src/test/py/bazel/bazel_windows_cpp_test.py b/src/test/py/bazel/bazel_windows_cpp_test.py
index 274779f..f28c615 100644
--- a/src/test/py/bazel/bazel_windows_cpp_test.py
+++ b/src/test/py/bazel/bazel_windows_cpp_test.py
@@ -592,6 +592,63 @@
self.AssertExitCode(exit_code, 1, stderr)
self.assertIn('this_is_an_error', ''.join(stdout))
+ def testBuildWithClangClByCompilerFlag(self):
+ self.CreateWorkspaceWithDefaultRepos('WORKSPACE')
+ self.ScratchFile('BUILD', [
+ 'cc_binary(',
+ ' name = "main",',
+ ' srcs = ["main.cc"],',
+ ')',
+ ])
+ self.ScratchFile('main.cc', [
+ 'int main() {',
+ ' return 0;',
+ '}',
+ ])
+ exit_code, _, stderr = self.RunBazel([
+ 'build', '-s', '--compiler=clang-cl',
+ '--incompatible_enable_cc_toolchain_resolution=false', '//:main'
+ ])
+ self.AssertExitCode(exit_code, 0, stderr)
+ self.assertIn('clang-cl.exe', ''.join(stderr))
+
+ def testBuildWithClangClByToolchainResolution(self):
+ self.CreateWorkspaceWithDefaultRepos('WORKSPACE', [
+ 'register_execution_platforms(',
+ ' ":windows_clang"',
+ ')',
+ '',
+ 'register_toolchains(',
+ ' "@local_config_cc//:cc-toolchain-x64_windows-clang-cl",',
+ ')',
+ ])
+ self.ScratchFile('BUILD', [
+ 'platform(',
+ ' name = "windows_clang",',
+ ' constraint_values = [',
+ ' "@platforms//cpu:x86_64",',
+ ' "@platforms//os:windows",',
+ ' "@bazel_tools//tools/cpp:clang-cl",',
+ ' ]',
+ ')',
+ '',
+ 'cc_binary(',
+ ' name = "main",',
+ ' srcs = ["main.cc"],',
+ ')',
+ ])
+ self.ScratchFile('main.cc', [
+ 'int main() {',
+ ' return 0;',
+ '}',
+ ])
+ exit_code, _, stderr = self.RunBazel([
+ 'build', '-s', '--incompatible_enable_cc_toolchain_resolution=true',
+ '//:main'
+ ])
+ self.AssertExitCode(exit_code, 0, stderr)
+ self.assertIn('clang-cl.exe', ''.join(stderr))
+
if __name__ == '__main__':
unittest.main()
diff --git a/src/test/py/bazel/test_base.py b/src/test/py/bazel/test_base.py
index ca6f792..bbb2184 100644
--- a/src/test/py/bazel/test_base.py
+++ b/src/test/py/bazel/test_base.py
@@ -115,12 +115,12 @@
actual_exit_code, lambda x: x != not_expected_exit_code,
'(against expectations)', stderr_lines, stdout_lines)
- def CreateWorkspaceWithDefaultRepos(self, path):
+ def CreateWorkspaceWithDefaultRepos(self, path, lines=None):
rule_definition = [
'load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")'
]
rule_definition.extend(self.GetDefaultRepoRules())
- self.ScratchFile(path, rule_definition)
+ self.ScratchFile(path, rule_definition + (lines if lines else []))
def GetDefaultRepoRules(self):
return self.GetCcRulesRepoRule()
diff --git a/tools/cpp/BUILD b/tools/cpp/BUILD
index 8222d23..d7cc616 100644
--- a/tools/cpp/BUILD
+++ b/tools/cpp/BUILD
@@ -41,6 +41,11 @@
)
constraint_value(
+ name = "clang-cl",
+ constraint_setting = ":cc_compiler",
+)
+
+constraint_value(
name = "mingw",
constraint_setting = ":cc_compiler",
)
diff --git a/tools/cpp/BUILD.windows.tpl b/tools/cpp/BUILD.windows.tpl
index 18df650..be903f9 100644
--- a/tools/cpp/BUILD.windows.tpl
+++ b/tools/cpp/BUILD.windows.tpl
@@ -35,6 +35,7 @@
"x64_windows|msvc-cl": ":cc-compiler-x64_windows",
"x64_windows|msys-gcc": ":cc-compiler-x64_windows_msys",
"x64_windows|mingw-gcc": ":cc-compiler-x64_windows_mingw",
+ "x64_windows|clang-cl": ":cc-compiler-x64_windows-clang-cl",
"x64_windows_msys": ":cc-compiler-x64_windows_msys",
"x64_windows": ":cc-compiler-x64_windows",
"armeabi-v7a": ":cc-compiler-armeabi-v7a",
@@ -179,6 +180,7 @@
"objdump": "wrapper/bin/msvc_nop.bat",
"strip": "wrapper/bin/msvc_nop.bat",
},
+ default_link_flags = ["/MACHINE:X64"],
dbg_mode_debug_flag = "%{dbg_mode_debug_flag}",
fastbuild_mode_debug_flag = "%{fastbuild_mode_debug_flag}",
)
@@ -198,6 +200,72 @@
)
cc_toolchain(
+ name = "cc-compiler-x64_windows-clang-cl",
+ toolchain_identifier = "clang_cl_x64",
+ toolchain_config = ":clang_cl_x64",
+ all_files = ":empty",
+ ar_files = ":empty",
+ as_files = ":empty",
+ compiler_files = ":empty",
+ dwp_files = ":empty",
+ linker_files = ":empty",
+ objcopy_files = ":empty",
+ strip_files = ":empty",
+ supports_param_files = 1,
+)
+
+cc_toolchain_config(
+ name = "clang_cl_x64",
+ cpu = "x64_windows",
+ compiler = "clang-cl",
+ host_system_name = "local",
+ target_system_name = "local",
+ target_libc = "msvcrt",
+ abi_version = "local",
+ abi_libc_version = "local",
+ toolchain_identifier = "clang_cl_x64",
+ msvc_env_tmp = "%{clang_cl_env_tmp}",
+ msvc_env_path = "%{clang_cl_env_path}",
+ msvc_env_include = "%{clang_cl_env_include}",
+ msvc_env_lib = "%{clang_cl_env_lib}",
+ msvc_cl_path = "%{clang_cl_cl_path}",
+ msvc_ml_path = "%{clang_cl_ml_path}",
+ msvc_link_path = "%{clang_cl_link_path}",
+ msvc_lib_path = "%{clang_cl_lib_path}",
+ cxx_builtin_include_directories = [%{clang_cl_cxx_builtin_include_directories}],
+ tool_paths = {
+ "ar": "%{clang_cl_lib_path}",
+ "ml": "%{clang_cl_ml_path}",
+ "cpp": "%{clang_cl_cl_path}",
+ "gcc": "%{clang_cl_cl_path}",
+ "gcov": "wrapper/bin/msvc_nop.bat",
+ "ld": "%{clang_cl_link_path}",
+ "nm": "wrapper/bin/msvc_nop.bat",
+ "objcopy": "wrapper/bin/msvc_nop.bat",
+ "objdump": "wrapper/bin/msvc_nop.bat",
+ "strip": "wrapper/bin/msvc_nop.bat",
+ },
+ default_link_flags = ["/MACHINE:X64", "/DEFAULTLIB:clang_rt.builtins-x86_64.lib"],
+ dbg_mode_debug_flag = "%{clang_cl_dbg_mode_debug_flag}",
+ fastbuild_mode_debug_flag = "%{clang_cl_fastbuild_mode_debug_flag}",
+)
+
+toolchain(
+ name = "cc-toolchain-x64_windows-clang-cl",
+ exec_compatible_with = [
+ "@platforms//cpu:x86_64",
+ "@platforms//os:windows",
+ "@bazel_tools//tools/cpp:clang-cl",
+ ],
+ target_compatible_with = [
+ "@platforms//cpu:x86_64",
+ "@platforms//os:windows",
+ ],
+ toolchain = ":cc-compiler-x64_windows-clang-cl",
+ toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
+)
+
+cc_toolchain(
name = "cc-compiler-armeabi-v7a",
toolchain_identifier = "stub_armeabi-v7a",
toolchain_config = ":stub_armeabi-v7a",
diff --git a/tools/cpp/clang_installation_error.bat.tpl b/tools/cpp/clang_installation_error.bat.tpl
new file mode 100644
index 0000000..bec44eb
--- /dev/null
+++ b/tools/cpp/clang_installation_error.bat.tpl
@@ -0,0 +1,24 @@
+:: Copyright 2019 The Bazel Authors. All rights reserved.
+::
+:: Licensed under the Apache License, Version 2.0 (the "License");
+:: you may not use this file except in compliance with the License.
+:: You may obtain a copy of the License at
+::
+:: http://www.apache.org/licenses/LICENSE-2.0
+::
+:: Unless required by applicable law or agreed to in writing, software
+:: distributed under the License is distributed on an "AS IS" BASIS,
+:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+:: See the License for the specific language governing permissions and
+:: limitations under the License.
+
+@echo OFF
+
+echo. 1>&2
+echo The target you are compiling requires the Clang compiler. 1>&2
+echo Bazel couldn't find a valid Clang installation on your machine. 1>&2
+%{clang_error_message}
+echo Please check your installation following https://docs.bazel.build/versions/master/windows.html#using 1>&2
+echo. 1>&2
+
+exit /b 1
diff --git a/tools/cpp/windows_cc_configure.bzl b/tools/cpp/windows_cc_configure.bzl
index 8cd9771..2b6e690 100644
--- a/tools/cpp/windows_cc_configure.bzl
+++ b/tools/cpp/windows_cc_configure.bzl
@@ -455,6 +455,15 @@
"""Returns True if USE_CLANG_CL is set to 1."""
return repository_ctx.os.environ.get("USE_CLANG_CL", default = "0") == "1"
+def _find_missing_llvm_tools(repository_ctx, llvm_path):
+ """Check if any required tool is missing under given LLVM path."""
+ missing_tools = []
+ for tool in ["clang-cl.exe", "lld-link.exe", "llvm-lib.exe"]:
+ if not find_llvm_tool(repository_ctx, llvm_path, tool):
+ missing_tools.append(tool)
+
+ return missing_tools
+
def _get_clang_version(repository_ctx, clang_cl):
result = repository_ctx.execute([clang_cl, "-v"])
if result.return_code != 0:
@@ -575,6 +584,77 @@
}
return msvc_vars
+def _get_clang_cl_vars(repository_ctx, paths, msvc_vars):
+ """Get the variables we need to populate the clang-cl toolchains."""
+ llvm_path = find_llvm_path(repository_ctx)
+ error_script = None
+ if msvc_vars["%{msvc_cl_path}"] == "vc_installation_error.bat":
+ error_script = "vc_installation_error.bat"
+ elif not llvm_path:
+ repository_ctx.template(
+ "clang_installation_error.bat",
+ paths["@bazel_tools//tools/cpp:clang_installation_error.bat.tpl"],
+ {"%{clang_error_message}": ""},
+ )
+ error_script = "clang_installation_error.bat"
+ else:
+ missing_tools = _find_missing_llvm_tools(repository_ctx, llvm_path)
+ if missing_tools:
+ message = "\r\n".join([
+ "echo. 1>&2",
+ "echo LLVM/Clang seems to be installed at %s 1>&2" % llvm_path,
+ "echo But Bazel can't find the following tools: 1>&2",
+ "echo %s 1>&2" % ", ".join(missing_tools),
+ "echo. 1>&2",
+ ])
+ repository_ctx.template(
+ "clang_installation_error.bat",
+ paths["@bazel_tools//tools/cpp:clang_installation_error.bat.tpl"],
+ {"%{clang_error_message}": message},
+ )
+ error_script = "clang_installation_error.bat"
+
+ if error_script:
+ clang_cl_vars = {
+ "%{clang_cl_env_tmp}": "clang_cl_not_found",
+ "%{clang_cl_env_path}": "clang_cl_not_found",
+ "%{clang_cl_env_include}": "clang_cl_not_found",
+ "%{clang_cl_env_lib}": "clang_cl_not_found",
+ "%{clang_cl_cl_path}": error_script,
+ "%{clang_cl_link_path}": error_script,
+ "%{clang_cl_lib_path}": error_script,
+ "%{clang_cl_ml_path}": error_script,
+ "%{clang_cl_dbg_mode_debug_flag}": "/DEBUG",
+ "%{clang_cl_fastbuild_mode_debug_flag}": "/DEBUG",
+ "%{clang_cl_cxx_builtin_include_directories}": "",
+ }
+ return clang_cl_vars
+
+ clang_cl_path = find_llvm_tool(repository_ctx, llvm_path, "clang-cl.exe")
+ lld_link_path = find_llvm_tool(repository_ctx, llvm_path, "lld-link.exe")
+ llvm_lib_path = find_llvm_tool(repository_ctx, llvm_path, "llvm-lib.exe")
+
+ clang_version = _get_clang_version(repository_ctx, clang_cl_path)
+ clang_dir = llvm_path + "\\lib\\clang\\" + clang_version
+ clang_include_path = (clang_dir + "\\include").replace("\\", "\\\\")
+ clang_lib_path = (clang_dir + "\\lib\\windows").replace("\\", "\\\\")
+
+ clang_cl_vars = {
+ "%{clang_cl_env_tmp}": msvc_vars["%{msvc_env_tmp}"],
+ "%{clang_cl_env_path}": msvc_vars["%{msvc_env_path}"],
+ "%{clang_cl_env_include}": msvc_vars["%{msvc_env_include}"] + ";" + clang_include_path,
+ "%{clang_cl_env_lib}": msvc_vars["%{msvc_env_lib}"] + ";" + clang_lib_path,
+ "%{clang_cl_cxx_builtin_include_directories}": msvc_vars["%{msvc_cxx_builtin_include_directories}"] + (",\n \"%s\"" % clang_include_path),
+ "%{clang_cl_cl_path}": clang_cl_path,
+ "%{clang_cl_link_path}": lld_link_path,
+ "%{clang_cl_lib_path}": llvm_lib_path,
+ "%{clang_cl_ml_path}": msvc_vars["%{msvc_ml_path}"],
+ # LLVM's lld-link.exe doesn't support /DEBUG:FASTLINK.
+ "%{clang_cl_dbg_mode_debug_flag}": "/DEBUG",
+ "%{clang_cl_fastbuild_mode_debug_flag}": "/DEBUG",
+ }
+ return clang_cl_vars
+
def configure_windows_toolchain(repository_ctx):
"""Configure C++ toolchain on Windows."""
paths = resolve_labels(repository_ctx, [
@@ -583,6 +663,7 @@
"@bazel_tools//tools/cpp:armeabi_cc_toolchain_config.bzl",
"@bazel_tools//tools/cpp:vc_installation_error.bat.tpl",
"@bazel_tools//tools/cpp:msys_gcc_installation_error.bat",
+ "@bazel_tools//tools/cpp:clang_installation_error.bat.tpl",
])
repository_ctx.symlink(
@@ -599,7 +680,9 @@
)
template_vars = dict()
- template_vars.update(_get_msvc_vars(repository_ctx, paths))
+ msvc_vars = _get_msvc_vars(repository_ctx, paths)
+ template_vars.update(msvc_vars)
+ template_vars.update(_get_clang_cl_vars(repository_ctx, paths, msvc_vars))
template_vars.update(_get_msys_mingw_vars(repository_ctx))
repository_ctx.template(
diff --git a/tools/cpp/windows_cc_toolchain_config.bzl b/tools/cpp/windows_cc_toolchain_config.bzl
index e2aa853..30571b6 100644
--- a/tools/cpp/windows_cc_toolchain_config.bzl
+++ b/tools/cpp/windows_cc_toolchain_config.bzl
@@ -79,8 +79,11 @@
ACTION_NAMES.cpp_link_nodeps_dynamic_library,
]
+def _use_msvc_toolchain(ctx):
+ return ctx.attr.cpu == "x64_windows" and (ctx.attr.compiler == "msvc-cl" or ctx.attr.compiler == "clang-cl")
+
def _impl(ctx):
- if ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "msvc-cl":
+ if _use_msvc_toolchain(ctx):
artifact_name_patterns = [
artifact_name_pattern(
category_name = "object_file",
@@ -122,7 +125,7 @@
),
]
- if ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "msvc-cl":
+ if _use_msvc_toolchain(ctx):
cpp_link_nodeps_dynamic_library_action = action_config(
action_name = ACTION_NAMES.cpp_link_nodeps_dynamic_library,
implies = [
@@ -261,7 +264,7 @@
else:
action_configs = []
- if ctx.attr.cpu == "x64_windows" and ctx.attr.compiler == "msvc-cl":
+ if _use_msvc_toolchain(ctx):
msvc_link_env_feature = feature(
name = "msvc_link_env",
env_sets = [
@@ -299,7 +302,7 @@
"-D__DATE__=\"redacted\"",
"-D__TIMESTAMP__=\"redacted\"",
"-D__TIME__=\"redacted\"",
- ],
+ ] + (["-Wno-builtin-macro-redefined"] if ctx.attr.compiler == "clang-cl" else []),
),
],
),
@@ -508,7 +511,7 @@
flag_sets = [
flag_set(
actions = all_link_actions,
- flag_groups = [flag_group(flags = ["/MACHINE:X64"])],
+ flag_groups = [flag_group(flags = ctx.attr.default_link_flags)],
),
],
)
@@ -1322,6 +1325,7 @@
"abi_libc_version": attr.string(),
"tool_paths": attr.string_dict(),
"cxx_builtin_include_directories": attr.string_list(),
+ "default_link_flags": attr.string_list(default = []),
"msvc_env_tmp": attr.string(default = "msvc_not_found"),
"msvc_env_path": attr.string(default = "msvc_not_found"),
"msvc_env_include": attr.string(default = "msvc_not_found"),