Skip to content

feat: cross-compile to windows msvc #390

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 15 commits into
base: master
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions platforms/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -45,3 +45,11 @@ platform(
"@platforms//cpu:aarch64",
],
)

platform(
name = "windows-msvc-x86_64",
constraint_values = [
"@platforms//os:windows",
"@platforms//cpu:x86_64",
],
)
2 changes: 2 additions & 0 deletions toolchain/BUILD.llvm_repo
Original file line number Diff line number Diff line change
@@ -32,6 +32,7 @@ filegroup(
srcs = [
"bin/clang",
"bin/clang++",
"bin/clang-cl",
"bin/clang-cpp",
],
)
@@ -41,6 +42,7 @@ filegroup(
srcs = [
"bin/ld.lld",
"bin/ld64.lld",
"bin/lld-link",
],
)

1 change: 1 addition & 0 deletions toolchain/BUILD.toolchain.tpl
Original file line number Diff line number Diff line change
@@ -35,6 +35,7 @@ filegroup(
name = "internal-use-wrapped-tools",
srcs = [
"%{wrapper_bin_prefix}cc_wrapper.sh",
"%{wrapper_bin_prefix}cc_wrapper_msvc.sh",
],
visibility = ["//visibility:private"],
)
303 changes: 171 additions & 132 deletions toolchain/cc_toolchain_config.bzl
Original file line number Diff line number Diff line change
@@ -16,6 +16,10 @@ load(
"@bazel_tools//tools/cpp:unix_cc_toolchain_config.bzl",
unix_cc_toolchain_config = "cc_toolchain_config",
)
load(
":windows_cc_toolchain_config.bzl",
windows_cc_toolchain_config = "cc_toolchain_config",
)
load(
"//toolchain/internal:common.bzl",
_check_os_arch_keys = "check_os_arch_keys",
@@ -89,6 +93,14 @@ def cc_toolchain_config(
"clang",
"glibc_unknown",
),
"windows-msvc-x86_64": (
"clang-x86_64-windows-msvc",
"x64_windows",
"msvc",
"clang-cl",
"clang-cl",
"msvc",
),
}[target_os_arch_key]

# Unfiltered compiler flags; these are placed at the end of the command
@@ -105,22 +117,19 @@ def cc_toolchain_config(
"-fdebug-prefix-map={}=__bazel_toolchain_llvm_repo__/".format(toolchain_path_prefix),
]

is_xcompile = not (exec_os == target_os and exec_arch == target_arch)

# Default compiler flags:
compile_flags = [
"--target=" + target_system_name,
# Security
"-U_FORTIFY_SOURCE", # https://github.com/google/sanitizers/issues/247
"-fstack-protector",
"-fno-omit-frame-pointer",
# Diagnostics
"-fcolor-diagnostics",
"-Wall",
"-Wthread-safety",
"-Wself-assign",
]

if target_os != "windows-msvc":
compile_flags.extend([
"-fstack-protector",
"-fno-omit-frame-pointer",
])

dbg_compile_flags = ["-g", "-fstandalone-debug"]

opt_compile_flags = [
@@ -134,62 +143,33 @@ def cc_toolchain_config(

link_flags = [
"--target=" + target_system_name,
"-lm",
"-no-canonical-prefixes",
]

# Similar to link_flags, but placed later in the command line such that
# unused symbols are not stripped.
link_libs = []

# Flags for ar.
archive_flags = []

# Linker flags:
if exec_os == "darwin" and not is_xcompile:
# lld is experimental for Mach-O, so we use the native ld64 linker.
# TODO: How do we cross-compile from Linux to Darwin?
use_lld = False
link_flags.extend([
"-headerpad_max_install_names",
"-fobjc-link-runtime",
])

# Use the bundled libtool (llvm-libtool-darwin).
use_libtool = True

# Pre-installed libtool on macOS has -static as default, but llvm-libtool-darwin needs it
# explicitly. cc_common.create_link_variables does not automatically add this either if
# output_file arg to it is None.
archive_flags.extend([
"-static",
])
else:
# Note that for xcompiling from darwin to linux, the native ld64 is
# not an option because it is not a cross-linker, so lld is the
# only option.
use_lld = True
link_flags.extend([
"-fuse-ld=lld",
"-Wl,--build-id=md5",
"-Wl,--hash-style=gnu",
"-Wl,-z,relro,-z,now",
])
use_libtool = False

# Flags related to C++ standard.
# The linker has no way of knowing if there are C++ objects; so we
# always link C++ libraries.
cxx_standard = compiler_configuration["cxx_standard"]
stdlib = compiler_configuration["stdlib"]
# Let's be compatible with the old way of specifying the standard library.
if stdlib == "stdc++":
print("WARNING: stdc++ is deprecated. Please use libstdc++ instead.")
stdlib = "libstdc++"
sysroot_path = compiler_configuration["sysroot_path"]
if stdlib == "builtin-libc++" and is_xcompile:
stdlib = "stdc++"
if stdlib == "builtin-libc++":
cxx_flags = [

cxx_flags = []

if target_os != "windows-msvc":
cxx_flags.extend([
"-std=" + cxx_standard,
"-stdlib=libc++",
]
"-stdlib=" + stdlib,
])

if stdlib == "libc++":
if major_llvm_version >= 14:
# With C++20, Clang defaults to using C++ rather than Clang modules,
# which breaks Bazel's `use_module_maps` feature, which is used by
@@ -199,52 +179,13 @@ def cc_toolchain_config(
# https://github.com/llvm/llvm-project/commit/0556138624edf48621dd49a463dbe12e7101f17d
cxx_flags.append("-Xclang")
cxx_flags.append("-fno-cxx-modules")
if use_lld:
# For single-platform builds, we can statically link the bundled
# libraries.
link_flags.extend([
"-l:libc++.a",
"-l:libc++abi.a",
"-l:libunwind.a",
# Compiler runtime features.
"-rtlib=compiler-rt",
# To support libunwind.
"-lpthread",
"-ldl",
])
else:
# Several system libraries on macOS dynamically link libc++ and
# libc++abi, so static linking them becomes a problem. We need to
# ensure that they are dynamic linked from the system sysroot and
# not static linked from the toolchain, so explicitly have the
# sysroot directory on the search path and then add the toolchain
# directory back after we are done.
link_flags.extend([
"-L{}/usr/lib".format(sysroot_path),
"-lc++",
"-lc++abi",
"-Bstatic",
"-lunwind",
"-Bdynamic",
"-L{}lib".format(toolchain_path_prefix),
])

elif stdlib == "libc++":
cxx_flags = [
"-std=" + cxx_standard,
"-stdlib=libc++",
]

link_flags.extend([
"-l:c++.a",
"-l:c++abi.a",
"-l:libc++.a",
"-l:libc++abi.a",
"-l:libunwind.a",
])
elif stdlib == "stdc++":
cxx_flags = [
"-std=" + cxx_standard,
"-stdlib=libstdc++",
]

elif stdlib == "libstdc++":
link_flags.extend([
"-l:libstdc++.a",
])
@@ -256,9 +197,90 @@ def cc_toolchain_config(
link_flags.extend([
"-nostdlib",
])
else:
elif target_os != "windows-msvc":
# When targetting Windows, we don't need to link against the standard
# library, as it is provided by the MSVC runtime.
fail("Unknown value passed for stdlib: {stdlib}".format(stdlib = stdlib))

archive_flags = []

if target_os == "darwin":
ld = "ld64.lld"
ld_path = toolchain_path_prefix + "/bin/" + ld
link_flags.extend([
"-headerpad_max_install_names",
"-fobjc-link-runtime",

"-fuse-ld=lld",
"--ld-path=" + ld_path,

# Compiler runtime features.
"-rtlib=compiler-rt",

"-lm",
"-ldl",
"-pthread",
])

# Use the bundled libtool (llvm-libtool-darwin).
use_libtool = True

# Pre-installed libtool on macOS has -static as default, but llvm-libtool-darwin needs it
# explicitly. cc_common.create_link_variables does not automatically add this either if
# output_file arg to it is None.
archive_flags.extend([
"-static",
])
elif target_os == "linux":
ld = "ld.lld"
ld_path = toolchain_path_prefix + "/bin/" + ld
link_flags.extend([
"-fuse-ld=lld",
"--ld-path=" + ld_path,
"-Wl,--build-id=md5",
"-Wl,--hash-style=gnu",
"-Wl,-z,relro,-z,now",
"-lm",
"-ldl",
"-pthread",
])

use_libtool = False
elif target_os == "windows-msvc":
cxx_flags.extend([
"/std:" + cxx_standard,
"-fms-compatibility",
"-fms-extensions",
])
compile_flags.extend([
"/MT",
"/Brepro",
"/DWIN32",
"/D_WIN32",
"/D_WINDOWS",
"/clang:-isystem{}splat/VC/Tools/MSVC/14.41.17.11/include".format(sysroot_path),
"/clang:-isystem{}splat/Windows_Kits/10/include/10.0.26100/um".format(sysroot_path),
"/clang:-isystem{}splat/Windows_Kits/10/include/10.0.26100/shared".format(sysroot_path),
"/clang:-isystem{}splat/Windows_Kits/10/include/10.0.26100/ucrt".format(sysroot_path),
# Do not resolve our symlinked resource prefixes to real paths.
"-no-canonical-prefixes",
# Reproducibility
"-Wno-builtin-macro-redefined",
"/clang:-fdebug-prefix-map={}=__bazel_toolchain_llvm_repo__/".format(toolchain_path_prefix),
])

ld = "lld-link"

link_flags = [
"/libpath:{}splat/Windows_Kits/10/lib/10.0.26100/ucrt/x64".format(sysroot_path),
"/libpath:{}splat/Windows_Kits/10/lib/10.0.26100/um/x64".format(sysroot_path),
"/libpath:{}splat/VC/Tools/MSVC/14.41.17.11/lib/x64".format(sysroot_path),
]

use_libtool = False
else:
fail("Unknown value passed for target_os: {}".format(target_os))

opt_link_flags = ["-Wl,--gc-sections"] if target_os == "linux" else []

# Coverage flags:
@@ -280,9 +302,9 @@ def cc_toolchain_config(
"ar": tools_path_prefix + ("llvm-ar" if not use_libtool else "libtool"),
"cpp": tools_path_prefix + "clang-cpp",
"dwp": tools_path_prefix + "llvm-dwp",
"gcc": wrapper_bin_prefix + "cc_wrapper.sh",
"gcc": wrapper_bin_prefix + ("cc_wrapper_msvc.sh" if target_os == "windows-msvc" else "cc_wrapper.sh"),
"gcov": tools_path_prefix + "llvm-profdata",
"ld": tools_path_prefix + "ld.lld" if use_lld else "/usr/bin/ld",
"ld": tools_path_prefix + ld,
"llvm-cov": tools_path_prefix + "llvm-cov",
"llvm-profdata": tools_path_prefix + "llvm-profdata",
"nm": tools_path_prefix + "llvm-nm",
@@ -291,14 +313,6 @@ def cc_toolchain_config(
"strip": tools_path_prefix + "llvm-strip",
}

# Start-end group linker support:
# This was added to `lld` in this patch: http://reviews.llvm.org/D18814
#
# The oldest version of LLVM that we support is 6.0.0 which was released
# after the above patch was merged, so we just set this to `True` when
# `lld` is being used as the linker.
supports_start_end_lib = use_lld

# Replace flags with any user-provided overrides.
if compiler_configuration["compile_flags"] != None:
compile_flags = _fmt_flags(compiler_configuration["compile_flags"], toolchain_path_prefix)
@@ -323,30 +337,55 @@ def cc_toolchain_config(
if compiler_configuration["unfiltered_compile_flags"] != None:
unfiltered_compile_flags = _fmt_flags(compiler_configuration["unfiltered_compile_flags"], toolchain_path_prefix)

# Source: https://cs.opensource.google/bazel/bazel/+/master:tools/cpp/unix_cc_toolchain_config.bzl
unix_cc_toolchain_config(
name = name,
cpu = target_cpu,
compiler = compiler,
toolchain_identifier = toolchain_identifier,
host_system_name = exec_arch,
target_system_name = target_system_name,
target_libc = target_libc,
abi_version = abi_version,
abi_libc_version = abi_libc_version,
cxx_builtin_include_directories = cxx_builtin_include_directories,
tool_paths = tool_paths,
compile_flags = compile_flags,
dbg_compile_flags = dbg_compile_flags,
opt_compile_flags = opt_compile_flags,
cxx_flags = cxx_flags,
link_flags = link_flags,
archive_flags = archive_flags,
link_libs = link_libs,
opt_link_flags = opt_link_flags,
unfiltered_compile_flags = unfiltered_compile_flags,
coverage_compile_flags = coverage_compile_flags,
coverage_link_flags = coverage_link_flags,
supports_start_end_lib = supports_start_end_lib,
builtin_sysroot = sysroot_path,
)
if target_os == "windows-msvc":
windows_cc_toolchain_config(
name = name,
cpu = target_cpu,
compiler = compiler,
toolchain_identifier = toolchain_identifier,
host_system_name = exec_arch,
target_system_name = target_system_name,
target_libc = target_libc,
abi_version = abi_version,
abi_libc_version = abi_libc_version,
cxx_builtin_include_directories = cxx_builtin_include_directories,
tool_paths = tool_paths,
archiver_flags = archive_flags,
default_compile_flags = compile_flags,
cxx_flags = cxx_flags,
default_link_flags = link_flags,
supports_parse_showincludes = False,
builtin_sysroot = sysroot_path,
msvc_cl_path = tools_path_prefix + "clang-cl",
msvc_ml_path = tools_path_prefix + "clang-cl",
msvc_link_path = tools_path_prefix + ld,
msvc_lib_path = tools_path_prefix + "llvm-lib",
)
else:
# Source: https://cs.opensource.google/bazel/bazel/+/master:tools/cpp/unix_cc_toolchain_config.bzl
unix_cc_toolchain_config(
name = name,
cpu = target_cpu,
compiler = compiler,
toolchain_identifier = toolchain_identifier,
host_system_name = exec_arch,
target_system_name = target_system_name,
target_libc = target_libc,
abi_version = abi_version,
abi_libc_version = abi_libc_version,
cxx_builtin_include_directories = cxx_builtin_include_directories,
tool_paths = tool_paths,
compile_flags = compile_flags,
dbg_compile_flags = dbg_compile_flags,
opt_compile_flags = opt_compile_flags,
cxx_flags = cxx_flags,
link_flags = link_flags,
archive_flags = archive_flags,
link_libs = link_libs,
opt_link_flags = opt_link_flags,
unfiltered_compile_flags = unfiltered_compile_flags,
coverage_compile_flags = coverage_compile_flags,
coverage_link_flags = coverage_link_flags,
supports_start_end_lib = True, # We only support lld, so this is always true.
builtin_sysroot = sysroot_path,
)
6 changes: 3 additions & 3 deletions toolchain/cc_wrapper.sh.tpl
Original file line number Diff line number Diff line change
@@ -32,7 +32,7 @@ set -euo pipefail
# See note in toolchain/internal/configure.bzl where we define
# `wrapper_bin_prefix` for why this wrapper is needed.

if [[ -f %{toolchain_path_prefix}bin/clang ]]; then
if [[ -f %{toolchain_path_prefix}bin/%{clang} ]]; then
execroot_path=""
elif [[ ${BASH_SOURCE[0]} == "/"* ]]; then
# Some consumers of `CcToolchainConfigInfo` (e.g. `cmake` from rules_foreign_cc)
@@ -47,8 +47,8 @@ fi

function sanitize_option() {
local -r opt=$1
if [[ ${opt} == */cc_wrapper.sh ]]; then
printf "%s" "${execroot_path}%{toolchain_path_prefix}bin/clang"
if [[ ${opt} == */cc_wrapper.sh ]] || [[ ${opt} == */cc_wrapper_msvc.sh ]]; then
printf "%s" "${execroot_path}%{toolchain_path_prefix}bin/%{clang}"
elif [[ ${opt} =~ ^-fsanitize-(ignore|black)list=[^/] ]]; then
# shellcheck disable=SC2206
parts=(${opt/=/ }) # Split flag name and value into array.
20 changes: 17 additions & 3 deletions toolchain/internal/common.bzl
Original file line number Diff line number Diff line change
@@ -12,19 +12,29 @@
# See the License for the specific language governing permissions and
# limitations under the License.

SUPPORTED_TARGETS = [("linux", "x86_64"), ("linux", "aarch64"), ("darwin", "x86_64"), ("darwin", "aarch64")]
SUPPORTED_TARGETS = [
("darwin", "aarch64"),
("darwin", "x86_64"),
("linux", "aarch64"),
("linux", "x86_64"),
("windows-msvc", "x86_64"),
]

# Map of tool name to its symlinked name in the tools directory.
# See tool_paths in toolchain/cc_toolchain_config.bzl.
_toolchain_tools = {
name: name
for name in [
"clang-cpp",
"clang-cl",
"ld.lld",
"ld64.lld",
"lld-link",
"llvm-ar",
"llvm-cov",
"llvm-dwp",
"llvm-lib",
"llvm-profdata",
"llvm-cov",
"llvm-nm",
"llvm-objcopy",
"llvm-objdump",
@@ -120,7 +130,11 @@ def os(rctx):

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

def arch(rctx):
arch = rctx.attr.exec_arch
13 changes: 13 additions & 0 deletions toolchain/internal/configure.bzl
Original file line number Diff line number Diff line change
@@ -219,6 +219,15 @@ def llvm_config_impl(rctx):
cc_wrapper_tpl,
{
"%{toolchain_path_prefix}": llvm_dist_path_prefix,
"%{clang}": "clang",
},
)
rctx.template(
"bin/cc_wrapper_msvc.sh",
rctx.attr._cc_wrapper_sh_tpl,
{
"%{toolchain_path_prefix}": llvm_dist_path_prefix,
"%{clang}": "clang-cl",
},
)

@@ -315,6 +324,7 @@ def _cc_toolchain_str(
"darwin-aarch64": "aarch64-apple-macosx",
"linux-aarch64": "aarch64-unknown-linux-gnu",
"linux-x86_64": "x86_64-unknown-linux-gnu",
"windows-msvc-x86_64": "x86_64-pc-windows-msvc",
}[target_pair]
cxx_builtin_include_directories = [
toolchain_path_prefix + "include/c++/v1",
@@ -341,6 +351,9 @@ def _cc_toolchain_str(
_join(sysroot_prefix, "/usr/include"),
_join(sysroot_prefix, "/System/Library/Frameworks"),
])
elif target_os == "windows-msvc":
# No additional include directories for MSVC.
pass
else:
fail("Unreachable")

1,490 changes: 1,490 additions & 0 deletions toolchain/windows_cc_toolchain_config.bzl

Large diffs are not rendered by default.