| # 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 functions for C++ rules.""" |
| |
| load(":common/objc/semantics.bzl", "semantics") |
| |
| CcInfo = _builtins.toplevel.CcInfo |
| cc_common = _builtins.toplevel.cc_common |
| cc_internal = _builtins.internal.cc_internal |
| |
| def _check_src_extension(file, allowed_src_files): |
| extension = "." + file.extension |
| if _matches_extension(extension, allowed_src_files) or _is_shared_library_extension_valid(file.path): |
| return True |
| return False |
| |
| def _check_srcs_extensions(ctx, allowed_src_files, rule_name): |
| for src in ctx.attr.srcs: |
| if DefaultInfo in src: |
| files = src[DefaultInfo].files.to_list() |
| if len(files) == 1 and files[0].is_source: |
| if not _check_src_extension(files[0], allowed_src_files) and not files[0].is_directory: |
| fail("in srcs attribute of {} rule {}: source file '{}' is misplaced here".format(rule_name, ctx.label, str(src.label))) |
| else: |
| at_least_one_good = False |
| for file in files: |
| if _check_src_extension(file, allowed_src_files) or file.is_directory: |
| at_least_one_good = True |
| break |
| if not at_least_one_good: |
| fail("'{}' does not produce any {} srcs files".format(str(src.label), rule_name), attr = "srcs") |
| |
| def _merge_cc_debug_contexts(compilation_outputs, dep_cc_infos): |
| debug_context = cc_common.create_debug_context(compilation_outputs) |
| debug_contexts = [debug_context] |
| for dep_cc_info in dep_cc_infos: |
| debug_contexts.append(dep_cc_info.debug_context()) |
| |
| return cc_common.merge_debug_context(debug_contexts) |
| |
| def _is_code_coverage_enabled(ctx): |
| if ctx.coverage_instrumented(): |
| return True |
| if hasattr(ctx.attr, "deps"): |
| for dep in ctx.attr.deps: |
| if CcInfo in dep: |
| if ctx.coverage_instrumented(dep): |
| return True |
| return False |
| |
| def _get_dynamic_libraries_for_runtime(cc_linking_context, linking_statically): |
| libraries = [] |
| for linker_input in cc_linking_context.linker_inputs.to_list(): |
| libraries.extend(linker_input.libraries) |
| |
| dynamic_libraries_for_runtime = [] |
| for library in libraries: |
| artifact = _get_dynamic_library_for_runtime_or_none(library, linking_statically) |
| if artifact != None: |
| dynamic_libraries_for_runtime.append(artifact) |
| |
| return dynamic_libraries_for_runtime |
| |
| def _get_dynamic_library_for_runtime_or_none(library, linking_statically): |
| if library.dynamic_library == None: |
| return None |
| |
| if linking_statically and (library.static_library != None or library.pic_static_library != None): |
| return None |
| |
| return library.dynamic_library |
| |
| def _find_cpp_toolchain(ctx): |
| """ |
| Finds the c++ toolchain. |
| |
| If the c++ toolchain is in use, returns it. Otherwise, returns a c++ |
| toolchain derived from legacy toolchain selection. |
| |
| Args: |
| ctx: The rule context for which to find a toolchain. |
| |
| Returns: |
| A CcToolchainProvider. |
| """ |
| |
| # Check the incompatible flag for toolchain resolution. |
| if hasattr(cc_common, "is_cc_toolchain_resolution_enabled_do_not_use") and cc_common.is_cc_toolchain_resolution_enabled_do_not_use(ctx = ctx): |
| if not "@" + semantics.get_repo() + "//tools/cpp:toolchain_type" in ctx.toolchains: |
| fail("In order to use find_cpp_toolchain, you must include the '//tools/cpp:toolchain_type' in the toolchains argument to your rule.") |
| toolchain_info = ctx.toolchains["@" + semantics.get_repo() + "//tools/cpp:toolchain_type"] |
| if hasattr(toolchain_info, "cc_provider_in_toolchain") and hasattr(toolchain_info, "cc"): |
| return toolchain_info.cc |
| return toolchain_info |
| |
| # Otherwise, fall back to the legacy attribute. |
| if hasattr(ctx.attr, "_cc_toolchain"): |
| return ctx.attr._cc_toolchain[cc_common.CcToolchainInfo] |
| |
| # We didn't find anything. |
| fail("In order to use find_cpp_toolchain, you must define the '_cc_toolchain' attribute on your rule or aspect.") |
| |
| def _build_output_groups_for_emitting_compile_providers( |
| compilation_outputs, |
| compilation_context, |
| cpp_configuration, |
| cc_toolchain, |
| feature_configuration, |
| ctx, |
| generate_hidden_top_level_group): |
| output_groups_builder = {} |
| process_hdrs = cpp_configuration.process_headers_in_dependencies() |
| use_pic = cc_toolchain.needs_pic_for_dynamic_libraries(feature_configuration = feature_configuration) |
| output_groups_builder["temp_files_INTERNAL_"] = compilation_outputs.temps() |
| files_to_compile = compilation_outputs.files_to_compile( |
| parse_headers = process_hdrs, |
| use_pic = use_pic, |
| ) |
| output_groups_builder["compilation_outputs"] = files_to_compile |
| output_groups_builder["compilation_prerequisites_INTERNAL_"] = cc_internal.collect_compilation_prerequisites(ctx = ctx, compilation_context = compilation_context) |
| |
| if generate_hidden_top_level_group: |
| output_groups_builder["_hidden_top_level_INTERNAL_"] = _collect_library_hidden_top_level_artifacts( |
| ctx, |
| files_to_compile, |
| ) |
| |
| _create_save_feature_state_artifacts( |
| output_groups_builder, |
| cpp_configuration, |
| feature_configuration, |
| ctx, |
| ) |
| |
| return output_groups_builder |
| |
| CC_SOURCE = [".cc", ".cpp", ".cxx", ".c++", ".C", ".cu", ".cl"] |
| C_SOURCE = [".c"] |
| OBJC_SOURCE = [".m"] |
| OBJCPP_SOURCE = [".mm"] |
| CLIF_INPUT_PROTO = [".ipb"] |
| CLIF_OUTPUT_PROTO = [".opb"] |
| CC_AND_OBJC = [] |
| CC_AND_OBJC.extend(CC_SOURCE) |
| CC_AND_OBJC.extend(C_SOURCE) |
| CC_AND_OBJC.extend(OBJC_SOURCE) |
| CC_AND_OBJC.extend(OBJCPP_SOURCE) |
| CC_AND_OBJC.extend(CLIF_INPUT_PROTO) |
| CC_AND_OBJC.extend(CLIF_OUTPUT_PROTO) |
| |
| CC_HEADER = [".h", ".hh", ".hpp", ".ipp", ".hxx", ".h++", ".inc", ".inl", ".tlh", ".tli", ".H", ".tcc"] |
| ASSESMBLER_WITH_C_PREPROCESSOR = [".S"] |
| ASSEMBLER = [".s"] |
| ARCHIVE = [".a", ".lib"] |
| PIC_ARCHIVE = [".pic.a"] |
| ALWAYSLINK_LIBRARY = [".lo"] |
| ALWAYSLINK_PIC_LIBRARY = [".pic.lo"] |
| SHARED_LIBRARY = [".so", ".dylib", ".dll"] |
| OBJECT_FILE = [".o"] |
| PIC_OBJECT_FILE = [".pic.o"] |
| |
| extensions = struct( |
| CC_SOURCE = CC_SOURCE, |
| C_SOURCE = C_SOURCE, |
| CC_HEADER = CC_HEADER, |
| ASSESMBLER_WITH_C_PREPROCESSOR = ASSESMBLER_WITH_C_PREPROCESSOR, |
| ASSEMBLER = ASSEMBLER, |
| ARCHIVE = ARCHIVE, |
| PIC_ARCHIVE = PIC_ARCHIVE, |
| ALWAYSLINK_LIBRARY = ALWAYSLINK_LIBRARY, |
| ALWAYSLINK_PIC_LIBRARY = ALWAYSLINK_PIC_LIBRARY, |
| SHARED_LIBRARY = SHARED_LIBRARY, |
| OBJECT_FILE = OBJECT_FILE, |
| PIC_OBJECT_FILE = PIC_OBJECT_FILE, |
| CC_AND_OBJC = CC_AND_OBJC, |
| ) |
| |
| def _collect_header_tokens( |
| ctx, |
| cpp_configuration, |
| compilation_outputs, |
| process_hdrs, |
| add_self_tokens): |
| header_tokens_transitive = [] |
| for dep in ctx.attr.deps: |
| if "_hidden_header_tokens_INTERNAL_" in dep[OutputGroupInfo]: |
| header_tokens_transitive.append(dep[OutputGroupInfo]["_hidden_header_tokens_INTERNAL_"]) |
| else: |
| header_tokens_transitive.append(depset([])) |
| |
| header_tokens_direct = [] |
| if add_self_tokens and process_hdrs: |
| header_tokens_direct.extend(compilation_outputs.header_tokens()) |
| |
| return depset(direct = header_tokens_direct, transitive = header_tokens_transitive) |
| |
| def _collect_library_hidden_top_level_artifacts( |
| ctx, |
| files_to_compile): |
| artifacts_to_force_builder = [files_to_compile] |
| if hasattr(ctx.attr, "deps"): |
| for dep in ctx.attr.deps: |
| if OutputGroupInfo in dep: |
| artifacts_to_force_builder.append(dep[OutputGroupInfo]["_hidden_top_level_INTERNAL_"]) |
| |
| return depset(transitive = artifacts_to_force_builder) |
| |
| def _create_save_feature_state_artifacts( |
| output_groups_builder, |
| cpp_configuration, |
| feature_configuration, |
| ctx): |
| if cpp_configuration.save_feature_state(): |
| feature_state_file = ctx.actions.declare_file(ctx.label.name + "_feature_state.txt") |
| |
| ctx.actions.write(feature_state_file, str(feature_configuration)) |
| output_groups_builder["default"] = depset(direct = [feature_state_file]) |
| |
| def _merge_output_groups(output_groups): |
| merged_output_groups_builder = {} |
| for output_group in output_groups: |
| for output_key, output_value in output_group.items(): |
| depset_list = merged_output_groups_builder.get(output_key, []) |
| depset_list.append(output_value) |
| merged_output_groups_builder[output_key] = depset_list |
| |
| merged_output_group = {} |
| for k, v in merged_output_groups_builder.items(): |
| merged_output_group[k] = depset(transitive = v) |
| |
| return merged_output_group |
| |
| def _rule_error(msg): |
| fail(msg) |
| |
| def _attribute_error(attr_name, msg): |
| fail("in attribute '" + attr_name + "': " + msg) |
| |
| def _get_linking_contexts_from_deps(deps): |
| linking_contexts = [] |
| for dep in deps: |
| if CcInfo in dep: |
| linking_contexts.append(dep[CcInfo].linking_context) |
| return linking_contexts |
| |
| def _is_test_target(ctx): |
| if hasattr(ctx.attr, "testonly"): |
| return ctx.attr.testonly |
| |
| return False |
| |
| def _get_compilation_contexts_from_deps(deps): |
| compilation_contexts = [] |
| for dep in deps: |
| if CcInfo in dep: |
| compilation_contexts.append(dep[CcInfo].compilation_context) |
| return compilation_contexts |
| |
| def _is_compiltion_outputs_empty(compilation_outputs): |
| return (len(compilation_outputs.pic_objects) == 0 and |
| len(compilation_outputs.objects) == 0) |
| |
| def _matches_extension(extension, patterns): |
| for pattern in patterns: |
| if extension.endswith(pattern): |
| return True |
| return False |
| |
| def _build_precompiled_files(ctx): |
| objects = [] |
| pic_objects = [] |
| static_libraries = [] |
| pic_static_libraries = [] |
| alwayslink_static_libraries = [] |
| pic_alwayslink_static_libraries = [] |
| shared_libraries = [] |
| |
| for src in ctx.files.srcs: |
| short_path = src.short_path |
| |
| # For compatibility with existing BUILD files, any ".o" files listed |
| # in srcs are assumed to be position-independent code, or |
| # at least suitable for inclusion in shared libraries, unless they |
| # end with ".nopic.o". (The ".nopic.o" extension is an undocumented |
| # feature to give users at least some control over this.) Note that |
| # some target platforms do not require shared library code to be PIC. |
| if _matches_extension(short_path, OBJECT_FILE): |
| objects.append(src) |
| if not short_path.endswith(".nopic.o"): |
| pic_objects.append(src) |
| |
| if _matches_extension(short_path, PIC_OBJECT_FILE): |
| pic_objects.append(src) |
| |
| elif _matches_extension(short_path, PIC_ARCHIVE): |
| pic_static_libraries.append(src) |
| elif _matches_extension(short_path, ARCHIVE): |
| static_libraries.append(src) |
| elif _matches_extension(short_path, ALWAYSLINK_PIC_LIBRARY): |
| pic_alwayslink_static_libraries.append(src) |
| elif _matches_extension(short_path, ALWAYSLINK_LIBRARY): |
| alwayslink_static_libraries.append(src) |
| elif _is_shared_library_extension_valid(short_path): |
| shared_libraries.append(src) |
| return ( |
| objects, |
| pic_objects, |
| static_libraries, |
| pic_static_libraries, |
| alwayslink_static_libraries, |
| pic_alwayslink_static_libraries, |
| shared_libraries, |
| ) |
| |
| def _is_shared_library_extension_valid(shared_library_name): |
| if (shared_library_name.endswith(".so") or |
| shared_library_name.endswith(".dll") or |
| shared_library_name.endswith(".dylib")): |
| return True |
| |
| # Validate against the regex "^.+\.so(\.\d\w*)+$" for versioned .so files |
| parts = shared_library_name.split(".") |
| if len(parts) == 1: |
| return False |
| extension = parts[1] |
| if extension != "so": |
| return False |
| version_parts = parts[2:] |
| for part in version_parts: |
| if not part[0].isdigit(): |
| return False |
| for c in part[1:].elems(): |
| if not (c.isalnum() or c == "_"): |
| return False |
| return True |
| |
| def _get_providers(deps, provider): |
| providers = [] |
| for dep in deps: |
| if provider in dep: |
| providers.append(dep[provider]) |
| return providers |
| |
| def _is_compilation_outputs_empty(compilation_outputs): |
| return len(compilation_outputs.pic_objects) == 0 and len(compilation_outputs.objects) == 0 |
| |
| def _get_static_mode_params_for_dynamic_library_libraries(libs): |
| linker_inputs = [] |
| for lib in libs.to_list(): |
| if lib.pic_static_library: |
| linker_inputs.append(lib.pic_static_library) |
| elif lib.static_library: |
| linker_inputs.append(lib.static_library) |
| elif lib.interface_library: |
| linker_inputs.append(lib.interface_library) |
| else: |
| linker_inputs.append(lib.dynamic_library) |
| return linker_inputs |
| |
| def _should_create_per_object_debug_info(feature_configuration, cpp_configuration): |
| return cpp_configuration.fission_active_for_current_compilation_mode() and \ |
| cc_common.is_enabled( |
| feature_configuration = feature_configuration, |
| feature_name = "per_object_debug_info", |
| ) |
| |
| cc_helper = struct( |
| merge_cc_debug_contexts = _merge_cc_debug_contexts, |
| is_code_coverage_enabled = _is_code_coverage_enabled, |
| get_dynamic_libraries_for_runtime = _get_dynamic_libraries_for_runtime, |
| get_dynamic_library_for_runtime_or_none = _get_dynamic_library_for_runtime_or_none, |
| find_cpp_toolchain = _find_cpp_toolchain, |
| build_output_groups_for_emitting_compile_providers = _build_output_groups_for_emitting_compile_providers, |
| merge_output_groups = _merge_output_groups, |
| rule_error = _rule_error, |
| attribute_error = _attribute_error, |
| get_linking_contexts_from_deps = _get_linking_contexts_from_deps, |
| get_compilation_contexts_from_deps = _get_compilation_contexts_from_deps, |
| is_test_target = _is_test_target, |
| extensions = extensions, |
| build_precompiled_files = _build_precompiled_files, |
| is_shared_library_extension_valid = _is_shared_library_extension_valid, |
| get_providers = _get_providers, |
| is_compilation_outputs_empty = _is_compilation_outputs_empty, |
| matches_extension = _matches_extension, |
| get_static_mode_params_for_dynamic_library_libraries = _get_static_mode_params_for_dynamic_library_libraries, |
| should_create_per_object_debug_info = _should_create_per_object_debug_info, |
| check_srcs_extensions = _check_srcs_extensions, |
| ) |