Skip to content

Commit cb1d170

Browse files
committed
WIP: Add clang-cl windows cross compilation
Hi, this is going to be a little insane and a little strange but I would like to get a read on the following as I refine it towards a potential addition to these rules. The following code allows for cross compiling windows (with MSVC ABI rather than GNU, and I hope to adapt it to cope with clang on windows too. If this is something wanted I can polish these rules further rather than fork and adapt. This is very much an initial read, so there are some hacks, a hardcoded path I could use help with and probably constructions that I am sure we want to change.
1 parent f14a8a5 commit cb1d170

File tree

8 files changed

+206
-5
lines changed

8 files changed

+206
-5
lines changed

platforms/BUILD.bazel

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,11 @@ platform(
3737
"@platforms//cpu:x86_64",
3838
],
3939
)
40+
41+
platform(
42+
name = "windows-x86_64",
43+
constraint_values = [
44+
"@platforms//os:windows",
45+
"@platforms//cpu:x86_64",
46+
],
47+
)

toolchain/BUILD.llvm_repo

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,41 @@ filegroup(
3131
],
3232
)
3333

34+
filegroup(
35+
name = "clang-cl",
36+
srcs = [
37+
"bin/clang-cl",
38+
],
39+
)
40+
41+
filegroup(
42+
name = "llvm-ml",
43+
srcs = [
44+
"bin/llvm-ml",
45+
],
46+
)
47+
3448
filegroup(
3549
name = "ld",
3650
srcs = [
3751
"bin/ld.lld",
3852
],
3953
)
4054

55+
filegroup(
56+
name = "lld-link",
57+
srcs = [
58+
"bin/lld-link",
59+
],
60+
)
61+
62+
filegroup(
63+
name = "llvm-lib",
64+
srcs = [
65+
"bin/llvm-lib",
66+
]
67+
)
68+
4169
filegroup(
4270
name = "include",
4371
srcs = glob([

toolchain/cc_toolchain_config.bzl

Lines changed: 143 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ load(
1616
"@bazel_tools//tools/cpp:unix_cc_toolchain_config.bzl",
1717
unix_cc_toolchain_config = "cc_toolchain_config",
1818
)
19+
load(
20+
"@bazel_tools//tools/cpp:windows_cc_toolchain_config.bzl",
21+
windows_cc_toolchain_config = "cc_toolchain_config",
22+
)
1923
load(
2024
"//toolchain/internal:common.bzl",
2125
_check_os_arch_keys = "check_os_arch_keys",
@@ -43,6 +47,14 @@ def cc_toolchain_config(
4347
compiler_configuration,
4448
llvm_version,
4549
host_tools_info = {}):
50+
51+
# Windows even if cross compiling is different to the point of just using a
52+
# different configuration step
53+
if target_os == "windows":
54+
return windows_cc_toolchain(name, host_arch, host_os, target_arch, target_os,
55+
toolchain_path_prefix, tools_path_prefix, wrapper_bin_prefix,
56+
compiler_configuration, llvm_version, host_tools_info = {})
57+
4658
host_os_arch_key = _os_arch_pair(host_os, host_arch)
4759
target_os_arch_key = _os_arch_pair(target_os, target_arch)
4860
_check_os_arch_keys([host_os_arch_key, target_os_arch_key])
@@ -169,6 +181,7 @@ def cc_toolchain_config(
169181
stdlib = compiler_configuration["stdlib"]
170182
if stdlib == "builtin-libc++" and is_xcompile:
171183
stdlib = "stdc++"
184+
172185
if stdlib == "builtin-libc++":
173186
cxx_flags = [
174187
"-std=" + cxx_standard,
@@ -245,8 +258,10 @@ def cc_toolchain_config(
245258

246259
sysroot_path = compiler_configuration["sysroot_path"]
247260
sysroot_prefix = ""
261+
248262
if sysroot_path:
249263
sysroot_prefix = "%sysroot%"
264+
250265
if target_os == "linux":
251266
cxx_builtin_include_directories.extend([
252267
sysroot_prefix + "/include",
@@ -285,8 +300,6 @@ def cc_toolchain_config(
285300
else:
286301
ar_binary = host_tools_info["libtool"]["path"]
287302

288-
# The tool names come from [here](https://github.com/bazelbuild/bazel/blob/c7e58e6ce0a78fdaff2d716b4864a5ace8917626/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfiguration.java#L76-L90):
289-
# NOTE: Ensure these are listed in toolchain_tools in toolchain/internal/common.bzl.
290303
tool_paths = {
291304
"ar": ar_binary,
292305
"cpp": tools_path_prefix + "clang-cpp",
@@ -358,3 +371,131 @@ def cc_toolchain_config(
358371
supports_start_end_lib = supports_start_end_lib,
359372
builtin_sysroot = sysroot_path,
360373
)
374+
375+
def windows_cc_toolchain(
376+
name,
377+
host_arch,
378+
host_os,
379+
target_arch,
380+
target_os,
381+
toolchain_path_prefix,
382+
tools_path_prefix,
383+
wrapper_bin_prefix,
384+
compiler_configuration,
385+
llvm_version,
386+
host_tools_info = {}):
387+
388+
sysroot_path = compiler_configuration["sysroot_path"]
389+
390+
# For the bring your own sysroot crowd, we assume xwin
391+
# These are the defaults ...
392+
msvc_env_tmp = "msvc_not_found"
393+
msvc_env_path = "msvc_not_found"
394+
msvc_env_include = "msvc_not_found"
395+
msvc_env_lib = "msvc_not_found"
396+
msvc_cl_path = tools_path_prefix + "clang-cl"
397+
msvc_ml_path = tools_path_prefix + "clang-cl" # llvm-ml"
398+
msvc_link_path = tools_path_prefix + "lld-link"
399+
msvc_lib_path = tools_path_prefix + "llvm-lib"
400+
401+
cxx_builtin_include_directories = []
402+
403+
raw_llvm_repo_path = compiler_configuration['raw_llvm_repo_path']
404+
if raw_llvm_repo_path:
405+
cxx_builtin_include_directories.extend([
406+
raw_llvm_repo_path + "include/c++/v1",
407+
raw_llvm_repo_path + "include/windows/c++/v1",
408+
raw_llvm_repo_path + "lib/clang/{}/include".format(llvm_version),
409+
raw_llvm_repo_path + "lib64/clang/{}/include".format(llvm_version),
410+
])
411+
412+
if host_os == "windows":
413+
script_lang = ".bat"
414+
# TODO: Make work on windows ;)
415+
# sysroot = figure_out_sysroot_vs_msvc_install()
416+
fail("Not currently working on windows")
417+
else:
418+
script_lang = ".sh"
419+
if not sysroot_path:
420+
fail("In cross compilation of windows artifacts an MSVCRT sysroot needs to be prepared")
421+
422+
includes = [
423+
sysroot_path + "/crt/include",
424+
sysroot_path + "/sdk/include/ucrt",
425+
sysroot_path + "/sdk/include/um",
426+
sysroot_path + "/sdk/include/shared",
427+
]
428+
429+
lib_paths = [
430+
# TODO: Arch should be inferred
431+
sysroot_path + "/crt/lib/x86_64",
432+
sysroot_path + "/sdk/lib/um/x86_64",
433+
sysroot_path + "/sdk/lib/ucrt/x86_64",
434+
# TODO: How to get this in a sysroot
435+
sysroot_path + "/clang-rt-14.0.0",
436+
]
437+
438+
# HACK we combine paths with `;` which is not a unix idiom, since windows_cc_toolchain_config
439+
# HACK breaks this apart with split :/
440+
msvc_env_include = ';'.join(includes)
441+
cxx_builtin_include_directories.extend(includes)
442+
443+
msvc_env_lib = ';'.join(lib_paths)
444+
#msvc_link_path = ';'.join(lib_paths)
445+
446+
# These are the defaults, how to configure?
447+
archiver_flags = []
448+
default_link_flags = []
449+
450+
tool_paths = {
451+
"ar": tools_path_prefix + "clang-cl",
452+
"ml": tools_path_prefix + "clang-cl", # llvm-ml",
453+
"cpp": tools_path_prefix + "clang-cl",
454+
"gcc": tools_path_prefix + "clang-cl",
455+
"ld": tools_path_prefix + "lld-link",
456+
"gcov": wrapper_bin_prefix + "msvc_nop" + script_lang,
457+
"nm": wrapper_bin_prefix + "msvc_nop" + script_lang,
458+
"objcopy": wrapper_bin_prefix + "msvc_nop" + script_lang,
459+
"objdump": wrapper_bin_prefix + "msvc_nop" + script_lang,
460+
"strip": wrapper_bin_prefix + "msvc_nop" + script_lang,
461+
}
462+
463+
tool_bin_path = "not_found"
464+
465+
cxx_builtin_include_directories.extend(compiler_configuration["additional_include_dirs"])
466+
467+
dbg_mode_debug_flag = "/DEBUG"
468+
fastbuild_mode_debug_flag = "/DEBUG"
469+
470+
windows_cc_toolchain_config(
471+
name = name,
472+
cpu = "x64_windows",
473+
# The compiler is hardcoded, we only support clang (suprise!) and its
474+
# required to satisify https://cs.opensource.google/bazel/bazel/+/master:tools/cpp/windows_cc_toolchain_config.bzl;l=82
475+
compiler = "clang-cl",
476+
# TODO: To support Arm or x86_32 this needs to be configured
477+
toolchain_identifier = "clang_cl_x64",
478+
host_system_name = "linux",
479+
target_system_name = "windows",
480+
# TODO: On windows this should probably figure out where the VC install is
481+
# This toolchain only supports MSCRT as the ABI, I would suggest zig-cc-bazel for GNU ABI
482+
# These values are related to the provided sysroot
483+
target_libc = "msvcrt",
484+
abi_version = "local",
485+
abi_libc_version = "local",
486+
cxx_builtin_include_directories = cxx_builtin_include_directories,
487+
tool_paths = tool_paths,
488+
archiver_flags = ["/MACHINE:X64"],
489+
default_link_flags = ["/MACHINE:X64", "/DEFAULTLIB:clang_rt.builtins-x86_64.lib"],
490+
msvc_env_tmp = msvc_env_tmp,
491+
msvc_env_path = msvc_env_path,
492+
msvc_env_include = msvc_env_include,
493+
msvc_env_lib = msvc_env_lib,
494+
msvc_cl_path = msvc_cl_path,
495+
msvc_ml_path = msvc_ml_path,
496+
msvc_link_path = msvc_link_path,
497+
msvc_lib_path = msvc_lib_path,
498+
dbg_mode_debug_flag = dbg_mode_debug_flag,
499+
fastbuild_mode_debug_flag = fastbuild_mode_debug_flag,
500+
tool_bin_path = tool_bin_path,
501+
)

toolchain/internal/common.bzl

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,23 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
SUPPORTED_TARGETS = [("linux", "x86_64"), ("linux", "aarch64"), ("darwin", "x86_64")]
15+
SUPPORTED_TARGETS = [("linux", "x86_64"), ("linux", "aarch64"), ("darwin", "x86_64"), ("windows", "x86_64")]
1616

1717
host_tool_features = struct(
1818
SUPPORTS_ARG_FILE = "supports_arg_file",
1919
)
2020

2121
toolchain_tools = [
2222
"clang-cpp",
23+
"clang-cl",
2324
"ld.lld",
25+
"lld-link",
26+
"llvm-lib",
2427
"llvm-ar",
2528
"llvm-dwp",
2629
"llvm-profdata",
2730
"llvm-cov",
31+
"llvm-ml",
2832
"llvm-nm",
2933
"llvm-objcopy",
3034
"llvm-objdump",
@@ -58,7 +62,7 @@ def os(rctx):
5862

5963
def os_bzl(os):
6064
# Return the OS string as used in bazel platform constraints.
61-
return {"darwin": "osx", "linux": "linux"}[os]
65+
return {"darwin": "osx", "linux": "linux", "windows": "windows"}[os]
6266

6367
def arch(rctx):
6468
exec_result = rctx.execute([

toolchain/internal/configure.bzl

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ def llvm_register_toolchains():
6767
if toolchain_root[0] == "/" and (len(toolchain_root) == 1 or toolchain_root[1] != "/"):
6868
use_absolute_paths = True
6969

70+
raw_llvm_repo_path = ""
71+
7072
if use_absolute_paths:
7173
llvm_repo_label = Label(toolchain_root + ":BUILD.bazel") # Exact target does not matter.
7274
llvm_repo_path = _canonical_dir_path(str(rctx.path(llvm_repo_label).dirname))
@@ -79,6 +81,7 @@ def llvm_register_toolchains():
7981
else:
8082
llvm_repo_label = Label(toolchain_root + ":BUILD.bazel") # Exact target does not matter.
8183
llvm_repo_path = _pkg_path_from_label(llvm_repo_label)
84+
raw_llvm_repo_path = _canonical_dir_path(str(rctx.path(llvm_repo_label).dirname))
8285
config_repo_path = "external/%s/" % rctx.name
8386

8487
# tools can only be defined in a subdirectory of config_repo_path,
@@ -97,6 +100,7 @@ def llvm_register_toolchains():
97100
tools_path_prefix = "bin/"
98101
for tool_name in _toolchain_tools:
99102
rctx.symlink("../../" + llvm_repo_path + "/bin/" + tool_name, tools_path_prefix + tool_name)
103+
100104
symlinked_tools_str = "\n".join([" " * 8 + "\"" + tools_path_prefix + name + "\"," for name in _toolchain_tools])
101105
llvm_repo_label_prefix = toolchain_root + ":"
102106
toolchain_path_prefix = llvm_repo_path
@@ -128,6 +132,7 @@ def llvm_register_toolchains():
128132
coverage_link_flags_dict = rctx.attr.coverage_link_flags,
129133
unfiltered_compile_flags_dict = rctx.attr.unfiltered_compile_flags,
130134
llvm_version = rctx.attr.llvm_version,
135+
raw_llvm_repo_path = raw_llvm_repo_path,
131136
)
132137
host_dl_ext = "dylib" if os == "darwin" else "so"
133138
host_tools_info = dict([
@@ -257,6 +262,7 @@ def _cc_toolchain_str(
257262
target_os,
258263
target_arch,
259264
)
265+
260266
if not sysroot_path:
261267
if host_os == target_os and host_arch == target_arch:
262268
# For darwin -> darwin, we can use the macOS SDK path.
@@ -265,6 +271,7 @@ def _cc_toolchain_str(
265271
# We are trying to cross-compile without a sysroot, let's bail.
266272
# TODO: Are there situations where we can continue?
267273
return ""
274+
268275
sysroot_label_str = "\"%s\"" % str(sysroot) if sysroot else ""
269276

270277
extra_files_str = ", \":internal-use-symlinked-tools\", \":internal-use-wrapped-tools\""
@@ -302,6 +309,7 @@ cc_toolchain_config(
302309
"coverage_compile_flags": {coverage_compile_flags},
303310
"coverage_link_flags": {coverage_link_flags},
304311
"unfiltered_compile_flags": {unfiltered_compile_flags},
312+
"raw_llvm_repo_path": "{raw_llvm_repo_path}",
305313
}},
306314
llvm_version = "{llvm_version}",
307315
host_tools_info = {host_tools_info},
@@ -322,6 +330,8 @@ toolchain(
322330
)
323331
"""
324332

333+
# TODO: Should this look at windows and not require lld-link / clang-cl for all platforms?
334+
325335
if use_absolute_paths:
326336
template = template + """
327337
cc_toolchain(
@@ -346,6 +356,8 @@ filegroup(
346356
name = "compiler-components-{suffix}",
347357
srcs = [
348358
"{llvm_repo_label_prefix}clang",
359+
"{llvm_repo_label_prefix}clang-cl",
360+
"{llvm_repo_label_prefix}llvm-lib",
349361
"{llvm_repo_label_prefix}include",
350362
":sysroot-components-{suffix}",
351363
],
@@ -356,6 +368,8 @@ filegroup(
356368
srcs = [
357369
"{llvm_repo_label_prefix}clang",
358370
"{llvm_repo_label_prefix}ld",
371+
"{llvm_repo_label_prefix}lld-link",
372+
"{llvm_repo_label_prefix}llvm-lib",
359373
"{llvm_repo_label_prefix}ar",
360374
"{llvm_repo_label_prefix}lib",
361375
":sysroot-components-{suffix}",
@@ -403,6 +417,7 @@ cc_toolchain(
403417
target_os_bzl = target_os_bzl,
404418
host_os_bzl = host_os_bzl,
405419
llvm_repo_label_prefix = toolchain_info.llvm_repo_label_prefix,
420+
raw_llvm_repo_path = toolchain_info.raw_llvm_repo_path,
406421
toolchain_path_prefix = toolchain_info.toolchain_path_prefix,
407422
tools_path_prefix = toolchain_info.tools_path_prefix,
408423
wrapper_bin_prefix = toolchain_info.wrapper_bin_prefix,

toolchain/internal/sysroot.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ def _darwin_sdk_path(rctx):
3030
# sysroot for the target, and when host platform is the same as target
3131
# platform.
3232
def default_sysroot_path(rctx, os):
33+
# TODO: Add windows
3334
if os == "darwin":
3435
return _darwin_sdk_path(rctx)
3536
else:

toolchain/tools/host_os_key.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
import sys
2020

2121

22-
_known_distros = ["freebsd", "suse", "ubuntu", "arch", "manjaro", "debian", "fedora", "centos", "amzn", "raspbian", "pop"]
22+
_known_distros = ["freebsd", "suse", "ubuntu", "arch", "manjaro", "debian",
23+
"fedora", "centos", "amzn", "raspbian", "pop", "nixos"]
2324

2425

2526
def _linux_dist():

toolchain/tools/llvm_release_name.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ def _linux(llvm_version, distname, version, arch):
122122
elif distname == "raspbian":
123123
arch = "armv7a"
124124
os_name = "linux-gnueabihf"
125+
elif distname == "nixos":
126+
# This is a lie that works with nix-ld
127+
os_name = "linux-gnu-ubuntu-18.04"
125128
else:
126129
sys.exit("Unsupported linux distribution and version: %s, %s" % (distname, version))
127130

0 commit comments

Comments
 (0)