| # 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("//cc:action_names.bzl", "ACTION_NAMES") | |
| load("//cc:find_cc_toolchain.bzl", "CC_TOOLCHAIN_TYPE") | |
| load("//cc/common:feature_names.bzl", "feature_names") | |
| load("//cc/private:paths.bzl", "is_path_absolute") | |
| load("//cc/private:visibility.bzl", "PUBLIC_IF_NOT_GOOGLE") | |
| load("//cc/private/rules_impl:objc_common.bzl", "objc_common") | |
| load(":cc_common.bzl", "cc_common") | |
| load( | |
| ":cc_helper_internal.bzl", | |
| "get_relative_path", | |
| "is_versioned_shared_library_extension_valid", | |
| "path_contains_up_level_references", | |
| "should_create_per_object_debug_info", | |
| _artifact_category = "artifact_category_names", | |
| _extensions = "extensions", | |
| _get_cc_runtimes = "get_cc_runtimes", | |
| _get_cc_runtimes_copts = "get_cc_runtimes_copts", | |
| _is_stamping_enabled = "is_stamping_enabled", | |
| _package_source_root = "package_source_root", | |
| _repository_exec_path = "repository_exec_path", | |
| _should_stamp = "should_stamp", | |
| ) | |
| load(":cc_info.bzl", "CcInfo") | |
| visibility(PUBLIC_IF_NOT_GOOGLE) | |
| linker_mode = struct( | |
| LINKING_DYNAMIC = "dynamic_linking_mode", | |
| LINKING_STATIC = "static_linking_mode", | |
| ) | |
| cpp_file_types = struct( | |
| LINKER_SCRIPT = ["ld", "lds", "ldscript"], | |
| ) | |
| artifact_category = _artifact_category | |
| extensions = _extensions | |
| _SHARED_LIBRARY_SUFFIXES = tuple(extensions.SHARED_LIBRARY) | |
| _OBJECT_FILE_EXTENSIONS = tuple(extensions.OBJECT_FILE) | |
| _PIC_OBJECT_FILE_EXTENSIONS = tuple(extensions.PIC_OBJECT_FILE) | |
| _PIC_ARCHIVE_EXTENSIONS = tuple(extensions.PIC_ARCHIVE) | |
| _ARCHIVE_EXTENSIONS = tuple(extensions.ARCHIVE) | |
| _ALWAYSLINK_PIC_LIBRARY_EXTENSIONS = tuple(extensions.ALWAYSLINK_PIC_LIBRARY) | |
| _ALWAYSLINK_LIBRARY_EXTENSIONS = tuple(extensions.ALWAYSLINK_LIBRARY) | |
| def _rule_error(msg): | |
| fail(msg) | |
| def _attribute_error(attr_name, msg): | |
| fail("in attribute '" + attr_name + "': " + msg) | |
| def _libraries_from_linking_context(linking_context): | |
| libraries = [] | |
| for linker_input in linking_context.linker_inputs.to_list(): | |
| libraries.extend(linker_input.libraries) | |
| return depset(libraries, order = "topological") | |
| # NOTE: Prefer _is_valid_shared_library_artifact() when a File is available because | |
| # it checks File.extension directly. | |
| def _is_valid_shared_library_name(shared_library_name): | |
| return shared_library_name.endswith(_SHARED_LIBRARY_SUFFIXES) or \ | |
| is_versioned_shared_library_extension_valid(shared_library_name) | |
| def _replace_name(name, new_name): | |
| last_slash = name.rfind("/") | |
| if last_slash == -1: | |
| return new_name | |
| return name[:last_slash] + "/" + new_name | |
| def _get_base_name(name): | |
| last_slash = name.rfind("/") | |
| if last_slash == -1: | |
| return name | |
| return name[last_slash + 1:] | |
| def _get_linked_artifact(ctx, cc_toolchain, linked_artifact_category): | |
| name = ctx.label.name | |
| new_name = cc_common.get_artifact_name_for_category( | |
| cc_toolchain = cc_toolchain, | |
| category = linked_artifact_category, | |
| output_name = _get_base_name(name), | |
| ) | |
| name = _replace_name(name, new_name) | |
| return ctx.actions.declare_file(name) | |
| def _is_test_target(ctx): | |
| if hasattr(ctx.attr, "testonly"): | |
| return ctx.attr.testonly | |
| return False | |
| 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 _get_providers(deps, provider): | |
| providers = [] | |
| for dep in deps: | |
| if provider in dep: | |
| providers.append(dep[provider]) | |
| return providers | |
| def _merge_cc_debug_contexts(compilation_outputs, dep_cc_infos): | |
| debug_context = cc_common.create_debug_context(compilation_outputs) | |
| debug_contexts = [] | |
| for dep_cc_info in dep_cc_infos: | |
| if hasattr(dep_cc_info, "_debug_context"): | |
| debug_contexts.append(dep_cc_info._debug_context) | |
| else: | |
| debug_contexts.append(dep_cc_info.debug_context()) | |
| debug_contexts.append(debug_context) | |
| return cc_common.merge_debug_context(debug_contexts) | |
| def _collect_compilation_prerequisites(ctx, compilation_context): | |
| direct = [] | |
| transitive = [] | |
| if hasattr(ctx.attr, "srcs"): | |
| for src in ctx.attr.srcs: | |
| if DefaultInfo in src: | |
| files = src[DefaultInfo].files.to_list() | |
| for file in files: | |
| if _check_file_extension(file, extensions.CC_AND_OBJC, False): | |
| direct.append(file) | |
| transitive.append(compilation_context.headers) | |
| if hasattr(compilation_context, "additional_inputs"): | |
| transitive.append(compilation_context.additional_inputs()) | |
| else: | |
| direct_module_maps = compilation_context._direct_module_maps | |
| if type(direct_module_maps) != "depset": | |
| direct_module_maps = depset(direct_module_maps) | |
| transitive.append(direct_module_maps) | |
| transitive.append(compilation_context._non_code_inputs) | |
| if compilation_context._module_map: | |
| transitive.append(depset([compilation_context._module_map.file if type(compilation_context._module_map.file) == "File" else compilation_context._module_map.file()])) | |
| if hasattr(compilation_context, "transitive_modules"): | |
| transitive.append(compilation_context.transitive_modules(use_pic = True)) | |
| transitive.append(compilation_context.transitive_modules(use_pic = False)) | |
| else: | |
| transitive.append(compilation_context._transitive_pic_modules) | |
| transitive.append(compilation_context._transitive_modules) | |
| return depset(direct = direct, transitive = transitive) | |
| 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.pic_objects if use_pic else compilation_outputs.objects | |
| if process_hdrs: | |
| if hasattr(compilation_outputs, "header_tokens"): | |
| files_to_compile = files_to_compile + compilation_outputs.header_tokens() | |
| else: | |
| files_to_compile = files_to_compile + compilation_outputs._header_tokens | |
| files_to_compile = depset(files_to_compile) | |
| output_groups_builder["compilation_outputs"] = files_to_compile | |
| output_groups_builder["compilation_prerequisites_INTERNAL_"] = _collect_compilation_prerequisites(ctx = ctx, compilation_context = compilation_context) | |
| if hasattr(compilation_outputs, "module_files"): | |
| output_groups_builder["module_files"] = depset(compilation_outputs.module_files()) | |
| else: | |
| output_groups_builder["module_files"] = depset(compilation_outputs._module_files) | |
| # Add trace JSON files to output groups | |
| if hasattr(compilation_outputs, "_trace_files") and compilation_outputs._trace_files: | |
| output_groups_builder["trace_files"] = depset(compilation_outputs._trace_files) | |
| if hasattr(compilation_outputs, "_pic_trace_files") and compilation_outputs._pic_trace_files: | |
| pic_trace_group = output_groups_builder.get("trace_files", depset()) | |
| output_groups_builder["trace_files"] = depset(transitive = [pic_trace_group, depset(compilation_outputs._pic_trace_files)]) | |
| 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 | |
| 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: | |
| if "_hidden_top_level_INTERNAL_" in dep[OutputGroupInfo]: | |
| artifacts_to_force_builder.append(dep[OutputGroupInfo]["_hidden_top_level_INTERNAL_"]) | |
| return depset(transitive = artifacts_to_force_builder) | |
| def _create_dynamic_libraries_copy_actions(ctx, binary, dynamic_libraries_for_runtime): | |
| result = [] | |
| for lib in depset(dynamic_libraries_for_runtime).to_list(): | |
| # If the binary and the DLL are not in the same directory, copy the DLL | |
| # to the binary's directory. | |
| if lib.dirname != binary.dirname: | |
| copy = ctx.actions.declare_file(lib.basename, sibling = binary) | |
| 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_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 _collect_native_cc_libraries(deps, libraries): | |
| _CcNativeLibraryInfo = cc_common.get_cc_native_library_info_provider() | |
| transitive_libraries = [ | |
| dep[CcInfo]._legacy_transitive_native_libraries if hasattr(dep[CcInfo], "_legacy_transitive_native_libraries") else dep[CcInfo].transitive_native_libraries() | |
| for dep in deps | |
| if CcInfo in dep | |
| ] | |
| return _CcNativeLibraryInfo(libraries_to_link = depset(direct = libraries, transitive = transitive_libraries)) | |
| def _build_linking_context_from_libraries(ctx, libraries): | |
| if len(libraries) == 0: | |
| return CcInfo().linking_context | |
| linker_input = cc_common.create_linker_input( | |
| owner = ctx.label, | |
| libraries = depset(libraries), | |
| ) | |
| linking_context = cc_common.create_linking_context( | |
| linker_inputs = depset([linker_input]), | |
| ) | |
| return linking_context | |
| def _dll_hash_suffix(ctx, feature_configuration, cpp_config): | |
| if cpp_config.dynamic_mode() != "OFF": | |
| if feature_configuration.is_enabled(feature_names.TARGETS_WINDOWS): | |
| if not hasattr(ctx.attr, "win_def_file") or ctx.file.win_def_file == None: | |
| # Note: ctx.label.workspace_name strips leading @, | |
| # which is different from the native behavior. | |
| string_to_hash = ctx.label.workspace_name + ctx.label.package | |
| return "_%x" % hash(string_to_hash) | |
| return "" | |
| 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_compilation_outputs_empty(compilation_outputs): | |
| return (len(compilation_outputs.pic_objects) == 0 and | |
| len(compilation_outputs.objects) == 0) | |
| 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 _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 short_path.endswith(_OBJECT_FILE_EXTENSIONS): | |
| objects.append(src) | |
| if not short_path.endswith(".nopic.o"): | |
| pic_objects.append(src) | |
| if short_path.endswith(_PIC_OBJECT_FILE_EXTENSIONS): | |
| pic_objects.append(src) | |
| elif short_path.endswith(_PIC_ARCHIVE_EXTENSIONS): | |
| pic_static_libraries.append(src) | |
| elif short_path.endswith(_ARCHIVE_EXTENSIONS): | |
| static_libraries.append(src) | |
| elif short_path.endswith(_ALWAYSLINK_PIC_LIBRARY_EXTENSIONS): | |
| pic_alwayslink_static_libraries.append(src) | |
| elif short_path.endswith(_ALWAYSLINK_LIBRARY_EXTENSIONS): | |
| alwayslink_static_libraries.append(src) | |
| elif _is_valid_shared_library_artifact(src): | |
| shared_libraries.append(src) | |
| return ( | |
| objects, | |
| pic_objects, | |
| static_libraries, | |
| pic_static_libraries, | |
| alwayslink_static_libraries, | |
| pic_alwayslink_static_libraries, | |
| shared_libraries, | |
| ) | |
| def _check_file_extension(file, allowed_extensions, allow_versioned_shared_libraries): | |
| extension = "." + file.extension | |
| return extension in allowed_extensions or \ | |
| (allow_versioned_shared_libraries and is_versioned_shared_library_extension_valid(file.path)) | |
| def _check_file_extensions(attr_values, allowed_extensions, attr_name, label, rule_name, allow_versioned_shared_libraries): | |
| for attr_value in attr_values: | |
| if DefaultInfo in attr_value: | |
| files = attr_value[DefaultInfo].files.to_list() | |
| if len(files) == 1 and files[0].is_source: | |
| if not _check_file_extension(files[0], allowed_extensions, allow_versioned_shared_libraries) and not files[0].is_directory: | |
| fail("in {} attribute of {} rule {}: source file '{}' is misplaced here".format( | |
| attr_name, | |
| rule_name, | |
| label, | |
| str(attr_value.label), | |
| )) | |
| elif len(files) > 0: | |
| at_least_one_good = False | |
| for file in files: | |
| if _check_file_extension(file, allowed_extensions, allow_versioned_shared_libraries) or file.is_directory: | |
| at_least_one_good = True | |
| break | |
| if not at_least_one_good: | |
| fail("'{}' does not produce any {} {} files".format(str(attr_value.label), rule_name, attr_name), attr = attr_name) | |
| def _check_srcs_extensions(ctx, allowed_extensions, rule_name, allow_versioned_shared_libraries): | |
| _check_file_extensions(ctx.attr.srcs, allowed_extensions, "srcs", ctx.label, rule_name, allow_versioned_shared_libraries) | |
| def _gen_empty_def_file(ctx): | |
| trivial_def_file = ctx.actions.declare_file(ctx.label.name + ".gen.empty.def") | |
| ctx.actions.write(trivial_def_file, "", False) | |
| return trivial_def_file | |
| def _get_windows_def_file_for_linking(ctx, custom_def_file, generated_def_file, feature_configuration): | |
| # 1. If a custom DEF file is specified in win_def_file attribute, use it. | |
| # 2. If a generated DEF file is available and should be used, use it. | |
| # 3. Otherwise, we use an empty DEF file to ensure the import library will be generated. | |
| if custom_def_file != None: | |
| return custom_def_file | |
| elif generated_def_file != None and _should_generate_def_file(ctx, feature_configuration) == True: | |
| return generated_def_file | |
| else: | |
| return _gen_empty_def_file(ctx) | |
| def _should_generate_def_file(ctx, feature_configuration): | |
| windows_export_all_symbols_enabled = feature_configuration.is_enabled(feature_names.WINDOWS_EXPORT_ALL_SYMBOLS) | |
| no_windows_export_all_symbols_enabled = feature_configuration.is_enabled(feature_names.NO_WINDOWS_EXPORT_ALL_SYMBOLS) | |
| return windows_export_all_symbols_enabled and (not no_windows_export_all_symbols_enabled) and (ctx.attr.win_def_file == None) | |
| def _generate_def_file(ctx, def_parser, object_files, dll_name, cc_toolchain, feature_configuration): | |
| use_toolchain_action = cc_common.action_is_enabled( | |
| feature_configuration = feature_configuration, | |
| action_name = ACTION_NAMES.generate_def_file, | |
| ) | |
| if not use_toolchain_action and def_parser == None: | |
| return None | |
| def_file = ctx.actions.declare_file(ctx.label.name + ".gen.def") | |
| args = ctx.actions.args() | |
| args.add(def_file) | |
| args.add(dll_name) | |
| argv = ctx.actions.args() | |
| argv.use_param_file("@%s", use_always = True) | |
| argv.set_param_file_format("shell") | |
| for object_file in object_files: | |
| argv.add(object_file.path) | |
| executable = def_parser | |
| inputs = object_files | |
| env = {} | |
| execution_requirements = {} | |
| if use_toolchain_action: | |
| executable = cc_common.get_tool_for_action( | |
| feature_configuration = feature_configuration, | |
| action_name = ACTION_NAMES.generate_def_file, | |
| ) | |
| env = cc_common.get_environment_variables( | |
| feature_configuration = feature_configuration, | |
| action_name = ACTION_NAMES.generate_def_file, | |
| variables = cc_common.empty_variables(), | |
| ) | |
| execution_requirements = { | |
| requirement: "" | |
| for requirement in cc_common.get_execution_requirements( | |
| feature_configuration = feature_configuration, | |
| action_name = ACTION_NAMES.generate_def_file, | |
| ) | |
| } | |
| inputs = depset( | |
| direct = object_files, | |
| transitive = [cc_toolchain.all_files], | |
| ) | |
| ctx.actions.run( | |
| mnemonic = "DefParser", | |
| executable = executable, | |
| toolchain = None, | |
| arguments = [args, argv], | |
| env = env, | |
| execution_requirements = execution_requirements, | |
| inputs = inputs, | |
| outputs = [def_file], | |
| use_default_shell_env = True, | |
| ) | |
| return def_file | |
| def _stringify_linker_input(linker_input): | |
| parts = [] | |
| parts.append(str(linker_input.owner)) | |
| for library in linker_input.libraries: | |
| if library.static_library != None: | |
| parts.append(library.static_library.path) | |
| if library.pic_static_library != None: | |
| parts.append(library.pic_static_library.path) | |
| if library.dynamic_library != None: | |
| parts.append(library.dynamic_library.path) | |
| if library.interface_library != None: | |
| parts.append(library.interface_library.path) | |
| for additional_input in linker_input.additional_inputs: | |
| parts.append(additional_input.path) | |
| for linkstamp in linker_input.linkstamps: | |
| parts.append(linkstamp.file().path) | |
| return "".join(parts) | |
| 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 _tool_path(tool_paths, tool, feature_configuration = None, action_name = None, default = ""): | |
| path = tool_paths.get(tool, None) | |
| if path: | |
| return path | |
| if feature_configuration != None and action_name != None: | |
| if not cc_common.action_is_enabled( | |
| feature_configuration = feature_configuration, | |
| action_name = action_name, | |
| ): | |
| return default | |
| return cc_common.get_tool_for_action( | |
| feature_configuration = feature_configuration, | |
| action_name = action_name, | |
| ) or default | |
| return default | |
| def _get_toolchain_global_make_variables(cc_toolchain, feature_configuration): | |
| tool_paths = cc_toolchain._tool_paths | |
| result = { | |
| "CC": _tool_path(tool_paths, "gcc", feature_configuration, ACTION_NAMES.c_compile), | |
| "AR": _tool_path(tool_paths, "ar", feature_configuration, ACTION_NAMES.cpp_link_static_library), | |
| "NM": _tool_path(tool_paths, "nm", feature_configuration, None), | |
| "LD": _tool_path(tool_paths, "ld", feature_configuration, ACTION_NAMES.cpp_link_executable), | |
| "STRIP": _tool_path(tool_paths, "strip", feature_configuration, ACTION_NAMES.strip), | |
| "C_COMPILER": cc_toolchain.compiler, | |
| } # buildifier: disable=unsorted-dict-items | |
| obj_copy_tool = _tool_path(tool_paths, "objcopy", feature_configuration, ACTION_NAMES.objcopy_embed_data) | |
| if obj_copy_tool != None: | |
| # objcopy is optional in Crostool. | |
| result["OBJCOPY"] = obj_copy_tool | |
| gcov_tool = _tool_path(tool_paths, "gcov-tool", feature_configuration, None) | |
| if gcov_tool: | |
| # gcovtool is optional in Crostool. | |
| result["GCOVTOOL"] = gcov_tool | |
| libc = cc_toolchain.libc | |
| if libc.startswith("glibc-"): | |
| # Strip "glibc-" prefix. | |
| result["GLIBC_VERSION"] = libc[6:] | |
| else: | |
| result["GLIBC_VERSION"] = libc | |
| abi_glibc_version = cc_toolchain._abi_glibc_version | |
| if abi_glibc_version != None: | |
| result["ABI_GLIBC_VERSION"] = abi_glibc_version | |
| abi = cc_toolchain._abi | |
| if abi != None: | |
| result["ABI"] = abi | |
| result["CROSSTOOLTOP"] = cc_toolchain._crosstool_top_path | |
| return result | |
| _SHARED_LIBRARY_EXTENSIONS = ["so", "dll", "dylib", "pyd", "wasm", "xll"] | |
| def _is_valid_shared_library_artifact(shared_library): | |
| if (shared_library.extension in _SHARED_LIBRARY_EXTENSIONS): | |
| return True | |
| return is_versioned_shared_library_extension_valid(shared_library.basename) | |
| 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 _create_strip_action(ctx, cc_toolchain, cpp_config, input, output, feature_configuration): | |
| if feature_configuration.is_enabled(feature_names.NO_STRIPPING): | |
| ctx.actions.symlink( | |
| output = output, | |
| target_file = input, | |
| progress_message = "Symlinking original binary as stripped binary", | |
| ) | |
| return | |
| if not cc_common.action_is_enabled(feature_configuration = feature_configuration, action_name = "strip"): | |
| fail("Expected action_config for 'strip' to be configured.") | |
| variables = cc_common.create_compile_variables( | |
| cc_toolchain = cc_toolchain, | |
| feature_configuration = feature_configuration, | |
| output_file = output.path, | |
| input_file = input.path, | |
| strip_opts = cpp_config.strip_opts(), | |
| ) | |
| command_line = cc_common.get_memory_inefficient_command_line( | |
| feature_configuration = feature_configuration, | |
| action_name = "strip", | |
| variables = variables, | |
| ) | |
| env = cc_common.get_environment_variables( | |
| feature_configuration = feature_configuration, | |
| action_name = "strip", | |
| variables = variables, | |
| ) | |
| execution_info = {} | |
| for execution_requirement in cc_common.get_tool_requirement_for_action(feature_configuration = feature_configuration, action_name = "strip"): | |
| execution_info[execution_requirement] = "" | |
| ctx.actions.run( | |
| inputs = depset( | |
| direct = [input], | |
| transitive = [cc_toolchain._strip_files], | |
| ), | |
| outputs = [output], | |
| use_default_shell_env = True, | |
| env = env, | |
| executable = cc_common.get_tool_for_action(feature_configuration = feature_configuration, action_name = "strip"), | |
| toolchain = CC_TOOLCHAIN_TYPE, | |
| execution_requirements = execution_info, | |
| progress_message = "Stripping {} for {}".format(output.short_path, ctx.label), | |
| mnemonic = "CcStrip", | |
| arguments = command_line, | |
| ) | |
| def _lookup_var(ctx, additional_vars, var): | |
| expanded_make_var_ctx = ctx.var.get(var) | |
| expanded_make_var_additional = additional_vars.get(var) | |
| if expanded_make_var_additional != None: | |
| return expanded_make_var_additional | |
| if expanded_make_var_ctx != None: | |
| return expanded_make_var_ctx | |
| fail("{}: {} not defined".format(ctx.label, "$(" + var + ")")) | |
| def _expand_nested_variable(ctx, additional_vars, exp, execpath = True, targets = []): | |
| # If make variable is predefined path variable(like $(location ...)) | |
| # we will expand it first. | |
| if exp.find(" ") != -1: | |
| if not execpath: | |
| if exp.startswith("location"): | |
| exp = exp.replace("location", "rootpath", 1) | |
| data_targets = [] | |
| if ctx.attr.data != None: | |
| data_targets = ctx.attr.data | |
| # Make sure we do not duplicate targets. | |
| unified_targets_set = {} | |
| for data_target in data_targets: | |
| unified_targets_set[data_target] = True | |
| for target in targets: | |
| unified_targets_set[target] = True | |
| return ctx.expand_location("$({})".format(exp), targets = unified_targets_set.keys()) | |
| # Recursively expand nested make variables, but since there is no recursion | |
| # in Starlark we will do it via for loop. | |
| unbounded_recursion = True | |
| # The only way to check if the unbounded recursion is happening or not | |
| # is to have a look at the depth of the recursion. | |
| # 10 seems to be a reasonable number, since it is highly unexpected | |
| # to have nested make variables which are expanding more than 10 times. | |
| for _ in range(10): | |
| exp = _lookup_var(ctx, additional_vars, exp) | |
| if len(exp) >= 3 and exp[0] == "$" and exp[1] == "(" and exp[len(exp) - 1] == ")": | |
| # Try to expand once more. | |
| exp = exp[2:len(exp) - 1] | |
| continue | |
| unbounded_recursion = False | |
| break | |
| if unbounded_recursion: | |
| fail("potentially unbounded recursion during expansion of {}".format(exp)) | |
| return exp | |
| def _expand(ctx, expression, additional_make_variable_substitutions, execpath = True, targets = []): | |
| idx = 0 | |
| last_make_var_end = 0 | |
| result = [] | |
| n = len(expression) | |
| for _ in range(n): | |
| if idx >= n: | |
| break | |
| if expression[idx] != "$": | |
| idx += 1 | |
| continue | |
| idx += 1 | |
| # We've met $$ pattern, so $ is escaped. | |
| if idx < n and expression[idx] == "$": | |
| idx += 1 | |
| result.append(expression[last_make_var_end:idx - 1]) | |
| last_make_var_end = idx | |
| # We might have found a potential start for Make Variable. | |
| elif idx < n and expression[idx] == "(": | |
| # Try to find the closing parentheses. | |
| make_var_start = idx | |
| make_var_end = make_var_start | |
| for j in range(idx + 1, n): | |
| if expression[j] == ")": | |
| make_var_end = j | |
| break | |
| # Note we cannot go out of string's bounds here, | |
| # because of this check. | |
| # If start of the variable is different from the end, | |
| # we found a make variable. | |
| if make_var_start != make_var_end: | |
| # Some clarifications: | |
| # *****$(MAKE_VAR_1)*******$(MAKE_VAR_2)***** | |
| # ^ ^ ^ | |
| # | | | | |
| # last_make_var_end make_var_start make_var_end | |
| result.append(expression[last_make_var_end:make_var_start - 1]) | |
| make_var = expression[make_var_start + 1:make_var_end] | |
| exp = _expand_nested_variable(ctx, additional_make_variable_substitutions, make_var, execpath, targets) | |
| result.append(exp) | |
| # Update indexes. | |
| idx = make_var_end + 1 | |
| last_make_var_end = idx | |
| # Add the last substring which would be skipped by for loop. | |
| if last_make_var_end < n: | |
| result.append(expression[last_make_var_end:n]) | |
| return "".join(result) | |
| def _get_expanded_env(ctx, additional_make_variable_substitutions): | |
| if not hasattr(ctx.attr, "env"): | |
| fail("could not find rule attribute named: 'env'") | |
| expanded_env = {} | |
| for k in ctx.attr.env: | |
| expanded_env[k] = _expand( | |
| ctx, | |
| ctx.attr.env[k], | |
| additional_make_variable_substitutions, | |
| # By default, Starlark `ctx.expand_location` has `execpath` semantics. | |
| # For legacy attributes, e.g. `env`, we want `rootpath` semantics instead. | |
| execpath = False, | |
| ) | |
| return expanded_env | |
| # Implementation of Bourne shell tokenization. | |
| # Tokenizes str and appends result to the options list. | |
| def _tokenize(options, options_string): | |
| token = [] | |
| force_token = False | |
| quotation = "\0" | |
| length = len(options_string) | |
| # Since it is impossible to modify loop variable inside loop | |
| # in Starlark, and also there is no while loop, I have to | |
| # use this ugly hack. | |
| i = -1 | |
| for _ in range(length): | |
| i += 1 | |
| if i >= length: | |
| break | |
| c = options_string[i] | |
| if quotation != "\0": | |
| # In quotation. | |
| if c == quotation: | |
| # End quotation. | |
| quotation = "\0" | |
| elif c == "\\" and quotation == "\"": | |
| i += 1 | |
| if i == length: | |
| fail("backslash at the end of the string: {}".format(options_string)) | |
| c = options_string[i] | |
| if c != "\\" and c != "\"": | |
| token.append("\\") | |
| token.append(c) | |
| else: | |
| # Regular char, in quotation. | |
| token.append(c) | |
| else: | |
| # Not in quotation. | |
| if c == "'" or c == "\"": | |
| # Begin single double quotation. | |
| quotation = c | |
| force_token = True | |
| elif c == " " or c == "\t": | |
| # Space not quoted. | |
| if force_token or len(token) > 0: | |
| options.append("".join(token)) | |
| token = [] | |
| force_token = False | |
| elif c == "\\": | |
| # Backslash not quoted. | |
| i += 1 | |
| if i == length: | |
| fail("backslash at the end of the string: {}".format(options_string)) | |
| token.append(options_string[i]) | |
| else: | |
| # Regular char, not quoted. | |
| token.append(c) | |
| if quotation != "\0": | |
| fail("unterminated quotation at the end of the string: {}".format(options_string)) | |
| if force_token or len(token) > 0: | |
| options.append("".join(token)) | |
| def _should_use_pic(ctx, cc_toolchain, feature_configuration): | |
| """Whether to use pic files | |
| Args: | |
| ctx: (RuleContext) | |
| cc_toolchain: (CcToolchainInfo) | |
| feature_configuration: (FeatureConfiguration) | |
| Returns: | |
| (bool) | |
| """ | |
| return ctx.fragments.cpp.force_pic() or ( | |
| cc_toolchain.needs_pic_for_dynamic_libraries(feature_configuration = feature_configuration) and ( | |
| ctx.var["COMPILATION_MODE"] != "opt" or | |
| feature_configuration.is_enabled(feature_names.PREFER_PIC_FOR_OPT_BINARIES) | |
| ) | |
| ) | |
| SYSROOT_FLAG = "--sysroot=" | |
| def _contains_sysroot(original_cc_flags, feature_config_cc_flags): | |
| if SYSROOT_FLAG in original_cc_flags: | |
| return True | |
| for flag in feature_config_cc_flags: | |
| if SYSROOT_FLAG in flag: | |
| return True | |
| return False | |
| def _get_cc_flags_make_variable(_ctx, feature_configuration, cc_toolchain): | |
| original_cc_flags = cc_toolchain._legacy_cc_flags_make_variable | |
| sysroot_cc_flag = "" | |
| if cc_toolchain.sysroot != None: | |
| sysroot_cc_flag = SYSROOT_FLAG + cc_toolchain.sysroot | |
| build_vars = cc_toolchain._build_variables | |
| feature_config_cc_flags = cc_common.get_memory_inefficient_command_line( | |
| feature_configuration = feature_configuration, | |
| action_name = "cc-flags-make-variable", | |
| variables = build_vars, | |
| ) | |
| cc_flags = [original_cc_flags] | |
| # Only add sysroots flag if nothing else adds sysroot, BUT it must appear | |
| # before the feature config flags. | |
| if not _contains_sysroot(original_cc_flags, feature_config_cc_flags): | |
| cc_flags.append(sysroot_cc_flag) | |
| cc_flags.extend(feature_config_cc_flags) | |
| return {"CC_FLAGS": " ".join(cc_flags)} | |
| def _package_exec_path(ctx, package, sibling_repository_layout): | |
| return get_relative_path(_repository_exec_path(ctx.label.workspace_name, sibling_repository_layout), package) | |
| def _include_dirs(ctx, additional_make_variable_substitutions, attr = "includes"): | |
| result = [] | |
| sibling_repository_layout = ctx.configuration.is_sibling_repository_layout() | |
| package = ctx.label.package | |
| package_exec_path = _package_exec_path(ctx, package, sibling_repository_layout) | |
| package_source_root = _package_source_root(ctx.label.workspace_name, package, sibling_repository_layout) | |
| for include in getattr(ctx.attr, attr): | |
| includes_attr = _expand(ctx, include, additional_make_variable_substitutions) | |
| if is_path_absolute(includes_attr): | |
| fail("The path '" + includes_attr + "' is absolute, but only relative paths are allowed.", attr = attr) | |
| includes_path = get_relative_path(package_exec_path, includes_attr) | |
| if not sibling_repository_layout and path_contains_up_level_references(includes_path): | |
| fail("Path references a path above the execution root.", attr = "includes") | |
| result.append(includes_path) | |
| # We don't need to perform the above checks against out_includes_path again since any errors | |
| # must have manifested in includesPath already. | |
| out_includes_path = get_relative_path(package_source_root, includes_attr) | |
| if (ctx.configuration.has_separate_genfiles_directory()): | |
| result.append(get_relative_path(ctx.genfiles_dir.path, out_includes_path)) | |
| result.append(get_relative_path(ctx.bin_dir.path, out_includes_path)) | |
| return result | |
| def _report_invalid_options(cc_toolchain, cpp_config): | |
| if cpp_config.grte_top() != None and cc_toolchain.sysroot == None: | |
| fail("The selected toolchain does not support setting --grte_top (it doesn't specify builtin_sysroot).") | |
| def _check_cpp_modules(ctx, feature_configuration): | |
| if len(ctx.files.module_interfaces) == 0: | |
| return | |
| if not ctx.fragments.cpp.experimental_cpp_modules(): | |
| fail("requires --experimental_cpp_modules", attr = "module_interfaces") | |
| if not feature_configuration.is_enabled( | |
| feature_names.CPP_MODULES, | |
| ): | |
| fail("to use C++ modules, the feature {} must be enabled".format(feature_names.CPP_MODULES)) | |
| def _expand_make_variables_for_copts(ctx, tokenization, unexpanded_tokens, additional_make_variable_substitutions): | |
| tokens = [] | |
| targets = [] | |
| for additional_compiler_input in getattr(ctx.attr, "additional_compiler_inputs", []): | |
| targets.append(additional_compiler_input) | |
| for token in unexpanded_tokens: | |
| if tokenization: | |
| expanded_token = _expand(ctx, token, additional_make_variable_substitutions, targets = targets) | |
| _tokenize(tokens, expanded_token) | |
| else: | |
| exp = _expand_single_make_variable(ctx, token, additional_make_variable_substitutions) | |
| if exp != None: | |
| _tokenize(tokens, exp) | |
| else: | |
| tokens.append(_expand(ctx, token, additional_make_variable_substitutions, targets = targets)) | |
| return tokens | |
| def _matches_disallowed_copt(flag, disallowed_flags): | |
| """Returns True if flag equals a disallowed flag or has a disallowed prefix.""" | |
| for disallowed in disallowed_flags: | |
| if flag == disallowed or ( | |
| disallowed.endswith("=") and flag.startswith(disallowed) | |
| ): | |
| return True | |
| return False | |
| def _verify_no_disallowed_copts( | |
| ctx, | |
| copts, | |
| attr_name, | |
| disallowed_copts_infos = [], | |
| command_line_copts = None): | |
| """Fails the build if copts contain any disallowed compiler flags.""" | |
| if not disallowed_copts_infos: | |
| return | |
| violations = [] | |
| for info in disallowed_copts_infos: | |
| if not hasattr(info, "flags") or not info.flags: | |
| continue | |
| # Check if target is exempt under this policy's allowlist | |
| allowlist = getattr(info, "allowlist", None) | |
| if allowlist != None and allowlist.contains(ctx.label): | |
| continue | |
| allowlist_target_label = getattr(info, "allowlist_target_label", None) | |
| error_message = getattr(info, "error_message", None) | |
| # Pair each set of flags with a description of where it came from. | |
| flag_sources = [("attribute '{}'".format(attr_name), copts)] | |
| if getattr(info, "verify_command_line", False) and command_line_copts: | |
| flag_sources.extend([ | |
| ("'--copt' flag", command_line_copts.copts), | |
| ("'--cxxopt' flag", command_line_copts.cxxopts), | |
| ("'--conlyopt' flag", command_line_copts.conlyopts), | |
| ]) | |
| for source, flags in flag_sources: | |
| for flag in flags: | |
| if _matches_disallowed_copt(flag, info.flags): | |
| violations.append((flag, allowlist_target_label, error_message, source)) | |
| if violations: | |
| formatted_violations = [] | |
| for flag, allowlist_target_label, error_message, source in violations: | |
| allowlist_clause = ( | |
| " (target is not in the allowlist '{}')".format( | |
| allowlist_target_label, | |
| ) if allowlist_target_label else "" | |
| ) | |
| error_clause = ( | |
| ": {}".format(error_message) if error_message else "" | |
| ) | |
| formatted_violations.append( | |
| "- Flag '{}' in {}{}{}".format( | |
| flag, | |
| source, | |
| allowlist_clause, | |
| error_clause, | |
| ), | |
| ) | |
| fail( | |
| """ | |
| [DISALLOWED COPTS ERROR] Target '{}' specifies disallowed compiler flag(s): | |
| {} | |
| """.format( | |
| ctx.label, | |
| "\n ".join(formatted_violations), | |
| ), | |
| stack_trace = False, | |
| ) | |
| def _get_copts( | |
| ctx, | |
| feature_configuration, | |
| additional_make_variable_substitutions, | |
| attr = "copts", | |
| requested_features = None, | |
| disallowed_copts_infos = []): | |
| if not hasattr(ctx.attr, attr): | |
| fail("could not find rule attribute named: '{}'".format(attr)) | |
| if requested_features == None: | |
| requested_features = ctx.features | |
| attribute_copts = getattr(ctx.attr, attr) | |
| # Only read the command-line options when a policy asks for them; this runs | |
| # for every C++ target, once per copts-like attribute. | |
| command_line_copts = None | |
| for info in disallowed_copts_infos: | |
| if getattr(info, "verify_command_line", False): | |
| cpp_fragment = ctx.fragments.cpp | |
| command_line_copts = struct( | |
| copts = cpp_fragment.copts, | |
| cxxopts = cpp_fragment.cxxopts, | |
| conlyopts = cpp_fragment.conlyopts, | |
| ) | |
| break | |
| tokenization = not (feature_configuration.is_enabled(feature_names.NO_COPTS_TOKENIZATION) or feature_names.NO_COPTS_TOKENIZATION in requested_features) | |
| expanded_attribute_copts = _expand_make_variables_for_copts(ctx, tokenization, attribute_copts, additional_make_variable_substitutions) | |
| _verify_no_disallowed_copts( | |
| ctx, | |
| expanded_attribute_copts, | |
| attr, | |
| disallowed_copts_infos = disallowed_copts_infos, | |
| command_line_copts = command_line_copts, | |
| ) | |
| return expanded_attribute_copts | |
| # Tries to expand a single make variable from token. | |
| # If token has additional characters other than ones | |
| # corresponding to make variable returns None. | |
| def _expand_single_make_variable(ctx, token, additional_make_variable_substitutions): | |
| if len(token) < 3: | |
| return None | |
| if token[0] != "$" or token[1] != "(" or token[len(token) - 1] != ")": | |
| return None | |
| unexpanded_var = token[2:len(token) - 1] | |
| expanded_var = _expand_nested_variable(ctx, additional_make_variable_substitutions, unexpanded_var) | |
| return expanded_var | |
| def _defines_attribute(ctx, additional_make_variable_substitutions, attr_name, additional_targets): | |
| defines = getattr(ctx.attr, attr_name, []) | |
| if len(defines) == 0: | |
| return [] | |
| targets = list(additional_targets) | |
| for dep in ctx.attr.deps: | |
| targets.append(dep) | |
| result = [] | |
| for define in defines: | |
| expanded_define = _expand(ctx, define, additional_make_variable_substitutions, targets = targets) | |
| tokens = [] | |
| _tokenize(tokens, expanded_define) | |
| if len(tokens) == 1: | |
| result.append(tokens[0]) | |
| elif len(tokens) == 0: | |
| fail("empty definition not allowed", attr = attr_name) | |
| else: | |
| fail("definition contains too many tokens (found {}, expecting exactly one)".format(len(tokens)), attr = attr_name) | |
| return result | |
| def _defines(ctx, additional_make_variable_substitutions): | |
| return _defines_attribute(ctx, additional_make_variable_substitutions, "defines", []) | |
| def _local_defines(ctx, additional_make_variable_substitutions): | |
| return _defines_attribute(ctx, additional_make_variable_substitutions, "local_defines", getattr(ctx.attr, "additional_compiler_inputs", [])) | |
| def _calculate_artifact_label_map(attr_list, attr_name): | |
| """ | |
| Converts a label_list attribute into 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). | |
| """ | |
| artifact_label_map = {} | |
| for attr in attr_list: | |
| if DefaultInfo in attr: | |
| for artifact in attr[DefaultInfo].files.to_list(): | |
| if "." + artifact.extension not in extensions.CC_HEADER: | |
| old_label = artifact_label_map.get(artifact, None) | |
| artifact_label_map[artifact] = attr.label | |
| if old_label != None and old_label != attr.label and ( | |
| "." + artifact.extension in extensions.CC_AND_OBJC or attr_name == "module_interfaces" | |
| ): | |
| fail( | |
| "Artifact '{}' is duplicated (through '{}' and '{}')".format(artifact, old_label, attr), | |
| attr = attr_name, | |
| ) | |
| return artifact_label_map | |
| def _get_srcs(ctx): | |
| if not hasattr(ctx.attr, "srcs"): | |
| return [] | |
| artifact_label_map = _calculate_artifact_label_map(ctx.attr.srcs, "srcs") | |
| return artifact_label_map.items() | |
| def _get_cpp_module_interfaces(ctx): | |
| if not hasattr(ctx.attr, "module_interfaces"): | |
| return [] | |
| artifact_label_map = _calculate_artifact_label_map(ctx.attr.module_interfaces, "module_interfaces") | |
| return artifact_label_map.items() | |
| # 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_private_hdrs(ctx): | |
| if not hasattr(ctx.attr, "srcs"): | |
| return [] | |
| artifact_label_map = {} | |
| for src in ctx.attr.srcs: | |
| if DefaultInfo in src: | |
| for artifact in src[DefaultInfo].files.to_list(): | |
| if "." + artifact.extension in extensions.CC_HEADER: | |
| artifact_label_map[artifact] = src.label | |
| return artifact_label_map.items() | |
| # Returns the files from headers and does some checks. | |
| def _get_public_hdrs(ctx): | |
| if not hasattr(ctx.attr, "hdrs"): | |
| return [] | |
| artifact_label_map = {} | |
| for hdr in ctx.attr.hdrs: | |
| if DefaultInfo in hdr: | |
| for artifact in hdr[DefaultInfo].files.to_list(): | |
| if _check_file_extension(artifact, extensions.DISALLOWED_HDRS_FILES, True): | |
| continue | |
| artifact_label_map[artifact] = hdr.label | |
| return artifact_label_map.items() | |
| def _linkopts(ctx, additional_make_variable_substitutions, cc_toolchain): | |
| linkopts = getattr(ctx.attr, "linkopts", []) | |
| if len(linkopts) == 0: | |
| return [] | |
| targets = [] | |
| for additional_linker_input in getattr(ctx.attr, "additional_linker_inputs", []): | |
| targets.append(additional_linker_input) | |
| tokens = [] | |
| for linkopt in linkopts: | |
| expanded_linkopt = _expand(ctx, linkopt, additional_make_variable_substitutions, targets = targets) | |
| _tokenize(tokens, expanded_linkopt) | |
| if objc_common.is_apple_platform(cc_toolchain.cpu) and "-static" in tokens: | |
| fail("in linkopts attribute of cc_library rule {}: Apple builds do not support statically linked binaries".format(ctx.label)) | |
| return tokens | |
| def _get_coverage_environment(ctx, cc_config, cc_toolchain, feature_configuration): | |
| if not ctx.configuration.coverage_enabled: | |
| return {} | |
| # buildifier: disable=unsorted-dict-items | |
| env = { | |
| "COVERAGE_GCOV_PATH": _tool_path(cc_toolchain._tool_paths, "gcov", feature_configuration, ACTION_NAMES.gcov, default = None), | |
| "LLVM_COV": _tool_path(cc_toolchain._tool_paths, "llvm-cov", feature_configuration, ACTION_NAMES.llvm_cov, default = None), | |
| "LLVM_PROFDATA": _tool_path(cc_toolchain._tool_paths, "llvm-profdata", feature_configuration, ACTION_NAMES.llvm_profdata, default = None), | |
| "GENERATE_LLVM_LCOV": "1" if cc_config.generate_llvm_lcov() else "0", | |
| } | |
| for k in list(env.keys()): | |
| if env[k] == None: | |
| env.pop(k) | |
| if cc_config.fdo_instrument(): | |
| env["FDO_DIR"] = cc_config.fdo_instrument() | |
| return env | |
| def _create_cc_instrumented_files_info(ctx, cc_config, cc_toolchain, feature_configuration, metadata_files, virtual_to_original_headers = None): | |
| source_extensions = extensions.CC_SOURCE + \ | |
| extensions.C_SOURCE + \ | |
| extensions.CC_HEADER + \ | |
| extensions.ASSEMBLER_WITH_C_PREPROCESSOR + \ | |
| extensions.ASSEMBLER | |
| coverage_environment = {} | |
| if ctx.configuration.coverage_enabled: | |
| coverage_environment = _get_coverage_environment(ctx, cc_config, cc_toolchain, feature_configuration) | |
| coverage_support_files = cc_toolchain._coverage_files if ctx.configuration.coverage_enabled else depset([]) | |
| info = coverage_common.instrumented_files_info( | |
| ctx = ctx, | |
| source_attributes = ["srcs", "hdrs"], | |
| dependency_attributes = ["implementation_deps", "deps", "data"], | |
| extensions = source_extensions, | |
| metadata_files = metadata_files, | |
| coverage_support_files = coverage_support_files, | |
| coverage_environment = coverage_environment, | |
| reported_to_actual_sources = virtual_to_original_headers, | |
| ) | |
| return info | |
| _RUNFILES_LIBRARY_TARGET = Label("@rules_cc//cc/runfiles") | |
| _LEGACY_RUNFILES_LIBRARY_TARGET = Label("@bazel_tools//tools/cpp/runfiles") | |
| def _get_local_defines_for_runfiles_lookup(ctx, all_deps): | |
| for dep in all_deps: | |
| if dep.label == _RUNFILES_LIBRARY_TARGET or dep.label == _LEGACY_RUNFILES_LIBRARY_TARGET: | |
| return ["BAZEL_CURRENT_REPOSITORY=\"{}\"".format(ctx.label.workspace_name)] | |
| return [] | |
| def _linker_scripts(ctx): | |
| result = [] | |
| for dep in ctx.attr.deps: | |
| for f in dep[DefaultInfo].files.to_list(): | |
| if f.extension in cpp_file_types.LINKER_SCRIPT: | |
| result.append(f) | |
| return result | |
| def _has_target_constraints(ctx, constraints): | |
| # Constraints is a label_list. | |
| for constraint in constraints: | |
| constraint_value = constraint[platform_common.ConstraintValueInfo] | |
| if ctx.target_platform_has_constraint(constraint_value): | |
| return True | |
| return False | |
| def _should_create_test_dwp_for_statically_linked_test(is_test, linking_mode, cpp_config): | |
| return is_test and linking_mode != linker_mode.LINKING_DYNAMIC and cpp_config.build_test_dwp() | |
| cc_helper = struct( | |
| rule_error = _rule_error, | |
| attribute_error = _attribute_error, | |
| create_strip_action = _create_strip_action, | |
| get_expanded_env = _get_expanded_env, | |
| get_static_mode_params_for_dynamic_library_libraries = _get_static_mode_params_for_dynamic_library_libraries, | |
| should_use_pic = _should_use_pic, | |
| tokenize = _tokenize, | |
| is_valid_shared_library_artifact = _is_valid_shared_library_artifact, | |
| is_valid_shared_library_name = _is_valid_shared_library_name, | |
| tool_path = _tool_path, | |
| get_toolchain_global_make_variables = _get_toolchain_global_make_variables, | |
| get_cc_flags_make_variable = _get_cc_flags_make_variable, | |
| get_compilation_contexts_from_deps = _get_compilation_contexts_from_deps, | |
| include_dirs = _include_dirs, | |
| system_include_dirs = _include_dirs, # TODO: Remove uses of old name | |
| stringify_linker_input = _stringify_linker_input, | |
| generate_def_file = _generate_def_file, | |
| get_windows_def_file_for_linking = _get_windows_def_file_for_linking, | |
| check_file_extensions = _check_file_extensions, | |
| check_srcs_extensions = _check_srcs_extensions, | |
| libraries_from_linking_context = _libraries_from_linking_context, | |
| report_invalid_options = _report_invalid_options, | |
| check_cpp_modules = _check_cpp_modules, | |
| build_precompiled_files = _build_precompiled_files, | |
| get_copts = _get_copts, | |
| defines = _defines, | |
| local_defines = _local_defines, | |
| get_srcs = _get_srcs, | |
| get_cpp_module_interfaces = _get_cpp_module_interfaces, | |
| get_private_hdrs = _get_private_hdrs, | |
| get_public_hdrs = _get_public_hdrs, | |
| is_code_coverage_enabled = _is_code_coverage_enabled, | |
| is_compilation_outputs_empty = _is_compilation_outputs_empty, | |
| get_linking_contexts_from_deps = _get_linking_contexts_from_deps, | |
| dll_hash_suffix = _dll_hash_suffix, | |
| linkopts = _linkopts, | |
| build_linking_context_from_libraries = _build_linking_context_from_libraries, | |
| collect_native_cc_libraries = _collect_native_cc_libraries, | |
| get_coverage_environment = _get_coverage_environment, | |
| create_cc_instrumented_files_info = _create_cc_instrumented_files_info, | |
| get_dynamic_libraries_for_runtime = _get_dynamic_libraries_for_runtime, | |
| create_dynamic_libraries_copy_actions = _create_dynamic_libraries_copy_actions, | |
| build_output_groups_for_emitting_compile_providers = _build_output_groups_for_emitting_compile_providers, | |
| merge_cc_debug_contexts = _merge_cc_debug_contexts, | |
| get_providers = _get_providers, | |
| merge_output_groups = _merge_output_groups, | |
| extensions = extensions, | |
| get_local_defines_for_runfiles_lookup = _get_local_defines_for_runfiles_lookup, | |
| linker_scripts = _linker_scripts, | |
| is_stamping_enabled = _is_stamping_enabled, | |
| should_stamp = _should_stamp, | |
| is_test_target = _is_test_target, | |
| get_linked_artifact = _get_linked_artifact, | |
| should_create_per_object_debug_info = should_create_per_object_debug_info, | |
| has_target_constraints = _has_target_constraints, | |
| package_exec_path = _package_exec_path, | |
| should_create_test_dwp_for_statically_linked_test = _should_create_test_dwp_for_statically_linked_test, | |
| get_cc_runtimes = _get_cc_runtimes, | |
| get_cc_runtimes_copts = _get_cc_runtimes_copts, | |
| ) |