Skip to content

Commit 87017f9

Browse files
nglevinluispadron
authored andcommitted
Move all linking logic into rules_apple except for "apple_common.compliation_support.build_common_variables", removing a very buried reference to the Apple fragment for identifying platform paths in the process.
Cherry-pick: - 3a552c3 - 7760068 - f4a3fa4
1 parent 1beecf4 commit 87017f9

4 files changed

Lines changed: 385 additions & 9 deletions

File tree

apple/internal/BUILD

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,18 @@ bzl_library(
195195
],
196196
)
197197

198+
bzl_library(
199+
name = "compilation_support",
200+
srcs = ["compilation_support.bzl"],
201+
visibility = [
202+
"//apple:__subpackages__",
203+
],
204+
deps = [
205+
":platform_support",
206+
"@bazel_skylib//lib:paths",
207+
],
208+
)
209+
198210
bzl_library(
199211
name = "entitlements_support",
200212
srcs = ["entitlements_support.bzl"],
@@ -330,6 +342,7 @@ bzl_library(
330342
deps = [
331343
":apple_toolchains",
332344
":cc_toolchain_info_support",
345+
":compilation_support",
333346
":entitlements_support",
334347
":intermediates",
335348
":providers",
Lines changed: 361 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,361 @@
1+
# Copyright 2020 The Bazel Authors. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Utility methods used for creating objc_* rules actions"""
16+
17+
load(
18+
"@bazel_skylib//lib:paths.bzl",
19+
"paths",
20+
)
21+
load(
22+
"@rules_cc//cc/common:cc_common.bzl",
23+
"cc_common",
24+
)
25+
load(
26+
"//apple/internal:platform_support.bzl",
27+
"platform_support",
28+
)
29+
30+
visibility([
31+
"//apple/...",
32+
])
33+
34+
def _build_feature_configuration(common_variables):
35+
ctx = common_variables.ctx
36+
37+
enabled_features = []
38+
enabled_features.extend(ctx.features)
39+
enabled_features.extend(common_variables.extra_enabled_features)
40+
41+
disabled_features = []
42+
disabled_features.extend(ctx.disabled_features)
43+
disabled_features.extend(common_variables.extra_disabled_features)
44+
disabled_features.append("parse_headers")
45+
46+
return cc_common.configure_features(
47+
ctx = common_variables.ctx,
48+
cc_toolchain = common_variables.toolchain,
49+
language = "objc",
50+
requested_features = enabled_features,
51+
unsupported_features = disabled_features,
52+
)
53+
54+
def _build_fully_linked_variable_extensions(archive, libs):
55+
extensions = {}
56+
extensions["fully_linked_archive_path"] = archive.path
57+
extensions["objc_library_exec_paths"] = [lib.path for lib in libs]
58+
extensions["cc_library_exec_paths"] = []
59+
extensions["imported_library_exec_paths"] = []
60+
return extensions
61+
62+
def _get_library_for_linking(library_to_link):
63+
if library_to_link.static_library:
64+
return library_to_link.static_library
65+
elif library_to_link.pic_static_library:
66+
return library_to_link.pic_static_library
67+
elif library_to_link.interface_library:
68+
return library_to_link.interface_library
69+
else:
70+
return library_to_link.dynamic_library
71+
72+
def _libraries_from_linking_context(linking_context):
73+
libraries = []
74+
for linker_input in linking_context.linker_inputs.to_list():
75+
libraries.extend(linker_input.libraries)
76+
return depset(libraries, order = "topological")
77+
78+
def _get_libraries_for_linking(libraries_to_link):
79+
libraries = []
80+
for library_to_link in libraries_to_link:
81+
libraries.append(_get_library_for_linking(library_to_link))
82+
return libraries
83+
84+
def _register_fully_link_action(name, common_variables, cc_linking_context):
85+
ctx = common_variables.ctx
86+
feature_configuration = _build_feature_configuration(common_variables)
87+
88+
libraries_to_link = _libraries_from_linking_context(cc_linking_context).to_list()
89+
libraries = _get_libraries_for_linking(libraries_to_link)
90+
91+
output_archive = ctx.actions.declare_file(name + ".a")
92+
extensions = _build_fully_linked_variable_extensions(
93+
output_archive,
94+
libraries,
95+
)
96+
97+
return cc_common.link(
98+
name = name,
99+
actions = ctx.actions,
100+
feature_configuration = feature_configuration,
101+
cc_toolchain = common_variables.toolchain,
102+
language = "objc",
103+
additional_inputs = libraries,
104+
output_type = "archive",
105+
variables_extension = extensions,
106+
)
107+
108+
def _register_binary_strip_action(
109+
ctx,
110+
name,
111+
binary,
112+
feature_configuration,
113+
build_config,
114+
extra_link_args):
115+
"""
116+
Registers an action that uses the 'strip' tool to perform binary stripping on the given binary.
117+
"""
118+
119+
strip_safe = ctx.fragments.objc.strip_executable_safely
120+
121+
# For dylibs, loadable bundles, and kexts, must strip only local symbols.
122+
link_dylib = cc_common.is_enabled(
123+
feature_configuration = feature_configuration,
124+
feature_name = "link_dylib",
125+
)
126+
link_bundle = cc_common.is_enabled(
127+
feature_configuration = feature_configuration,
128+
feature_name = "link_bundle",
129+
)
130+
if ("-dynamiclib" in extra_link_args or link_dylib or
131+
"-bundle" in extra_link_args or link_bundle or "-kext" in extra_link_args):
132+
strip_safe = True
133+
134+
# TODO(b/331163513): Use intermediates.file() instead of declare_shareable_artifact().
135+
stripped_binary = ctx.actions.declare_shareable_artifact(
136+
paths.join(ctx.label.package, name),
137+
build_config.bin_dir,
138+
)
139+
args = ctx.actions.args()
140+
args.add("strip")
141+
if strip_safe:
142+
args.add("-x")
143+
args.add("-o", stripped_binary)
144+
args.add(binary)
145+
xcode_config = ctx.attr._xcode_config[apple_common.XcodeVersionConfig]
146+
apple_common_platform = platform_support.apple_common_platform_from_platform_info(
147+
apple_platform_info = platform_support.apple_platform_info_from_rule_ctx(ctx),
148+
)
149+
150+
ctx.actions.run(
151+
mnemonic = "ObjcBinarySymbolStrip",
152+
executable = "/usr/bin/xcrun",
153+
arguments = [args],
154+
inputs = [binary],
155+
outputs = [stripped_binary],
156+
execution_requirements = xcode_config.execution_info(),
157+
env = apple_common.apple_host_system_env(xcode_config) |
158+
apple_common.target_apple_env(xcode_config, apple_common_platform),
159+
)
160+
return stripped_binary
161+
162+
def _emit_builtin_objc_strip_action(ctx):
163+
return (
164+
ctx.fragments.objc.builtin_objc_strip_action and
165+
ctx.fragments.cpp.objc_enable_binary_stripping() and
166+
ctx.fragments.cpp.compilation_mode() == "opt"
167+
)
168+
169+
def _register_configuration_specific_link_actions(
170+
name,
171+
common_variables,
172+
cc_linking_context,
173+
build_config,
174+
extra_link_args,
175+
stamp,
176+
user_variable_extensions,
177+
additional_outputs,
178+
extra_link_inputs,
179+
attr_linkopts):
180+
"""
181+
Registers actions to link a single-platform/architecture Apple binary in a specific config.
182+
183+
Registers any actions necessary to link this rule and its dependencies. Automatically infers
184+
the toolchain from the configuration.
185+
186+
Returns:
187+
(File) the linked binary
188+
"""
189+
ctx = common_variables.ctx
190+
feature_configuration = _build_feature_configuration(common_variables)
191+
192+
# When compilation_mode=opt and objc_enable_binary_stripping are specified, the unstripped
193+
# binary containing debug symbols is generated by the linker, which also needs the debug
194+
# symbols for dead-code removal. The binary is also used to generate dSYM bundle if
195+
# --apple_generate_dsym is specified. A symbol strip action is later registered to strip
196+
# the symbol table from the unstripped binary.
197+
if _emit_builtin_objc_strip_action(ctx):
198+
# TODO(b/331163513): Use intermediates.file() instead of declare_shareable_artifact().
199+
binary = ctx.actions.declare_shareable_artifact(
200+
paths.join(ctx.label.package, name + "_unstripped"),
201+
build_config.bin_dir,
202+
)
203+
else:
204+
# TODO(b/331163513): Use intermediates.file() instead of declare_shareable_artifact().
205+
binary = ctx.actions.declare_shareable_artifact(
206+
paths.join(ctx.label.package, name),
207+
build_config.bin_dir,
208+
)
209+
210+
return _register_configuration_specific_link_actions_with_cpp_variables(
211+
name,
212+
binary,
213+
common_variables,
214+
feature_configuration,
215+
cc_linking_context,
216+
build_config,
217+
extra_link_args,
218+
stamp,
219+
user_variable_extensions,
220+
additional_outputs,
221+
extra_link_inputs,
222+
attr_linkopts,
223+
)
224+
225+
def _register_configuration_specific_link_actions_with_cpp_variables(
226+
name,
227+
binary,
228+
common_variables,
229+
feature_configuration,
230+
cc_linking_context,
231+
build_config,
232+
extra_link_args,
233+
stamp,
234+
user_variable_extensions,
235+
additional_outputs,
236+
extra_link_inputs,
237+
attr_linkopts):
238+
ctx = common_variables.ctx
239+
240+
prefixed_attr_linkopts = [
241+
"-Wl,%s" % linkopt
242+
for linkopt in attr_linkopts
243+
]
244+
245+
seen_flags = {}
246+
(_, user_link_flags, seen_flags) = _dedup_link_flags(
247+
extra_link_args + prefixed_attr_linkopts,
248+
seen_flags,
249+
)
250+
(cc_linking_context, _) = _create_deduped_linkopts_linking_context(
251+
ctx.label,
252+
cc_linking_context,
253+
seen_flags,
254+
)
255+
256+
cc_common.link(
257+
name = name,
258+
actions = ctx.actions,
259+
additional_inputs = (
260+
extra_link_inputs +
261+
getattr(ctx.files, "additional_linker_inputs", [])
262+
),
263+
additional_outputs = additional_outputs,
264+
build_config = build_config,
265+
cc_toolchain = common_variables.toolchain,
266+
feature_configuration = feature_configuration,
267+
language = "objc",
268+
linking_contexts = [cc_linking_context],
269+
main_output = binary,
270+
output_type = "executable",
271+
stamp = stamp,
272+
user_link_flags = user_link_flags,
273+
variables_extension = user_variable_extensions,
274+
)
275+
276+
if _emit_builtin_objc_strip_action(ctx):
277+
return _register_binary_strip_action(
278+
ctx,
279+
name,
280+
binary,
281+
feature_configuration,
282+
build_config,
283+
extra_link_args,
284+
)
285+
else:
286+
return binary
287+
288+
def _dedup_link_flags(flags, seen_flags = {}):
289+
new_flags = []
290+
previous_arg = None
291+
for arg in flags:
292+
if previous_arg in ["-framework", "-weak_framework"]:
293+
framework = arg
294+
key = previous_arg[1] + framework
295+
if key not in seen_flags:
296+
new_flags.extend([previous_arg, framework])
297+
seen_flags[key] = True
298+
previous_arg = None
299+
elif arg in ["-framework", "-weak_framework"]:
300+
previous_arg = arg
301+
elif arg.startswith("-Wl,-framework,") or arg.startswith("-Wl,-weak_framework,"):
302+
framework = arg.split(",")[2]
303+
key = arg[5] + framework
304+
if key not in seen_flags:
305+
new_flags.extend([arg.split(",")[1], framework])
306+
seen_flags[key] = True
307+
elif arg.startswith("-Wl,-rpath,"):
308+
rpath = arg.split(",")[2]
309+
key = arg[5] + rpath
310+
if key not in seen_flags:
311+
new_flags.append(arg)
312+
seen_flags[key] = True
313+
elif arg.startswith("-l"):
314+
if arg not in seen_flags:
315+
new_flags.append(arg)
316+
seen_flags[arg] = True
317+
else:
318+
new_flags.append(arg)
319+
320+
same = (
321+
len(flags) == len(new_flags) and
322+
all([flags[i] == new_flags[i] for i in range(0, len(flags))])
323+
)
324+
325+
return (same, new_flags, seen_flags)
326+
327+
def _create_deduped_linkopts_linking_context(owner, cc_linking_context, seen_flags):
328+
linker_inputs = []
329+
for linker_input in cc_linking_context.linker_inputs.to_list():
330+
(same, new_flags, seen_flags) = _dedup_link_flags(
331+
linker_input.user_link_flags,
332+
seen_flags,
333+
)
334+
if same:
335+
linker_inputs.append(linker_input)
336+
else:
337+
linker_inputs.append(cc_common.create_linker_input(
338+
owner = linker_input.owner,
339+
libraries = depset(linker_input.libraries),
340+
user_link_flags = new_flags,
341+
additional_inputs = depset(linker_input.additional_inputs),
342+
))
343+
344+
# Why does linker_input not expose linkstamp? This needs to be fixed.
345+
linker_inputs.append(cc_common.create_linker_input(
346+
owner = owner,
347+
linkstamps = cc_linking_context.linkstamps(),
348+
))
349+
350+
return (
351+
cc_common.create_linking_context(
352+
linker_inputs = depset(linker_inputs),
353+
),
354+
seen_flags,
355+
)
356+
357+
compilation_support = struct(
358+
# TODO(b/331163513): Move apple_common.compliation_support.build_common_variables here, too.
359+
register_fully_link_action = _register_fully_link_action,
360+
register_configuration_specific_link_actions = _register_configuration_specific_link_actions,
361+
)

0 commit comments

Comments
 (0)