Skip to content

Commit f21487d

Browse files
authored
Merge pull request #133 from yanok/yanok/find-cc-toolchain-windows-macos
Use CC toolchain for linking
2 parents f93dca2 + 2335af2 commit f21487d

File tree

5 files changed

+151
-6
lines changed

5 files changed

+151
-6
lines changed

d/private/rules/BUILD.bazel

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,20 @@ bzl_library(
1515
deps = ["//d/private/rules:common"],
1616
)
1717

18+
bzl_library(
19+
name = "cc_toolchain",
20+
srcs = ["cc_toolchain.bzl"],
21+
visibility = [
22+
"//d:__subpackages__",
23+
"//docs:__subpackages__",
24+
],
25+
deps = [
26+
"@rules_cc//cc:action_names_bzl",
27+
"@rules_cc//cc:find_cc_toolchain_bzl",
28+
"@rules_cc//cc/common",
29+
],
30+
)
31+
1832
bzl_library(
1933
name = "common",
2034
srcs = ["common.bzl"],
@@ -24,6 +38,7 @@ bzl_library(
2438
],
2539
deps = [
2640
"//d/private:providers",
41+
"//d/private/rules:cc_toolchain",
2742
"@bazel_lib//lib:expand_make_vars",
2843
"@bazel_skylib//lib:dicts",
2944
"@bazel_skylib//lib:paths",
@@ -38,7 +53,10 @@ bzl_library(
3853
"//d:__subpackages__",
3954
"//docs:__subpackages__",
4055
],
41-
deps = ["//d/private/rules:common"],
56+
deps = [
57+
"//d/private/rules:common",
58+
"@rules_cc//cc:find_cc_toolchain_bzl",
59+
],
4260
)
4361

4462
bzl_library(
@@ -48,5 +66,8 @@ bzl_library(
4866
"//d:__subpackages__",
4967
"//docs:__subpackages__",
5068
],
51-
deps = ["//d/private/rules:common"],
69+
deps = [
70+
"//d/private/rules:common",
71+
"@rules_cc//cc:find_cc_toolchain_bzl",
72+
],
5273
)

d/private/rules/binary.bzl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""D test rule for compiling binaries."""
22

3+
load("@rules_cc//cc:find_cc_toolchain.bzl", "use_cc_toolchain")
34
load("//d/private/rules:common.bzl", "TARGET_TYPE", "compilation_action", "runnable_attrs")
45

56
def _d_binary_impl(ctx):
@@ -9,6 +10,7 @@ def _d_binary_impl(ctx):
910
d_binary = rule(
1011
implementation = _d_binary_impl,
1112
attrs = runnable_attrs,
12-
toolchains = ["//d:toolchain_type"],
13+
toolchains = ["//d:toolchain_type"] + use_cc_toolchain(),
14+
fragments = ["cpp"],
1315
executable = True,
1416
)

d/private/rules/cc_toolchain.bzl

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
"""
2+
Helper functions to extract the C++ toolchain and linker options for linking.
3+
"""
4+
5+
load("@rules_cc//cc:action_names.bzl", "CPP_LINK_EXECUTABLE_ACTION_NAME")
6+
load("@rules_cc//cc:find_cc_toolchain.bzl", "find_cc_toolchain")
7+
load("@rules_cc//cc/common:cc_common.bzl", "cc_common")
8+
9+
_UNSUPPORTED_FEATURES = [
10+
# These toolchain features require special rule support and will thus break
11+
# with D.
12+
# Taken from rules_go
13+
"thin_lto",
14+
"module_maps",
15+
"use_header_modules",
16+
"fdo_instrument",
17+
"fdo_optimize",
18+
# This is a nonspecific unsupported feature which allows the authors of C++
19+
# toolchain to apply separate flags when compiling D code.
20+
"rules_d_unsupported_feature",
21+
]
22+
23+
_LINKER_OPTIONS_DENYLIST = {
24+
# Don't link C++ libraries
25+
"-lstdc++": None,
26+
"-lc++": None,
27+
"-lc++abi": None,
28+
# libm and libobjc are added by the D compiler already, so suppress them here, to avoid warnings
29+
"-lm": None,
30+
"-lobjc": None,
31+
"-fobjc-link-runtime": None,
32+
# --target is passed by the D compiler
33+
"--target=": None,
34+
# --target passed by the D compiler conflicts with -mmacosx-version-min set by cc_toolchain
35+
"-mmacosx-version-min=": None,
36+
}
37+
38+
def _match_option(option, pattern):
39+
if pattern.endswith("="):
40+
return option.startswith(pattern)
41+
else:
42+
return option == pattern
43+
44+
def _filter_options(options, denylist):
45+
return [
46+
option
47+
for option in options
48+
if not any([_match_option(option, pattern) for pattern in denylist])
49+
]
50+
51+
def find_cc_toolchain_for_linking(ctx):
52+
"""
53+
Find the C++ toolchain and linker options for linking.
54+
55+
Args:
56+
ctx: The rule context
57+
Returns:
58+
A struct with the following fields:
59+
- cc_toolchain: The C++ toolchain
60+
- cc_compiler: The C/C++ compiler
61+
- cc_linking_options: The linker options
62+
- env: The environment variables to set for the linker
63+
"""
64+
cc_toolchain = find_cc_toolchain(ctx)
65+
feature_configuration = cc_common.configure_features(
66+
ctx = ctx,
67+
cc_toolchain = cc_toolchain,
68+
unsupported_features = _UNSUPPORTED_FEATURES,
69+
)
70+
linker_variables = cc_common.create_link_variables(
71+
feature_configuration = feature_configuration,
72+
cc_toolchain = cc_toolchain,
73+
is_linking_dynamic_library = False,
74+
)
75+
cc_compiler = cc_common.get_tool_for_action(
76+
feature_configuration = feature_configuration,
77+
action_name = CPP_LINK_EXECUTABLE_ACTION_NAME,
78+
)
79+
cc_linking_options = _filter_options(cc_common.get_memory_inefficient_command_line(
80+
feature_configuration = feature_configuration,
81+
action_name = CPP_LINK_EXECUTABLE_ACTION_NAME,
82+
variables = linker_variables,
83+
), _LINKER_OPTIONS_DENYLIST)
84+
env = cc_common.get_environment_variables(
85+
feature_configuration = feature_configuration,
86+
action_name = CPP_LINK_EXECUTABLE_ACTION_NAME,
87+
variables = linker_variables,
88+
)
89+
90+
return struct(
91+
cc_toolchain = cc_toolchain,
92+
cc_compiler = cc_compiler,
93+
cc_linking_options = cc_linking_options,
94+
env = env,
95+
)

d/private/rules/common.bzl

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ load("@bazel_skylib//lib:dicts.bzl", "dicts")
55
load("@bazel_skylib//lib:paths.bzl", "paths")
66
load("@rules_cc//cc/common:cc_info.bzl", "CcInfo")
77
load("//d/private:providers.bzl", "DInfo")
8+
load("//d/private/rules:cc_toolchain.bzl", "find_cc_toolchain_for_linking")
89

910
D_FILE_EXTENSIONS = [".d", ".di"]
1011

@@ -37,6 +38,10 @@ runnable_attrs = dicts.add(
3738
{
3839
"env": attr.string_dict(doc = "Environment variables for the binary at runtime. Subject of location and make variable expansion."),
3940
"data": attr.label_list(allow_files = True, doc = "List of files to be made available at runtime."),
41+
"_cc_toolchain": attr.label(
42+
default = "@rules_cc//cc:current_cc_toolchain",
43+
doc = "Default CC toolchain, used for linking. Remove after https://github.com/bazelbuild/bazel/issues/7260 is flipped (and support for old Bazel version is not needed)",
44+
),
4045
},
4146
)
4247

@@ -142,6 +147,8 @@ def compilation_action(ctx, target_type = TARGET_TYPE.LIBRARY):
142147
args.add_all(toolchain.linker_flags)
143148
args.add_all(linker_flags.to_list(), format_each = "-L=%s")
144149
output = None
150+
cc_toolchain = None
151+
env = ctx.var
145152
if target_type in [TARGET_TYPE.BINARY, TARGET_TYPE.TEST]:
146153
for dep in d_deps:
147154
args.add_all(dep.libraries)
@@ -150,6 +157,23 @@ def compilation_action(ctx, target_type = TARGET_TYPE.LIBRARY):
150157
args.add_all(["-main", "-unittest"])
151158
output = ctx.actions.declare_file(_binary_name(ctx, ctx.label.name))
152159
args.add(output, format = "-of=%s")
160+
cc_linker_info = find_cc_toolchain_for_linking(ctx)
161+
env = dict(cc_linker_info.env)
162+
env.update({
163+
"CC": cc_linker_info.cc_compiler, # Have to use the env variable here, since DMD doesn't support -gcc= flag
164+
# Ok, this is a bit weird. Local toolchain from rules_cc works fine if we don't set PATH here.
165+
# But doesn't work if we set it to an empty string.
166+
# OTOH the toolchain from toolchains_llvm doesn't work without setting PATH here. (Can't find the linker executable)
167+
# Even though the cc_wrapper script adds "/usr/bin" to the PATH variable,
168+
# it only works if the PATH is already in the environment. (I think they have to `export`)
169+
# So toolchains_llvm works if we set PATH to "" but doesn't work if we don't set it at all.
170+
# So, to get to a common ground, we set PATH to something generic.
171+
"PATH": "/bin:/usr/bin:/usr/local/bin",
172+
})
173+
if _get_os(ctx) != "windows":
174+
# DMD doesn't support -Xcc on Windows
175+
args.add_all(cc_linker_info.cc_linking_options, format_each = "-Xcc=%s")
176+
cc_toolchain = cc_linker_info.cc_toolchain
153177
elif target_type == TARGET_TYPE.LIBRARY:
154178
args.add("-lib")
155179
output = ctx.actions.declare_file(_static_library_name(ctx, ctx.label.name))
@@ -169,11 +193,12 @@ def compilation_action(ctx, target_type = TARGET_TYPE.LIBRARY):
169193

170194
ctx.actions.run(
171195
inputs = inputs,
196+
tools = [cc_toolchain.all_files] if cc_toolchain else [],
172197
outputs = [output],
173198
executable = toolchain.d_compiler[DefaultInfo].files_to_run,
174199
arguments = [args],
175-
env = ctx.var,
176-
use_default_shell_env = target_type != TARGET_TYPE.LIBRARY, # True to make the linker work properly
200+
env = env,
201+
use_default_shell_env = False,
177202
mnemonic = "Dcompile",
178203
progress_message = "Compiling D %s %s" % (target_type, ctx.label.name),
179204
)

d/private/rules/test.bzl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""D test rule for compiling and running D unit tests."""
22

3+
load("@rules_cc//cc:find_cc_toolchain.bzl", "use_cc_toolchain")
34
load("//d/private/rules:common.bzl", "TARGET_TYPE", "compilation_action", "runnable_attrs")
45

56
def _d_test_impl(ctx):
@@ -9,6 +10,7 @@ def _d_test_impl(ctx):
910
d_test = rule(
1011
implementation = _d_test_impl,
1112
attrs = runnable_attrs,
12-
toolchains = ["//d:toolchain_type"],
13+
toolchains = ["//d:toolchain_type"] + use_cc_toolchain(),
14+
fragments = ["cpp"],
1315
test = True,
1416
)

0 commit comments

Comments
 (0)