blob: 724c300722dd1d011c3bf04168af5650fd9150f1 [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("@_builtins//:common/cc/cc_helper.bzl", "cc_helper")
load("@_builtins//:common/objc/objc_common.bzl", "objc_common")
load(":common/cc/cc_common.bzl", "cc_common")
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 = [],
runtime_deps = [],
extra_disabled_features = [],
extra_enabled_features = [],
extra_import_libraries = [],
attr_linkopts = [],
alwayslink = False,
has_module_map = False):
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,
runtime_deps = runtime_deps,
intermediate_artifacts = intermediate_artifacts,
alwayslink = alwayslink,
has_module_map = has_module_map,
extra_import_libraries = extra_import_libraries,
attr_linkopts = attr_linkopts,
)
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
includes = []
includes.extend(priority_headers)
includes.extend(objc_compilation_context.includes)
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,
user_compile_flags = user_compile_flags,
grep_includes = _get_grep_includes(common_variables.ctx),
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 = False,
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 _get_grep_includes(ctx):
if hasattr(ctx.executable, "_grep_includes"):
return ctx.executable._grep_includes
elif hasattr(ctx.file, "_grep_includes"):
return ctx.file._grep_includes
elif hasattr(ctx.files, "_grep_includes"):
return ctx.files._grep_includes[0]
return None
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,
grep_includes = _get_grep_includes(ctx),
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,
grep_includes = _get_grep_includes(common_variables.ctx),
)
def _build_fully_linked_variable_extensions_pre_migration(archive, objc_provider):
extensions = {}
extensions["fully_linked_archive_path"] = archive.path
cc_libs = {}
for cc_lib in objc_provider.flattened_cc_libraries():
cc_libs[cc_lib.path] = True
exclusively_objc_libs = []
for objc_lib in objc_provider.flattened_objc_libraries():
if objc_lib.path in cc_libs:
continue
exclusively_objc_libs.append(objc_lib.path)
import_paths = []
for import_lib in objc_provider.imported_library.to_list():
import_paths.append(import_lib.path)
for static_framework_file in objc_provider.static_framework_file.to_list():
import_paths.append(static_framework_file.path)
extensions["objc_library_exec_paths"] = exclusively_objc_libs
extensions["cc_library_exec_paths"] = cc_libs.keys()
extensions["imported_library_exec_paths"] = import_paths
return extensions
def _build_fully_linked_variable_extensions_post_migration(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 _register_fully_link_action_pre_migration(name, common_variables, objc_provider):
ctx = common_variables.ctx
feature_configuration = _build_feature_configuration(common_variables, False, False)
output_archive = ctx.actions.declare_file(name + ".a")
extensions = _build_fully_linked_variable_extensions_pre_migration(
output_archive,
objc_provider,
)
linker_inputs = []
linker_inputs.extend(objc_provider.flattened_objc_libraries())
linker_inputs.extend(objc_provider.flattened_cc_libraries())
linker_inputs.extend(objc_provider.imported_library.to_list())
linker_inputs.extend(objc_provider.static_framework_file.to_list())
return cc_common.link(
name = name,
actions = ctx.actions,
feature_configuration = feature_configuration,
cc_toolchain = common_variables.toolchain,
language = "objc",
additional_inputs = linker_inputs,
output_type = "archive",
variables_extension = extensions,
)
def _get_libraries_for_linking(libraries_to_link):
libraries = []
for library_to_link in libraries_to_link.to_list():
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_post_migration(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)
libraries = _get_libraries_for_linking(libraries_to_link)
output_archive = ctx.actions.declare_file(name + ".a")
extensions = _build_fully_linked_variable_extensions_post_migration(
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_fully_link_action(
*,
name,
linking_info_migration,
common_variables,
objc_provider = None,
cc_linking_context = None):
if not linking_info_migration:
return _register_fully_link_action_pre_migration(
name,
common_variables,
objc_provider,
)
else:
return _register_fully_link_action_post_migration(
name,
common_variables,
cc_linking_context,
)
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,
)