# Copyright 2021 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. """cc_binary Starlark implementation replacing native""" load(":common/cc/semantics.bzl", "semantics") load(":common/cc/cc_shared_library.bzl", "GraphNodeInfo", "add_unused_dynamic_deps", "build_exports_map_from_only_dynamic_deps", "build_link_once_static_libs_map", "merge_cc_shared_library_infos", "separate_static_and_dynamic_link_libraries", "sort_linker_inputs", "throw_linked_but_not_exported_errors") load(":common/cc/cc_helper.bzl", "cc_helper", "linker_mode") load(":common/cc/cc_info.bzl", "CcInfo") load(":common/cc/cc_common.bzl", "cc_common") DebugPackageInfo = _builtins.toplevel.DebugPackageInfo cc_internal = _builtins.internal.cc_internal StaticallyLinkedMarkerInfo = _builtins.internal.StaticallyLinkedMarkerProvider _EXECUTABLE = "executable" _DYNAMIC_LIBRARY = "dynamic_library" _IOS_SIMULATOR_TARGET_CPUS = ["ios_x86_64", "ios_i386", "ios_sim_arm64"] _IOS_DEVICE_TARGET_CPUS = ["ios_armv6", "ios_arm64", "ios_armv7", "ios_armv7s", "ios_arm64e"] _VISIONOS_SIMULATOR_TARGET_CPUS = ["visionos_x86_64", "visionos_sim_arm64"] _VISIONOS_DEVICE_TARGET_CPUS = ["visionos_arm64"] _WATCHOS_SIMULATOR_TARGET_CPUS = ["watchos_i386", "watchos_x86_64", "watchos_arm64"] _WATCHOS_DEVICE_TARGET_CPUS = ["watchos_armv7k", "watchos_arm64_32"] _TVOS_SIMULATOR_TARGET_CPUS = ["tvos_x86_64", "tvos_sim_arm64"] _TVOS_DEVICE_TARGET_CPUS = ["tvos_arm64"] _CATALYST_TARGET_CPUS = ["catalyst_x86_64"] _MACOS_TARGET_CPUS = ["darwin_x86_64", "darwin_arm64", "darwin_arm64e"] def _strip_extension(file): if file.extension == "": return file.basename return file.basename[:-(1 + len(file.extension))] def _new_dwp_action(ctx, cc_toolchain, dwp_tools): return { "tools": dwp_tools, "executable": cc_toolchain.tool_path(tool = "DWP"), "arguments": ctx.actions.args(), "inputs": [], "outputs": [], } def _get_intermediate_dwp_file(ctx, dwp_output, order_number): output_path = dwp_output.short_path # Since it is a dwp_output we can assume that it always # ends with .dwp suffix, because it is declared so in outputs # attribute. extension_stripped_output_path = output_path[0:len(output_path) - 4] intermediate_path = extension_stripped_output_path + "-" + str(order_number) + ".dwp" return ctx.actions.declare_file("_dwps/" + intermediate_path) def _create_intermediate_dwp_packagers(ctx, dwp_output, cc_toolchain, dwp_files, dwo_files, intermediate_dwp_count): intermediate_outputs = dwo_files # This long loop is a substitution for recursion, which is not currently supported in Starlark. for _ in range(2147483647): packagers = [] current_packager = _new_dwp_action(ctx, cc_toolchain, dwp_files) inputs_for_current_packager = 0 # Step 1: generate our batches. We currently break into arbitrary batches of fixed maximum # input counts, but we can always apply more intelligent heuristics if the need arises. for dwo_file in intermediate_outputs: if inputs_for_current_packager == 100: packagers.append(current_packager) current_packager = _new_dwp_action(ctx, cc_toolchain, dwp_files) inputs_for_current_packager = 0 current_packager["inputs"].append(dwo_file) current_packager["arguments"].add(dwo_file) inputs_for_current_packager += 1 packagers.append(current_packager) # Step 2: given the batches, create the actions. if len(packagers) > 1: # If we have multiple batches, make them all intermediate actions, then pipe their outputs # into an additional level. intermediate_outputs = [] for packager in packagers: intermediate_output = _get_intermediate_dwp_file(ctx, dwp_output, intermediate_dwp_count) intermediate_dwp_count += 1 packager["outputs"].append(intermediate_output) packager["arguments"].add("-o", intermediate_output) ctx.actions.run( mnemonic = "CcGenerateIntermediateDwp", tools = packager["tools"], executable = packager["executable"], toolchain = cc_helper.CPP_TOOLCHAIN_TYPE, arguments = [packager["arguments"]], inputs = packager["inputs"], outputs = packager["outputs"], ) intermediate_outputs.append(intermediate_output) else: return packagers[0] # This is to fix buildifier errors, even though we should never reach this part of the code. return None def _create_debug_packager_actions(ctx, cc_toolchain, dwp_output, dwo_files): # No inputs? Just generate a trivially empty .dwp. # # Note this condition automatically triggers for any build where fission is disabled. # Because rules referencing .dwp targets may be invoked with or without fission, we need # to support .dwp generation even when fission is disabled. Since no actual functionality # is expected then, an empty file is appropriate. dwo_files_list = dwo_files.to_list() if len(dwo_files_list) == 0: ctx.actions.write(dwp_output, "", False) return # We apply a hierarchical action structure to limit the maximum number of inputs to any # single action. # # While the dwp tool consumes .dwo files, it can also consume intermediate .dwp files, # allowing us to split a large input set into smaller batches of arbitrary size and order. # Aside from the parallelism performance benefits this offers, this also reduces input # size requirements: if a.dwo, b.dwo, c.dwo, and e.dwo are each 1 KB files, we can apply # two intermediate actions DWP(a.dwo, b.dwo) --> i1.dwp and DWP(c.dwo, e.dwo) --> i2.dwp. # When we then apply the final action DWP(i1.dwp, i2.dwp) --> finalOutput.dwp, the inputs # to this action will usually total far less than 4 KB. # # The actions form an n-ary tree with n == MAX_INPUTS_PER_DWP_ACTION. The tree is fuller # at the leaves than the root, but that both increases parallelism and reduces the final # action's input size. packager = _create_intermediate_dwp_packagers(ctx, dwp_output, cc_toolchain, cc_toolchain.dwp_files(), dwo_files_list, 1) packager["outputs"].append(dwp_output) packager["arguments"].add("-o", dwp_output) ctx.actions.run( mnemonic = "CcGenerateDwp", tools = packager["tools"], executable = packager["executable"], toolchain = cc_helper.CPP_TOOLCHAIN_TYPE, arguments = [packager["arguments"]], inputs = packager["inputs"], outputs = packager["outputs"], ) def _get_non_data_deps(ctx): return ctx.attr.srcs + ctx.attr.deps def _runfiles_function(dep, linking_statically): provider = None if CcInfo in dep: provider = dep[CcInfo] if provider == None: return depset() return depset(cc_helper.get_dynamic_libraries_for_runtime(provider.linking_context, linking_statically)) def _default_runfiles_function(ctx, dep): provider = None if DefaultInfo in dep: provider = dep[DefaultInfo].default_runfiles if provider == None: return ctx.runfiles() return provider def _add(ctx, linking_statically): runfiles = [] for dep in _get_non_data_deps(ctx): provider = None if CcInfo in dep: provider = dep[CcInfo] if provider != None: runfiles.extend(cc_helper.get_dynamic_libraries_for_runtime(provider.linking_context, linking_statically)) return depset(runfiles) def _get_file_content(objects): result = [] for obj in objects: result.append(obj.path) result.append("\n") return "".join(result) def _add_transitive_info_providers(ctx, cc_toolchain, cpp_config, feature_configuration, cc_compilation_outputs, compilation_context, libraries, runtime_objects_for_coverage): additional_meta_data = [] if len(runtime_objects_for_coverage) != 0 and cpp_config.generate_llvm_lcov(): runtime_objects_list = ctx.actions.declare_file(ctx.label.name + "runtime_objects_list.txt") file_content = _get_file_content(runtime_objects_for_coverage) ctx.actions.write(output = runtime_objects_list, content = file_content, is_executable = False) additional_meta_data = [runtime_objects_list] + runtime_objects_for_coverage instrumented_files_provider = cc_helper.create_cc_instrumented_files_info( ctx = ctx, cc_config = cpp_config, cc_toolchain = cc_toolchain, metadata_files = additional_meta_data + cc_compilation_outputs.gcno_files() + cc_compilation_outputs.pic_gcno_files(), virtual_to_original_headers = compilation_context.virtual_to_original_headers(), ) output_groups = cc_helper.build_output_groups_for_emitting_compile_providers( cc_compilation_outputs, compilation_context, cpp_config, cc_toolchain, feature_configuration, ctx, False, # generate_hidden_top_level_group ) cc_info = CcInfo( compilation_context = compilation_context, cc_native_library_info = cc_helper.collect_native_cc_libraries(deps = ctx.attr.deps, libraries = libraries), ) output_groups["_validation"] = compilation_context.validation_artifacts return (cc_info, instrumented_files_provider, output_groups) def _collect_runfiles(ctx, feature_configuration, cc_toolchain, libraries, cc_library_linking_outputs, linking_mode, transitive_artifacts, link_compile_output_separately): # TODO(b/198254254): Add Legacyexternalrunfiles if necessary. runtime_objects_for_coverage = [] builder_artifacts = [] builder_transitive_artifacts = [] builder = ctx.runfiles(transitive_files = _add(ctx, linking_mode != linker_mode.LINKING_DYNAMIC), collect_default = True) coverage_runtime_objects_builder = ctx.runfiles(transitive_files = _add(ctx, linking_mode != linker_mode.LINKING_DYNAMIC)) runtime_objects_for_coverage.extend(coverage_runtime_objects_builder.files.to_list()) dynamic_libraries_for_runtime = _get_dynamic_libraries_for_runtime(True, libraries) runtime_objects_for_coverage.extend(dynamic_libraries_for_runtime) builder_transitive_artifacts.extend(transitive_artifacts.to_list()) builder_artifacts.extend(dynamic_libraries_for_runtime) runfiles_is_static = [] runfiles_is_not_static = [] for transitive_info_collection in ctx.attr.data: runfiles_is_static.append(ctx.runfiles(transitive_files = _runfiles_function(transitive_info_collection, True))) runfiles_is_not_static.append(ctx.runfiles(transitive_files = _runfiles_function(transitive_info_collection, False))) runtime_objects_for_coverage.extend(_runfiles_function(transitive_info_collection, True).to_list()) runtime_objects_for_coverage.extend(_runfiles_function(transitive_info_collection, False).to_list()) for dynamic_dep in ctx.attr.dynamic_deps: builder = builder.merge(dynamic_dep[DefaultInfo].default_runfiles) builder = builder.merge_all(runfiles_is_static + runfiles_is_not_static) if linking_mode == linker_mode.LINKING_DYNAMIC: dynamic_runtime_lib = cc_toolchain.dynamic_runtime_lib(feature_configuration = feature_configuration) dynamic_runtime_lib_list = dynamic_runtime_lib.to_list() builder_transitive_artifacts.extend(dynamic_runtime_lib_list) runtime_objects_for_coverage.extend(dynamic_runtime_lib_list) if link_compile_output_separately: if cc_library_linking_outputs != None and cc_library_linking_outputs.library_to_link != None and cc_library_linking_outputs.library_to_link.dynamic_library != None: builder_artifacts.append(cc_library_linking_outputs.library_to_link.dynamic_library) runtime_objects_for_coverage.append(cc_library_linking_outputs.library_to_link.dynamic_library) builder = builder.merge_all([ _default_runfiles_function(ctx, runtime) for runtime in semantics.get_cc_runtimes(ctx, _is_link_shared(ctx)) ] + [ ctx.runfiles(transitive_files = _runfiles_function(runtime, linking_mode != linker_mode.LINKING_DYNAMIC)) for runtime in semantics.get_cc_runtimes(ctx, _is_link_shared(ctx)) ]) return (builder.merge(ctx.runfiles(files = builder_artifacts, transitive_files = depset(builder_transitive_artifacts))), runtime_objects_for_coverage) def _get_target_sub_dir(target_name): last_separator = target_name.rfind("/") if last_separator == -1: return "" return target_name[0:last_separator] def _create_dynamic_libraries_copy_actions(ctx, dynamic_libraries_for_runtime): result = [] for lib in dynamic_libraries_for_runtime: # If the binary and the DLL don't belong to the same package or the DLL is a source file, # we should copy the DLL to the binary's directory. if ctx.label.package != lib.owner.package or ctx.label.workspace_name != lib.owner.workspace_name or lib.is_source: target_name = ctx.label.name target_sub_dir = _get_target_sub_dir(target_name) copy_file_path = lib.basename if target_sub_dir != "": copy_file_path = target_sub_dir + "/" + copy_file_path copy = ctx.actions.declare_file(copy_file_path) ctx.actions.symlink(output = copy, target_file = lib, progress_message = "Copying Execution Dynamic Library") result.append(copy) else: # If the library is already in the same directory as the binary, we don't need to copy it, # but we still add it to the result. result.append(lib) return depset(result) def _get_dynamic_library_for_runtime_or_none(library_to_link, link_statically): if library_to_link.dynamic_library == None: return None if link_statically and (library_to_link.static_library != None or library_to_link.pic_static_library != None): return None return library_to_link.dynamic_library def _get_dynamic_libraries_for_runtime(link_statically, libraries): dynamic_libraries_for_runtime = [] for library_to_link in libraries: artifact = _get_dynamic_library_for_runtime_or_none(library_to_link, link_statically) if artifact != None: dynamic_libraries_for_runtime.append(artifact) return dynamic_libraries_for_runtime def _get_providers(ctx): all_deps = ctx.attr.deps + semantics.get_cc_runtimes(ctx, _is_link_shared(ctx)) return [dep[CcInfo] for dep in all_deps if CcInfo in dep] def _collect_transitive_dwo_artifacts(cc_compilation_outputs, cc_debug_context, linking_mode, use_pic, lto_backend_artifacts): dwo_files = [] transitive_dwo_files = depset() if use_pic: dwo_files.extend(cc_compilation_outputs.pic_dwo_files()) else: dwo_files.extend(cc_compilation_outputs.dwo_files()) if lto_backend_artifacts != None: for lto_backend_artifact in lto_backend_artifacts: if lto_backend_artifact.dwo_file() != None: dwo_files.append(lto_backend_artifact.dwo_file()) if linking_mode != linker_mode.LINKING_DYNAMIC: if use_pic: transitive_dwo_files = cc_debug_context.pic_files else: transitive_dwo_files = cc_debug_context.files return depset(dwo_files, transitive = [transitive_dwo_files]) def _filter_libraries_that_are_linked_dynamically(ctx, feature_configuration, cc_linking_context): merged_cc_shared_library_infos = merge_cc_shared_library_infos(ctx) link_once_static_libs_map = build_link_once_static_libs_map(merged_cc_shared_library_infos) transitive_exports = build_exports_map_from_only_dynamic_deps(merged_cc_shared_library_infos) static_linker_inputs = [] linker_inputs = cc_linking_context.linker_inputs.to_list() all_deps = ctx.attr.deps + semantics.get_cc_runtimes(ctx, _is_link_shared(ctx)) graph_structure_aspect_nodes = [dep[GraphNodeInfo] for dep in all_deps if GraphNodeInfo in dep] can_be_linked_dynamically = {} for linker_input in linker_inputs: owner = str(linker_input.owner) if owner in transitive_exports: can_be_linked_dynamically[owner] = True # Entries in unused_dynamic_linker_inputs will be marked None if they are # used ( targets_to_be_linked_statically_map, targets_to_be_linked_dynamically_set, topologically_sorted_labels, unused_dynamic_linker_inputs, ) = separate_static_and_dynamic_link_libraries( ctx, graph_structure_aspect_nodes, can_be_linked_dynamically, ) topologically_sorted_labels = [ctx.label] + topologically_sorted_labels linker_inputs_seen = {} linked_statically_but_not_exported = {} label_to_linker_inputs = {} def _add_linker_input_to_dict(owner, linker_input): label_to_linker_inputs.setdefault(owner, []).append(linker_input) linker_inputs_count = 0 for linker_input in linker_inputs: stringified_linker_input = cc_helper.stringify_linker_input(linker_input) if stringified_linker_input in linker_inputs_seen: continue linker_inputs_seen[stringified_linker_input] = True owner = str(linker_input.owner) if owner in targets_to_be_linked_dynamically_set: unused_dynamic_linker_inputs[transitive_exports[owner].owner] = None _add_linker_input_to_dict(linker_input.owner, transitive_exports[owner]) linker_inputs_count += 1 elif owner in targets_to_be_linked_statically_map or str(ctx.label) == owner: if owner in link_once_static_libs_map: linked_statically_but_not_exported.setdefault(targets_to_be_linked_statically_map[owner], []).append(owner) else: _add_linker_input_to_dict(linker_input.owner, linker_input) linker_inputs_count += 1 # Unlike Unix on Windows every dynamic dependency must be linked to the # main binary, even indirect ones that are dependencies of direct # dynamic dependencies of this binary. link_indirect_deps = cc_common.is_enabled(feature_configuration = feature_configuration, feature_name = "targets_windows") linker_inputs_count += add_unused_dynamic_deps(ctx, unused_dynamic_linker_inputs, _add_linker_input_to_dict, topologically_sorted_labels, link_indirect_deps) throw_linked_but_not_exported_errors(linked_statically_but_not_exported) sorted_linker_inputs = sort_linker_inputs( topologically_sorted_labels, label_to_linker_inputs, linker_inputs_count, ) return cc_common.create_linking_context(linker_inputs = depset(sorted_linker_inputs, order = "topological")) def _create_transitive_linking_actions( ctx, cc_toolchain, feature_configuration, cpp_config, precompiled_files, cc_compilation_outputs, additional_linker_inputs, cc_linking_outputs, compilation_context, binary, deps_cc_linking_context, extra_link_time_libraries_depset, link_compile_output_separately, linking_mode, link_target_type, pdb_file, win_def_file, additional_linkopts, additional_make_variable_substitutions): cc_compilation_outputs_with_only_objects = cc_common.create_compilation_outputs(objects = None, pic_objects = None) deps_cc_info = CcInfo(linking_context = deps_cc_linking_context) libraries_for_current_cc_linking_context = [] if link_compile_output_separately: if cc_linking_outputs != None and cc_linking_outputs.library_to_link != None: libraries_for_current_cc_linking_context.append(cc_linking_outputs.library_to_link) else: cc_compilation_outputs_with_only_objects = cc_common.create_compilation_outputs( objects = depset(cc_compilation_outputs.objects), pic_objects = depset(cc_compilation_outputs.pic_objects), lto_compilation_context = cc_compilation_outputs.lto_compilation_context(), ) # Determine the libraries to link in. # First libraries from srcs. Shared library artifacts here are substituted with mangled symlink # artifacts generated by getDynamicLibraryLink(). This is done to minimize number of -rpath # entries during linking process. for libs in precompiled_files[:]: for artifact in libs: if _matches([".so", ".dylib", ".dll", ".ifso", ".tbd", ".lib", ".dll.a"], artifact.basename) or cc_helper.is_valid_shared_library_artifact(artifact): library_to_link = cc_common.create_library_to_link( actions = ctx.actions, feature_configuration = feature_configuration, cc_toolchain = cc_toolchain, dynamic_library = artifact, ) libraries_for_current_cc_linking_context.append(library_to_link) elif _matches([".pic.lo", ".lo", ".lo.lib"], artifact.basename): library_to_link = cc_common.create_library_to_link( actions = ctx.actions, feature_configuration = feature_configuration, cc_toolchain = cc_toolchain, static_library = artifact, alwayslink = True, ) libraries_for_current_cc_linking_context.append(library_to_link) elif _matches([".a", ".lib", ".pic.a", ".rlib"], artifact.basename) and not _matches([".if.lib"], artifact.basename): library_to_link = cc_common.create_library_to_link( actions = ctx.actions, feature_configuration = feature_configuration, cc_toolchain = cc_toolchain, static_library = artifact, ) libraries_for_current_cc_linking_context.append(library_to_link) linker_inputs = cc_common.create_linker_input( owner = ctx.label, libraries = depset(libraries_for_current_cc_linking_context), user_link_flags = cc_helper.linkopts(ctx, additional_make_variable_substitutions, cc_toolchain) + additional_linkopts, additional_inputs = depset(cc_helper.linker_scripts(ctx)), ) current_cc_linking_context = cc_common.create_linking_context(linker_inputs = depset([linker_inputs])) cc_info_without_extra_link_time_libraries = cc_common.merge_cc_infos(cc_infos = [CcInfo(linking_context = current_cc_linking_context), deps_cc_info]) extra_link_time_libraries_cc_info = CcInfo(linking_context = cc_common.create_linking_context(linker_inputs = extra_link_time_libraries_depset)) cc_info = cc_common.merge_cc_infos(cc_infos = [cc_info_without_extra_link_time_libraries, extra_link_time_libraries_cc_info]) cc_linking_context = cc_info.linking_context if len(ctx.attr.dynamic_deps) > 0: cc_linking_context = _filter_libraries_that_are_linked_dynamically(ctx, feature_configuration, cc_linking_context) link_deps_statically = True if linking_mode == linker_mode.LINKING_DYNAMIC: link_deps_statically = False cc_linking_outputs = cc_common.link( actions = ctx.actions, feature_configuration = feature_configuration, cc_toolchain = cc_toolchain, compilation_outputs = cc_compilation_outputs_with_only_objects, grep_includes = cc_helper.grep_includes_executable(ctx.attr._grep_includes), stamp = cc_helper.is_stamping_enabled(ctx), additional_inputs = additional_linker_inputs, linking_contexts = [cc_linking_context], name = ctx.label.name, use_test_only_flags = ctx.attr._is_test, # Note: Current Starlark API supports either dynamic or static linking modes, # even though there are more(LEGACY_FULL_STATIC, LEGACY_MOSTLY_STATIC_LIBRARIES) cc_binary # only uses dynamic or static modes. So instead of adding more native footprint # we can use what is already supported. # It is highly unlikely that cc_binary will start using legacy modes, # but if in case it does, code needs to be modified to support it. link_deps_statically = link_deps_statically, test_only_target = cc_helper.is_test_target(ctx) or ctx.attr._is_test, output_type = link_target_type, main_output = binary, never_link = True, pdb_file = pdb_file, win_def_file = win_def_file, ) cc_launcher_info = cc_internal.create_cc_launcher_info(cc_info = cc_info_without_extra_link_time_libraries, compilation_outputs = cc_compilation_outputs_with_only_objects) return (cc_linking_outputs, cc_launcher_info, cc_linking_context) def _use_pic(ctx, cc_toolchain, cpp_config, feature_configuration): if _is_link_shared(ctx): return cc_toolchain.needs_pic_for_dynamic_libraries(feature_configuration = feature_configuration) return cpp_config.force_pic() or ( cc_toolchain.needs_pic_for_dynamic_libraries(feature_configuration = feature_configuration) and ( ctx.var["COMPILATION_MODE"] != "opt" or cc_common.is_enabled(feature_configuration = feature_configuration, feature_name = "prefer_pic_for_opt_binaries") ) ) def _collect_linking_context(ctx): cc_infos = _get_providers(ctx) return cc_common.merge_cc_infos(direct_cc_infos = cc_infos, cc_infos = cc_infos).linking_context def _get_link_staticness(ctx, cpp_config): linkstatic_attr = None if hasattr(ctx.attr, "_linkstatic_explicitly_set") and not ctx.attr._linkstatic_explicitly_set: # If we know that linkstatic is not explicitly set, use computed default: linkstatic_attr = semantics.get_linkstatic_default(ctx) else: linkstatic_attr = ctx.attr.linkstatic if cpp_config.dynamic_mode() == "FULLY": return linker_mode.LINKING_DYNAMIC elif cpp_config.dynamic_mode() == "OFF" or linkstatic_attr: return linker_mode.LINKING_STATIC else: return linker_mode.LINKING_DYNAMIC def _matches(extensions, target): for extension in extensions: if target.endswith(extension): return True return False def _is_link_shared(ctx): return hasattr(ctx.attr, "linkshared") and ctx.attr.linkshared def _is_apple_platform(target_cpu): if target_cpu in _IOS_SIMULATOR_TARGET_CPUS or target_cpu in _IOS_DEVICE_TARGET_CPUS or target_cpu in _VISIONOS_SIMULATOR_TARGET_CPUS or target_cpu in _VISIONOS_DEVICE_TARGET_CPUS or target_cpu in _WATCHOS_SIMULATOR_TARGET_CPUS or target_cpu in _WATCHOS_DEVICE_TARGET_CPUS or target_cpu in _TVOS_SIMULATOR_TARGET_CPUS or target_cpu in _TVOS_DEVICE_TARGET_CPUS or target_cpu in _CATALYST_TARGET_CPUS or target_cpu in _MACOS_TARGET_CPUS: return True return False def cc_binary_impl(ctx, additional_linkopts): """Implementation function of cc_binary rule. Do NOT import outside cc_test. Args: ctx: The Starlark rule context. additional_linkopts: Additional linkopts from an external source (e.g. toolchain) Returns: Appropriate providers for cc_binary/cc_test. """ cc_helper.check_srcs_extensions(ctx, ALLOWED_SRC_FILES, "cc_binary", True) common = cc_internal.create_common(ctx = ctx) semantics.validate_deps(ctx) if len(ctx.attr.dynamic_deps) > 0: cc_common.check_experimental_cc_shared_library() # TODO(b/198254254): Add a check if linkshared value is explicitly specified. # if ctx.attr.linkshared: # fail("Do not use 'linkshared' to build a shared library. Use cc_shared_library instead.") # TODO(b/198254254): Fill empty providers if needed. cc_toolchain = cc_helper.find_cpp_toolchain(ctx) cpp_config = ctx.fragments.cpp cc_helper.report_invalid_options(cc_toolchain, cpp_config) precompiled_files = cc_helper.build_precompiled_files(ctx) link_target_type = _EXECUTABLE if _is_link_shared(ctx): link_target_type = _DYNAMIC_LIBRARY is_dynamic_link_type = True if link_target_type == _EXECUTABLE: is_dynamic_link_type = False semantics.validate_attributes(ctx) # TODO(b/198254254): Fill in empty providers if needed. # If cc_binary includes "linkshared=1" then gcc will be invoked with # linkopt "-shared", which causes the result of linking to be a shared library. # For linkshared=1 we used to force users to specify the file extension manually, as part of # the target name. # This is no longer necessary, the toolchain can figure out the correct file extensions. target_name = ctx.label.name has_legacy_link_shared_name = _is_link_shared(ctx) and (_matches([".so", ".dylib", ".dll"], target_name) or cc_helper.is_valid_shared_library_name(target_name)) binary = None if has_legacy_link_shared_name: binary = ctx.actions.declare_file(target_name) else: binary = cc_helper.get_linked_artifact( ctx = ctx, cc_toolchain = cc_toolchain, is_dynamic_link_type = is_dynamic_link_type, ) linking_mode = _get_link_staticness(ctx, cpp_config) features = ctx.features features.append(linking_mode) disabled_features = ctx.disabled_features if ctx.attr._is_test and cpp_config.incompatible_enable_cc_test_feature: features.append("is_cc_test") disabled_features.append("legacy_is_cc_test") feature_configuration = cc_common.configure_features( ctx = ctx, cc_toolchain = cc_toolchain, requested_features = features, unsupported_features = disabled_features, ) all_deps = ctx.attr.deps + semantics.get_cc_runtimes(ctx, _is_link_shared(ctx)) compilation_context_deps = [dep[CcInfo].compilation_context for dep in all_deps if CcInfo in dep] additional_make_variable_substitutions = cc_helper.get_toolchain_global_make_variables(cc_toolchain) additional_make_variable_substitutions.update(cc_helper.get_cc_flags_make_variable(ctx, feature_configuration, cc_toolchain)) (compilation_context, compilation_outputs) = cc_common.compile( name = ctx.label.name, actions = ctx.actions, feature_configuration = feature_configuration, cc_toolchain = cc_toolchain, user_compile_flags = cc_helper.get_copts(ctx, feature_configuration, additional_make_variable_substitutions), defines = cc_helper.defines(ctx, additional_make_variable_substitutions), local_defines = cc_helper.local_defines(ctx, additional_make_variable_substitutions) + cc_helper.get_local_defines_for_runfiles_lookup(ctx), loose_includes = common.loose_include_dirs, system_includes = cc_helper.system_include_dirs(ctx, additional_make_variable_substitutions), private_hdrs = cc_helper.get_private_hdrs(ctx), public_hdrs = cc_helper.get_public_hdrs(ctx), copts_filter = cc_helper.copts_filter(ctx, additional_make_variable_substitutions), srcs = cc_helper.get_srcs(ctx), compilation_contexts = compilation_context_deps, grep_includes = cc_helper.grep_includes_executable(ctx.attr._grep_includes), code_coverage_enabled = cc_helper.is_code_coverage_enabled(ctx = ctx), hdrs_checking_mode = semantics.determine_headers_checking_mode(ctx), ) precompiled_file_objects = cc_common.create_compilation_outputs( objects = depset(precompiled_files[0]), # objects pic_objects = depset(precompiled_files[1]), # pic_objects ) cc_compilation_outputs = cc_common.merge_compilation_outputs(compilation_outputs = [compilation_outputs, precompiled_file_objects]) additional_linker_inputs = ctx.files.additional_linker_inputs # Allows the dynamic library generated for code of test targets to be linked separately. link_compile_output_separately = ctx.attr._is_test and linking_mode == linker_mode.LINKING_DYNAMIC and cpp_config.dynamic_mode() == "DEFAULT" and ("dynamic_link_test_srcs" in ctx.features) is_windows_enabled = cc_common.is_enabled(feature_configuration = feature_configuration, feature_name = "targets_windows") # When linking the object files directly into the resulting binary, we do not need # library-level link outputs; thus, we do not let CcCompilationHelper produce link outputs # (either shared object files or archives) for a non-library link type [*], and add # the object files explicitly in determineLinkerArguments. # # When linking the object files into their own library, we want CcCompilationHelper to # take care of creating the library link outputs for us, so we need to set the link # type to STATIC_LIBRARY. # # [*] The only library link type is STATIC_LIBRARY. EXECUTABLE specifies a normal # cc_binary output, while DYNAMIC_LIBRARY is a cc_binary rules that produces an # output matching a shared object, for example cc_binary(name="foo.so", ...) on linux. cc_linking_outputs = None if link_compile_output_separately and not cc_helper.is_compilation_outputs_empty(cc_compilation_outputs): (_, cc_linking_outputs) = cc_common.create_linking_context_from_compilation_outputs( actions = ctx.actions, feature_configuration = feature_configuration, cc_toolchain = cc_toolchain, compilation_outputs = cc_compilation_outputs, name = ctx.label.name, grep_includes = cc_helper.grep_includes_executable(ctx.attr._grep_includes), linking_contexts = cc_helper.get_linking_contexts_from_deps(all_deps), stamp = cc_helper.is_stamping_enabled(ctx), alwayslink = True, disallow_dynamic_library = is_windows_enabled, ) is_static_mode = linking_mode != linker_mode.LINKING_DYNAMIC deps_cc_linking_context = _collect_linking_context(ctx) generated_def_file = None win_def_file = None if _is_link_shared(ctx): if is_windows_enabled: # Make copy of a list, to avoid mutating frozen values. object_files = list(cc_compilation_outputs.objects) for linker_input in deps_cc_linking_context.linker_inputs.to_list(): for library in linker_input.libraries: if is_static_mode or (library.dynamic_library == None and library.interface_library == None): if library.pic_static_library != None: if library.pic_objects != None: object_files.extend(library.pic_objects) elif library.static_library != None: if library.objects != None: object_files.extend(library.objects) def_parser = ctx.file._def_parser if def_parser != None: generated_def_file = cc_helper.generate_def_file(ctx, def_parser, object_files, binary.basename) custom_win_def_file = ctx.file.win_def_file win_def_file = cc_helper.get_windows_def_file_for_linking(ctx, custom_win_def_file, generated_def_file, feature_configuration) use_pic = _use_pic(ctx, cc_toolchain, cpp_config, feature_configuration) # On Windows, if GENERATE_PDB_FILE feature is enabled # then a pdb file will be built along with the executable. pdb_file = None if cc_common.is_enabled(feature_configuration = feature_configuration, feature_name = "generate_pdb_file"): pdb_file = ctx.actions.declare_file(_strip_extension(binary) + ".pdb", sibling = binary) extra_link_time_libraries = deps_cc_linking_context.extra_link_time_libraries() linker_inputs_extra = depset() runtime_libraries_extra = depset() if extra_link_time_libraries != None: linker_inputs_extra, runtime_libraries_extra = extra_link_time_libraries.build_libraries(ctx = ctx, static_mode = linking_mode != linker_mode.LINKING_DYNAMIC, for_dynamic_library = _is_link_shared(ctx)) cc_linking_outputs_binary, cc_launcher_info, deps_cc_linking_context = _create_transitive_linking_actions( ctx, cc_toolchain, feature_configuration, cpp_config, precompiled_files, cc_compilation_outputs, additional_linker_inputs, cc_linking_outputs, compilation_context, binary, deps_cc_linking_context, linker_inputs_extra, link_compile_output_separately, linking_mode, link_target_type, pdb_file, win_def_file, additional_linkopts, additional_make_variable_substitutions, ) cc_linking_outputs_binary_library = cc_linking_outputs_binary.library_to_link libraries = [] if _is_link_shared(ctx) and cc_linking_outputs_binary_library != None: libraries.append(cc_linking_outputs_binary_library) # Also add all shared libraries from srcs. for library in precompiled_files[6]: #shared_libraries library_to_link = cc_common.create_library_to_link( actions = ctx.actions, feature_configuration = feature_configuration, cc_toolchain = cc_toolchain, dynamic_library = library, #dynamic_library_symlink_path = library.short_path, ) libraries.append(library_to_link) files_to_build_list = [binary] # Create the stripped binary but don't add it to filesToBuild; it's only built when requested. stripped_file = ctx.outputs.stripped_binary cc_helper.create_strip_action(ctx, cc_toolchain, cpp_config, binary, stripped_file, feature_configuration) dwo_files = _collect_transitive_dwo_artifacts( cc_compilation_outputs, cc_helper.merge_cc_debug_contexts(cc_compilation_outputs, _get_providers(ctx)), linking_mode, use_pic, cc_linking_outputs_binary.all_lto_artifacts(), ) dwp_file = ctx.outputs.dwp_file _create_debug_packager_actions(ctx, cc_toolchain, dwp_file, dwo_files) explicit_dwp_file = dwp_file if not cc_helper.should_create_per_object_debug_info(feature_configuration, cpp_config): explicit_dwp_file = None elif ctx.attr._is_test and linking_mode != linker_mode.LINKING_DYNAMIC and cpp_config.build_test_dwp(): files_to_build_list.append(dwp_file) # If the binary is linked dynamically and COPY_DYNAMIC_LIBRARIES_TO_BINARY is enabled, collect # all the dynamic libraries we need at runtime. Then copy these libraries next to the binary. copied_runtime_dynamic_libraries = None if cc_common.is_enabled(feature_configuration = feature_configuration, feature_name = "copy_dynamic_libraries_to_binary"): linker_inputs = deps_cc_linking_context.linker_inputs.to_list() libraries = [] for linker_input in linker_inputs: libraries.extend(linker_input.libraries) copied_runtime_dynamic_libraries = _create_dynamic_libraries_copy_actions(ctx, _get_dynamic_libraries_for_runtime(is_static_mode, libraries)) # TODO(b/198254254)(bazel-team): Do we need to put original shared libraries (along with # mangled symlinks) into the RunfilesSupport object? It does not seem # logical since all symlinked libraries will be linked anyway and would # not require manual loading but if we do, then we would need to collect # their names and use a different constructor below. files_to_build = depset(files_to_build_list) transitive_artifacts_list = [files_to_build, runtime_libraries_extra] if cc_common.is_enabled(feature_configuration = feature_configuration, feature_name = "copy_dynamic_libraries_to_binary"): transitive_artifacts_list.append(copied_runtime_dynamic_libraries) transitive_artifacts = depset(transitive = transitive_artifacts_list) runtime_objects_for_coverage = [binary] runfiles, new_runtime_objects_for_coverage = _collect_runfiles( ctx, feature_configuration, cc_toolchain, libraries, cc_linking_outputs, linking_mode, transitive_artifacts, link_compile_output_separately, ) runtime_objects_for_coverage.extend(new_runtime_objects_for_coverage) (cc_info, instrumented_files_provider, output_groups) = _add_transitive_info_providers( ctx, cc_toolchain, cpp_config, feature_configuration, cc_compilation_outputs, compilation_context, libraries, runtime_objects_for_coverage, ) if _is_apple_platform(cc_toolchain.cpu) and ctx.attr._is_test: # TODO(b/198254254): Add ExecutionInfo. # buildifier: disable=unused-variable execution_info = None # If PDB file is generated by the link action, we add it to pdb_file output group if pdb_file != None: output_groups["pdb_file"] = depset([pdb_file]) if generated_def_file != None: output_groups["def_file"] = depset([generated_def_file]) if cc_linking_outputs_binary_library != None: # For consistency and readability. library_to_link = cc_linking_outputs_binary_library dynamic_library_for_linking = None if library_to_link.interface_library != None: if library_to_link.resolved_symlink_interface_library != None: dynamic_library_for_linking = library_to_link.resolved_symlink_interface_library else: dynamic_library_for_linking = library_to_link.interface_library elif library_to_link.dynamic_library != None: if library_to_link.resolved_symlink_dynamic_library != None: dynamic_library_for_linking = library_to_link.resolved_symlink_dynamic_library else: dynamic_library_for_linking = library_to_link.dynamic_library if dynamic_library_for_linking != None: output_groups["interface_library"] = depset([dynamic_library_for_linking]) if copied_runtime_dynamic_libraries != None: output_groups["runtime_dynamic_libraries"] = copied_runtime_dynamic_libraries # TODO(b/198254254): SetRunfilesSupport if needed. debug_package_info = DebugPackageInfo( target_label = ctx.label, stripped_file = stripped_file, unstripped_file = binary, dwp_file = explicit_dwp_file, ) binary_info = struct( files = files_to_build, runfiles = runfiles, executable = binary, ) result = [ cc_info, instrumented_files_provider, debug_package_info, OutputGroupInfo(**output_groups), ] if "fully_static_link" in ctx.features: result.append(StaticallyLinkedMarkerInfo(is_linked_statically = True)) if cc_launcher_info != None: result.append(cc_launcher_info) return binary_info, result ALLOWED_SRC_FILES = [] ALLOWED_SRC_FILES.extend(cc_helper.extensions.CC_SOURCE) ALLOWED_SRC_FILES.extend(cc_helper.extensions.C_SOURCE) ALLOWED_SRC_FILES.extend(cc_helper.extensions.CC_HEADER) ALLOWED_SRC_FILES.extend(cc_helper.extensions.ASSESMBLER_WITH_C_PREPROCESSOR) ALLOWED_SRC_FILES.extend(cc_helper.extensions.ASSEMBLER) ALLOWED_SRC_FILES.extend(cc_helper.extensions.ARCHIVE) ALLOWED_SRC_FILES.extend(cc_helper.extensions.PIC_ARCHIVE) ALLOWED_SRC_FILES.extend(cc_helper.extensions.ALWAYSLINK_LIBRARY) ALLOWED_SRC_FILES.extend(cc_helper.extensions.ALWAYSLINK_PIC_LIBRARY) ALLOWED_SRC_FILES.extend(cc_helper.extensions.SHARED_LIBRARY) ALLOWED_SRC_FILES.extend(cc_helper.extensions.OBJECT_FILE) ALLOWED_SRC_FILES.extend(cc_helper.extensions.PIC_OBJECT_FILE) def _impl(ctx): binary_info, providers = cc_binary_impl(ctx, []) # We construct DefaultInfo here, as other cc_binary-like rules (cc_test) need # a different DefaultInfo. providers.append(DefaultInfo( files = binary_info.files, runfiles = binary_info.runfiles, executable = binary_info.executable, )) # We construct RunEnvironmentInfo here as well. providers.append(RunEnvironmentInfo( environment = cc_helper.get_expanded_env(ctx, {}), # cc_binary does not have env_inherit attr. inherited_environment = [], )) return providers def make_cc_binary(cc_binary_attrs, **kwargs): return rule( implementation = _impl, attrs = cc_binary_attrs, outputs = { "stripped_binary": "%{name}.stripped", "dwp_file": "%{name}.dwp", }, fragments = ["cpp"] + semantics.additional_fragments(), exec_groups = { "cpp_link": exec_group(toolchains = cc_helper.use_cpp_toolchain()), }, toolchains = cc_helper.use_cpp_toolchain() + semantics.get_runtimes_toolchain(), executable = True, **kwargs )