blob: a9a142a19a9e373bc19cfa913b2a82043b1912e3 [file] [log] [blame]
# Copyright 2020 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.
"""Utility methods used for creating objc_* rules actions"""
load(":common/cc/cc_common.bzl", "cc_common")
load(":common/cc/cc_helper.bzl", "cc_helper")
load(":common/cc/cc_info.bzl", "CcInfo")
load(":common/objc/apple_env.bzl", "apple_host_system_env", "target_apple_env")
load(":common/objc/objc_common.bzl", "objc_common")
load(":common/objc/providers.bzl", "J2ObjcEntryClassInfo", "J2ObjcMappingFileInfo")
load(":common/xcode/providers.bzl", "XcodeVersionInfo")
objc_internal = _builtins.internal.objc_internal
def _build_variable_extensions(ctx, arc_enabled):
extensions = {}
if hasattr(ctx.attr, "pch") and ctx.attr.pch != None:
extensions["pch_file"] = ctx.file.pch.path
extensions["modules_cache_path"] = ctx.genfiles_dir.path + "/" + "_objc_module_cache"
if arc_enabled:
extensions["objc_arc"] = ""
else:
extensions["no_objc_arc"] = ""
return extensions
def _build_common_variables(
ctx,
toolchain,
use_pch = False,
empty_compilation_artifacts = False,
deps = [],
implementation_deps = [],
extra_disabled_features = [],
extra_enabled_features = [],
attr_linkopts = [],
alwayslink = False,
has_module_map = False,
direct_cc_compilation_contexts = []):
compilation_attributes = objc_internal.create_compilation_attributes(ctx = ctx)
intermediate_artifacts = objc_internal.create_intermediate_artifacts(ctx = ctx)
if empty_compilation_artifacts:
compilation_artifacts = objc_internal.create_compilation_artifacts()
else:
compilation_artifacts = objc_internal.create_compilation_artifacts(ctx = ctx)
(
objc_provider,
objc_compilation_context,
objc_linking_context,
) = objc_common.create_context_and_provider(
ctx = ctx,
compilation_attributes = compilation_attributes,
compilation_artifacts = compilation_artifacts,
deps = deps,
implementation_deps = implementation_deps,
intermediate_artifacts = intermediate_artifacts,
has_module_map = has_module_map,
attr_linkopts = attr_linkopts,
direct_cc_compilation_contexts = direct_cc_compilation_contexts,
includes = cc_helper.system_include_dirs(ctx, {}) if hasattr(ctx.attr, "includes") else [],
)
return struct(
ctx = ctx,
intermediate_artifacts = intermediate_artifacts,
compilation_attributes = compilation_attributes,
compilation_artifacts = compilation_artifacts,
extra_disabled_features = extra_disabled_features,
extra_enabled_features = extra_enabled_features,
objc_compilation_context = objc_compilation_context,
objc_linking_context = objc_linking_context,
toolchain = toolchain,
alwayslink = alwayslink,
use_pch = use_pch,
objc_config = ctx.fragments.objc,
objc_provider = objc_provider,
)
def _build_feature_configuration(common_variables, for_swift_module_map, support_parse_headers):
ctx = common_variables.ctx
enabled_features = []
enabled_features.extend(ctx.features)
enabled_features.extend(common_variables.extra_enabled_features)
disabled_features = []
disabled_features.extend(ctx.disabled_features)
disabled_features.extend(common_variables.extra_disabled_features)
if not support_parse_headers:
disabled_features.append("parse_headers")
if for_swift_module_map:
enabled_features.append("module_maps")
enabled_features.append("compile_all_modules")
enabled_features.append("only_doth_headers_in_module_maps")
enabled_features.append("exclude_private_headers_in_module_maps")
enabled_features.append("module_map_without_extern_module")
disabled_features.append("generate_submodules")
return cc_common.configure_features(
ctx = common_variables.ctx,
cc_toolchain = common_variables.toolchain,
language = "objc",
requested_features = enabled_features,
unsupported_features = disabled_features,
)
def _compile(
common_variables,
feature_configuration,
extension,
extra_compile_args,
priority_headers,
srcs,
private_hdrs,
public_hdrs,
pch_hdr,
module_map,
purpose,
generate_module_map):
objc_compilation_context = common_variables.objc_compilation_context
user_compile_flags = []
user_compile_flags.extend(_get_compile_rule_copts(common_variables))
user_compile_flags.extend(common_variables.objc_config.copts_for_current_compilation_mode)
user_compile_flags.extend(extra_compile_args)
user_compile_flags.extend(
_paths_to_include_args(objc_compilation_context.strict_dependency_includes),
)
textual_hdrs = []
textual_hdrs.extend(objc_compilation_context.public_textual_hdrs)
if pch_hdr != None:
textual_hdrs.append(pch_hdr)
return cc_common.compile(
actions = common_variables.ctx.actions,
feature_configuration = feature_configuration,
cc_toolchain = common_variables.toolchain,
name = common_variables.ctx.label.name,
srcs = srcs,
public_hdrs = public_hdrs,
private_hdrs = private_hdrs,
textual_hdrs = textual_hdrs,
defines = objc_compilation_context.defines,
includes = objc_compilation_context.includes,
system_includes = objc_compilation_context.system_includes,
quote_includes = objc_compilation_context.quote_includes,
compilation_contexts = objc_compilation_context.cc_compilation_contexts,
implementation_compilation_contexts = objc_compilation_context.implementation_cc_compilation_contexts,
user_compile_flags = user_compile_flags,
module_map = module_map,
propagate_module_map_to_compile_action = True,
variables_extension = extension,
language = "objc",
code_coverage_enabled = cc_helper.is_code_coverage_enabled(ctx = common_variables.ctx),
hdrs_checking_mode = "strict",
do_not_generate_module_map = not generate_module_map or module_map.file().is_source,
purpose = purpose,
)
def _validate_attributes(common_variables):
for include in common_variables.compilation_attributes.includes.to_list():
if include.startswith("/"):
cc_helper.rule_error(
"The path '{}' is absolute, but only relative paths are allowed.".format(include),
)
ctx = common_variables.ctx
if hasattr(ctx.attr, "srcs"):
srcs = {}
for src in ctx.files.srcs:
srcs[src.path] = True
for src in ctx.files.non_arc_srcs:
if src.path in srcs:
cc_helper.attribute_error(
"srcs",
"File '{}' is present in both srcs and non_arc_srcs which is forbidden.".format(src.path),
)
if hasattr(ctx.attr, "module_name") and hasattr(ctx.attr, "module_map"):
if ctx.attr.module_name != "" and ctx.attr.module_map != None:
cc_helper.attribute_error(
"module_name",
"Specifying both module_name and module_map is invalid, please remove one of them.",
)
def _get_compile_rule_copts(common_variables):
attributes = common_variables.compilation_attributes
copts = []
copts.extend(attributes.copts)
if attributes.enable_modules and common_variables.ctx.attr.module_map == None:
copts.append("-fmodules")
if "-fmodules" in copts:
cache_path = common_variables.ctx.genfiles_dir.path + "/" + "_objc_module_cache"
copts.append("-fmodules-cache-path=" + cache_path)
return copts
def _paths_to_include_args(paths):
new_paths = []
for path in paths:
new_paths.append("-I" + path)
return new_paths
# TODO(bazel-team): This method can be deleted as soon as the native j2objc
# rules are deleted. The native rules are deprecated and will be replaced by
# better Starlark rules that are not a literal translation of the native
# implementation and use a better approach. This is not done by the Bazel team
# but a separate team (tball@). This method is added so that common utility code
# in CompilationSupport can be deleted from Java.
def _register_compile_and_archive_actions_for_j2objc(
ctx,
toolchain,
intermediate_artifacts,
compilation_artifacts,
objc_compilation_context,
cc_linking_contexts,
extra_compile_args):
compilation_attributes = objc_internal.create_compilation_attributes(ctx = ctx)
objc_linking_context = struct(
cc_linking_contexts = cc_linking_contexts,
linkopts = [],
)
common_variables = struct(
ctx = ctx,
intermediate_artifacts = intermediate_artifacts,
compilation_attributes = compilation_attributes,
compilation_artifacts = compilation_artifacts,
extra_enabled_features = ["j2objc_transpiled"],
extra_disabled_features = ["layering_check", "parse_headers"],
objc_compilation_context = objc_compilation_context,
objc_linking_context = objc_linking_context,
toolchain = toolchain,
alwayslink = True,
use_pch = False,
objc_config = ctx.fragments.objc,
objc_provider = None,
)
return _cc_compile_and_link(
compilation_artifacts.srcs,
compilation_artifacts.non_arc_srcs,
[],
compilation_artifacts.additional_hdrs,
common_variables,
extra_compile_args,
priority_headers = [],
generate_module_map_for_swift = True,
)
def _register_compile_and_archive_actions(
common_variables,
extra_compile_args = [],
priority_headers = [],
generate_module_map_for_swift = False):
ctx = common_variables.ctx
return _cc_compile_and_link(
cc_helper.get_srcs(ctx),
_get_non_arc_srcs(ctx),
cc_helper.get_private_hdrs(ctx),
cc_helper.get_public_hdrs(ctx),
common_variables,
extra_compile_args,
priority_headers,
generate_module_map_for_swift = generate_module_map_for_swift,
)
# Returns a list of (Artifact, Label) tuples. Each tuple represents an input source
# file and the label of the rule that generates it (or the label of the source file itself if it
# is an input file).
def _get_non_arc_srcs(ctx):
if not hasattr(ctx.attr, "non_arc_srcs"):
return []
artifact_label_map = {}
for src in ctx.attr.non_arc_srcs:
if DefaultInfo in src:
for artifact in src[DefaultInfo].files.to_list():
artifact_label_map[artifact] = src.label
return _map_to_list(artifact_label_map)
def _map_to_list(m):
result = []
for k, v in m.items():
result.append((k, v))
return result
def _cc_compile_and_link(
srcs,
non_arc_srcs,
private_hdrs,
public_hdrs,
common_variables,
extra_compile_args,
priority_headers,
generate_module_map_for_swift):
intermediate_artifacts = common_variables.intermediate_artifacts
compilation_attributes = common_variables.compilation_attributes
ctx = common_variables.ctx
(objects, pic_objects) = _get_object_files(common_variables.ctx)
pch_header = _get_pch_file(common_variables)
feature_configuration = _build_feature_configuration(
common_variables,
for_swift_module_map = False,
support_parse_headers = True,
)
generate_module_map = cc_common.is_enabled(
feature_configuration = feature_configuration,
feature_name = "module_maps",
)
module_map = None
if generate_module_map:
module_map = intermediate_artifacts.internal_module_map
purpose = "{}_objc_arc".format(_get_purpose(common_variables))
arc_primary_module_map_fc = feature_configuration
arc_extensions = _build_variable_extensions(ctx, arc_enabled = True)
(arc_compilation_context, arc_compilation_outputs) = _compile(
common_variables,
arc_primary_module_map_fc,
arc_extensions,
extra_compile_args,
priority_headers,
srcs,
private_hdrs,
public_hdrs,
pch_header,
module_map,
purpose,
generate_module_map,
)
purpose = "{}_non_objc_arc".format(_get_purpose(common_variables))
non_arc_primary_module_map_fc = _build_feature_configuration(
common_variables,
for_swift_module_map = False,
support_parse_headers = False,
)
non_arc_extensions = _build_variable_extensions(ctx, arc_enabled = False)
(non_arc_compilation_context, non_arc_compilation_outputs) = _compile(
common_variables,
non_arc_primary_module_map_fc,
non_arc_extensions,
extra_compile_args,
priority_headers,
non_arc_srcs,
private_hdrs,
public_hdrs,
pch_header,
module_map,
purpose,
generate_module_map = False,
)
objc_compilation_context = common_variables.objc_compilation_context
if generate_module_map_for_swift:
_generate_extra_module_map(
common_variables,
intermediate_artifacts.swift_module_map,
public_hdrs,
private_hdrs,
objc_compilation_context.public_textual_hdrs,
pch_header,
objc_compilation_context.cc_compilation_contexts,
_build_feature_configuration(
common_variables,
for_swift_module_map = True,
support_parse_headers = False,
),
)
compilation_context = cc_common.merge_compilation_contexts(
compilation_contexts = [arc_compilation_context, non_arc_compilation_context],
)
precompiled_compilation_outputs = cc_common.create_compilation_outputs(
pic_objects = depset(pic_objects),
objects = depset(objects),
)
compilation_outputs = cc_common.merge_compilation_outputs(
compilation_outputs = [
precompiled_compilation_outputs,
arc_compilation_outputs,
non_arc_compilation_outputs,
],
)
objc_linking_context = common_variables.objc_linking_context
if len(compilation_outputs.objects) != 0 or len(compilation_outputs.pic_objects) != 0:
(linking_context, _) = cc_common.create_linking_context_from_compilation_outputs(
actions = ctx.actions,
feature_configuration = feature_configuration,
cc_toolchain = common_variables.toolchain,
compilation_outputs = compilation_outputs,
user_link_flags = objc_linking_context.linkopts,
linking_contexts = objc_linking_context.cc_linking_contexts,
name = common_variables.ctx.label.name + intermediate_artifacts.archive_file_name_suffix,
language = "c++",
alwayslink = common_variables.alwayslink,
disallow_dynamic_library = True,
variables_extension = non_arc_extensions,
)
else:
linker_input = cc_common.create_linker_input(
owner = ctx.label,
user_link_flags = objc_linking_context.linkopts,
)
cc_linking_context = cc_common.create_linking_context(
linker_inputs = depset(direct = [linker_input]),
)
linking_context = cc_common.merge_linking_contexts(
linking_contexts = [cc_linking_context] + objc_linking_context.cc_linking_contexts,
)
arc_output_groups = cc_helper.build_output_groups_for_emitting_compile_providers(
arc_compilation_outputs,
arc_compilation_context,
ctx.fragments.cpp,
common_variables.toolchain,
feature_configuration,
ctx,
generate_hidden_top_level_group = True,
)
non_arc_output_groups = cc_helper.build_output_groups_for_emitting_compile_providers(
non_arc_compilation_outputs,
non_arc_compilation_context,
ctx.fragments.cpp,
common_variables.toolchain,
feature_configuration,
ctx,
generate_hidden_top_level_group = True,
)
merged_output_groups = cc_helper.merge_output_groups(
[arc_output_groups, non_arc_output_groups],
)
return (compilation_context, linking_context, compilation_outputs, merged_output_groups)
def _get_object_files(ctx):
if not hasattr(ctx.attr, "srcs"):
return ([], [])
pic_objects = []
for src in ctx.files.srcs:
path = src.path
if path.endswith(".pic.o") or path.endswith(".o") and not path.endswith(".nopic.o"):
pic_objects.append(src)
objects = []
for src in ctx.files.srcs:
path = src.path
if path.endswith(".o") and not path.endswith(".pic.o"):
objects.append(src)
return (objects, pic_objects)
def _get_pch_file(common_variables):
if not common_variables.use_pch:
return None
pch_hdr = None
if hasattr(common_variables.ctx.attr, "pch"):
pch_hdr = common_variables.ctx.file.pch
return pch_hdr
def _get_purpose(common_variables):
suffix = common_variables.intermediate_artifacts.archive_file_name_suffix
config = common_variables.ctx.bin_dir.path.split("/")[1]
return "Objc_build_arch_" + config + "_with_suffix_" + suffix
def _generate_extra_module_map(
common_variables,
module_map,
public_hdrs,
private_hdrs,
textual_hdrs,
pch_header,
compilation_contexts,
feature_configuration):
purpose = "{}_extra_module_map".format(_get_purpose(common_variables))
all_textual_hdrs = []
all_textual_hdrs.extend(textual_hdrs)
if pch_header != None:
all_textual_hdrs.append(pch_header)
cc_common.compile(
actions = common_variables.ctx.actions,
feature_configuration = feature_configuration,
cc_toolchain = common_variables.toolchain,
public_hdrs = public_hdrs,
textual_hdrs = textual_hdrs,
private_hdrs = private_hdrs,
compilation_contexts = compilation_contexts,
module_map = module_map,
purpose = purpose,
name = common_variables.ctx.label.name,
)
def _build_fully_linked_variable_extensions(archive, libs):
extensions = {}
extensions["fully_linked_archive_path"] = archive.path
extensions["objc_library_exec_paths"] = [lib.path for lib in libs]
extensions["cc_library_exec_paths"] = []
extensions["imported_library_exec_paths"] = []
return extensions
def _get_libraries_for_linking(libraries_to_link):
libraries = []
for library_to_link in libraries_to_link:
if library_to_link.static_library:
libraries.append(library_to_link.static_library)
elif library_to_link.pic_static_library:
libraries.append(library_to_link.pic_static_library)
elif library_to_link.interface_library:
libraries.append(library_to_link.interface_library)
else:
libraries.append(library_to_link.dynamic_library)
return libraries
def _register_fully_link_action(name, common_variables, cc_linking_context):
ctx = common_variables.ctx
feature_configuration = _build_feature_configuration(common_variables, False, False)
libraries_to_link = cc_helper.libraries_from_linking_context(cc_linking_context).to_list()
libraries = _get_libraries_for_linking(libraries_to_link)
output_archive = ctx.actions.declare_file(name + ".a")
extensions = _build_fully_linked_variable_extensions(
output_archive,
libraries,
)
return cc_common.link(
name = name,
actions = ctx.actions,
feature_configuration = feature_configuration,
cc_toolchain = common_variables.toolchain,
language = "objc",
additional_inputs = libraries,
output_type = "archive",
variables_extension = extensions,
)
def _register_j2objc_dead_code_removal_actions(common_variables, deps, build_config):
"""Registers actions to perform J2Objc dead code removal (when enabled)."""
ctx = common_variables.ctx
j2objc_entry_class_info = objc_common.j2objc_entry_class_info_union(
[dep[J2ObjcEntryClassInfo] for dep in deps if J2ObjcEntryClassInfo in dep],
)
entry_classes = j2objc_entry_class_info.entry_classes
# Only perform J2ObjC dead code stripping if flag --j2objc_dead_code_removal is specified and
# users have specified entry classes.
if (not ctx.fragments.j2objc.remove_dead_code() or not entry_classes or
not common_variables.objc_provider.j2objc_library.to_list()):
return {}
j2objc_mapping_file_info = objc_common.j2objc_mapping_file_info_union(
[dep[J2ObjcMappingFileInfo] for dep in deps if J2ObjcMappingFileInfo in dep],
)
j2objc_dependency_mapping_files = j2objc_mapping_file_info.dependency_mapping_files
j2objc_header_mapping_files = j2objc_mapping_file_info.header_mapping_files
j2objc_archive_source_mapping_files = j2objc_mapping_file_info.archive_source_mapping_files
replace_libs = {}
for j2objc_archive in common_variables.objc_provider.j2objc_library.to_list():
pruned_j2objc_archive = ctx.actions.declare_shareable_artifact(
ctx.label.package + "/_j2objc_pruned/" + ctx.label.name + "/" +
j2objc_archive.short_path[:-len(j2objc_archive.extension)].strip(".") +
"_pruned." + j2objc_archive.extension,
build_config.bin_dir,
)
replace_libs[j2objc_archive] = pruned_j2objc_archive
# Although _dummy_lib is always a label, weirdly when it has a cfg attached to it
# Bazel makes it a list (even if the cfg is not split)
_dummy_lib = ctx.attr._dummy_lib if type(ctx.attr._dummy_lib) == "Target" else ctx.attr._dummy_lib[0]
[dummy_archive] = _get_libraries_for_linking(cc_helper.libraries_from_linking_context(
_dummy_lib[CcInfo].linking_context,
).to_list())
args = ctx.actions.args()
args.add("--input_archive", j2objc_archive)
args.add("--output_archive", pruned_j2objc_archive)
args.add("--dummy_archive", dummy_archive)
args.add_joined(
"--dependency_mapping_files",
j2objc_dependency_mapping_files,
join_with = ",",
)
args.add_joined("--header_mapping_files", j2objc_header_mapping_files, join_with = ",")
args.add_joined(
"--archive_source_mapping_files",
j2objc_archive_source_mapping_files,
join_with = ",",
)
args.add("--entry_classes")
args.add_joined(entry_classes, join_with = ",")
args.set_param_file_format("multiline")
args.use_param_file("@%s", use_always = True)
ctx.actions.run(
mnemonic = "DummyPruner",
executable = ctx.executable._j2objc_dead_code_pruner,
inputs = depset(
[j2objc_archive, dummy_archive],
transitive = [
j2objc_dependency_mapping_files,
j2objc_header_mapping_files,
j2objc_archive_source_mapping_files,
],
),
arguments = [args],
outputs = [pruned_j2objc_archive],
exec_group = "j2objc",
)
return replace_libs
def _register_obj_filelist_action(ctx, build_config, obj_files):
"""
Returns a File containing the given set of object files.
This File is suitable to signal symbols to archive in a libtool archiving invocation.
"""
obj_list = ctx.actions.declare_shareable_artifact(
ctx.label.package + "/" + ctx.label.name + "-linker.objlist",
build_config.bin_dir,
)
args = ctx.actions.args()
args.add_all(obj_files)
args.set_param_file_format("multiline")
ctx.actions.write(obj_list, args)
return obj_list
def _register_binary_strip_action(
ctx,
name,
binary,
feature_configuration,
build_config,
extra_link_args):
"""
Registers an action that uses the 'strip' tool to perform binary stripping on the given binary.
"""
strip_safe = ctx.fragments.objc.strip_executable_safely
# For dylibs, loadable bundles, and kexts, must strip only local symbols.
link_dylib = cc_common.is_enabled(
feature_configuration = feature_configuration,
feature_name = "link_dylib",
)
link_bundle = cc_common.is_enabled(
feature_configuration = feature_configuration,
feature_name = "link_bundle",
)
if ("-dynamiclib" in extra_link_args or link_dylib or
"-bundle" in extra_link_args or link_bundle or "-kext" in extra_link_args):
strip_safe = True
stripped_binary = ctx.actions.declare_shareable_artifact(
ctx.label.package + "/" + name,
build_config.bin_dir,
)
args = ctx.actions.args()
args.add("strip")
if strip_safe:
args.add("-x")
args.add("-o", stripped_binary)
args.add(binary)
xcode_config = ctx.attr._xcode_config[XcodeVersionInfo]
platform = _builtins.internal.objc_internal.get_target_platform(build_config = build_config)
ctx.actions.run(
mnemonic = "ObjcBinarySymbolStrip",
executable = "/usr/bin/xcrun",
arguments = [args],
inputs = [binary],
outputs = [stripped_binary],
execution_requirements = ctx.attr._xcode_config[XcodeVersionInfo].execution_info(),
env = apple_host_system_env(xcode_config) |
target_apple_env(xcode_config, platform),
)
return stripped_binary
def _dedup_sdk_linkopts(linker_inputs):
duplicates = {}
final_linkopts = []
for linker_input in linker_inputs.to_list():
flags = linker_input.user_link_flags
previous_arg = None
for arg in flags:
if previous_arg in ["-framework", "-weak_framework"]:
framework = arg
key = previous_arg[1] + framework
if key not in duplicates:
final_linkopts.extend([previous_arg, framework])
duplicates[key] = None
previous_arg = None
elif arg in ["-framework", "-weak_framework"]:
previous_arg = arg
elif arg.startswith("-Wl,-framework,") or arg.startswith("-Wl,-weak_framework,"):
framework = arg.split(",")[2]
key = arg[5] + framework
if key not in duplicates:
final_linkopts.extend([arg.split(",")[1], framework])
duplicates[key] = None
elif arg.startswith("-l"):
if arg not in duplicates:
final_linkopts.append(arg)
duplicates[arg] = None
else:
final_linkopts.append(arg)
return final_linkopts
def _linkstamp_map(ctx, linkstamps, output, build_config):
# create linkstamps_map - mapping from linkstamps to object files
linkstamps_map = {}
stamp_output_dir = ctx.label.package + "/_objs/" + output.basename + "/"
for linkstamp in linkstamps.to_list():
linkstamp_file = linkstamp.file()
stamp_output_path = (
stamp_output_dir +
linkstamp_file.short_path[:-len(linkstamp_file.extension)].rstrip(".") + ".o"
)
stamp_output_file = ctx.actions.declare_shareable_artifact(
stamp_output_path,
build_config.bin_dir,
)
linkstamps_map[linkstamp_file] = stamp_output_file
return linkstamps_map
def _classify_libraries(libraries_to_link):
always_link_libraries = {
lib: None
for lib in _get_libraries_for_linking(
[lib for lib in libraries_to_link if lib.alwayslink],
)
}
as_needed_libraries = {
lib: None
for lib in _get_libraries_for_linking(
[lib for lib in libraries_to_link if not lib.alwayslink],
)
if lib not in always_link_libraries
}
return always_link_libraries.keys(), as_needed_libraries.keys()
def _register_configuration_specific_link_actions(
name,
common_variables,
cc_linking_context,
build_config,
extra_link_args,
stamp,
user_variable_extensions,
additional_outputs,
deps,
extra_link_inputs,
attr_linkopts):
"""
Registers actions to link a single-platform/architecture Apple binary in a specific config.
Registers any actions necessary to link this rule and its dependencies. Automatically infers
the toolchain from the configuration.
Returns:
(File) the linked binary
"""
ctx = common_variables.ctx
feature_configuration = _build_feature_configuration(common_variables, False, False)
# We need to split input libraries into those that require -force_load and those that don't.
# Clang loads archives specified in filelists and also specified as -force_load twice,
# resulting in duplicate symbol errors unless they are deduped.
libraries_to_link = cc_helper.libraries_from_linking_context(cc_linking_context).to_list()
always_link_libraries, as_needed_libraries = _classify_libraries(libraries_to_link)
replace_libs = _register_j2objc_dead_code_removal_actions(common_variables, deps, build_config)
# Substitutes both sets of unpruned J2ObjC libraries with pruned ones
always_link_libraries = [replace_libs.get(lib, lib) for lib in always_link_libraries]
as_needed_libraries = [replace_libs.get(lib, lib) for lib in as_needed_libraries]
static_runtimes = common_variables.toolchain.static_runtime_lib(
feature_configuration = feature_configuration,
)
# When compilation_mode=opt and objc_enable_binary_stripping are specified, the unstripped
# binary containing debug symbols is generated by the linker, which also needs the debug
# symbols for dead-code removal. The binary is also used to generate dSYM bundle if
# --apple_generate_dsym is specified. A symbol strip action is later registered to strip
# the symbol table from the unstripped binary.
if (ctx.fragments.cpp.objc_enable_binary_stripping() and
ctx.fragments.cpp.compilation_mode() == "opt"):
binary = ctx.actions.declare_shareable_artifact(
ctx.label.package + "/" + name + "_unstripped",
build_config.bin_dir,
)
else:
binary = ctx.actions.declare_shareable_artifact(
ctx.label.package + "/" + name,
build_config.bin_dir,
)
# Passing large numbers of inputs on the command line triggers a bug in Apple's Clang
# (b/29094356), so we'll create an input list manually and pass -filelist path/to/input/list.
# Populate the input file list with both the compiled object files and any linkstamp object
# files.
# There's some weirdness: cc_common.link compiles linkstamps and does the linking (without ever
# returning linkstamp objects)
# We replicate the linkstamp objects names (guess them) and generate input_file_list
# which is input to linking action.
linkstamp_map = _linkstamp_map(ctx, cc_linking_context.linkstamps(), binary, build_config)
input_file_list = _register_obj_filelist_action(
ctx,
build_config,
as_needed_libraries + static_runtimes.to_list() + linkstamp_map.values(),
)
extensions = user_variable_extensions | {
"framework_paths": [],
"framework_names": [],
"weak_framework_names": [],
"library_names": [],
"filelist": input_file_list.path,
"linked_binary": binary.path,
# artifacts to be passed to the linker with `-force_load`
"force_load_exec_paths": [lib.path for lib in always_link_libraries],
# linkopts from dependency
"dep_linkopts": _dedup_sdk_linkopts(cc_linking_context.linker_inputs),
"attr_linkopts": attr_linkopts, # linkopts arising from rule attributes
}
additional_inputs = [
input
for linker_input in cc_linking_context.linker_inputs.to_list()
for input in linker_input.additional_inputs
]
cc_common.link(
name = name,
actions = ctx.actions,
feature_configuration = feature_configuration,
cc_toolchain = common_variables.toolchain,
language = "objc",
additional_inputs = (
as_needed_libraries + always_link_libraries + [input_file_list] + extra_link_inputs +
additional_inputs +
getattr(ctx.files, "additional_linker_inputs", [])
),
linking_contexts = [cc_common.create_linking_context(linker_inputs = depset(
[cc_common.create_linker_input(
owner = ctx.label,
linkstamps = cc_linking_context.linkstamps(),
)],
))],
output_type = "executable",
build_config = build_config,
user_link_flags = extra_link_args,
stamp = stamp,
variables_extension = extensions,
additional_outputs = additional_outputs,
main_output = binary,
)
if not (ctx.fragments.cpp.objc_enable_binary_stripping() and
ctx.fragments.cpp.compilation_mode() == "opt"):
return binary
else:
return _register_binary_strip_action(ctx, name, binary, feature_configuration, build_config, extra_link_args)
compilation_support = struct(
register_compile_and_archive_actions = _register_compile_and_archive_actions,
register_compile_and_archive_actions_for_j2objc = _register_compile_and_archive_actions_for_j2objc,
build_common_variables = _build_common_variables,
build_feature_configuration = _build_feature_configuration,
validate_attributes = _validate_attributes,
register_fully_link_action = _register_fully_link_action,
register_configuration_specific_link_actions = _register_configuration_specific_link_actions,
)