""" Defines create_linking_info, which wraps passed libraries into CcLinkingInfo """ load("@bazel_skylib//lib:collections.bzl", "collections") load( "@bazel_tools//tools/build_defs/cc:action_names.bzl", "ASSEMBLE_ACTION_NAME", "CPP_COMPILE_ACTION_NAME", "CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME", "CPP_LINK_EXECUTABLE_ACTION_NAME", "CPP_LINK_STATIC_LIBRARY_ACTION_NAME", "C_COMPILE_ACTION_NAME", ) load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain") LibrariesToLinkInfo = provider( doc = "Libraries to be wrapped into CcLinkingInfo", fields = dict( static_libraries = "Static library files, optional", shared_libraries = "Shared library files, optional", interface_libraries = "Interface library files, optional", ), ) CxxToolsInfo = provider( doc = "Paths to the C/C++ tools, taken from the toolchain", fields = dict( cc = "C compiler", cxx = "C++ compiler", cxx_linker_static = "C++ linker to link static library", cxx_linker_executable = "C++ linker to link executable", ), ) CxxFlagsInfo = provider( doc = "Flags for the C/C++ tools, taken from the toolchain", fields = dict( cc = "C compiler flags", cxx = "C++ compiler flags", cxx_linker_shared = "C++ linker flags when linking shared library", cxx_linker_static = "C++ linker flags when linking static library", cxx_linker_executable = "C++ linker flags when linking executable", assemble = "Assemble flags", ), ) def _to_list(element): if element == None: return [] else: return [element] def _to_depset(element): if element == None: return depset() return depset(element) def _configure_features(ctx, cc_toolchain): return cc_common.configure_features( ctx = ctx, cc_toolchain = cc_toolchain, requested_features = ctx.features, unsupported_features = ctx.disabled_features, ) def _create_libraries_to_link(ctx, files): libs = [] static_map = _files_map(_filter(files.static_libraries or [], _is_position_independent, True)) pic_static_map = _files_map(_filter(files.static_libraries or [], _is_position_independent, False)) shared_map = _files_map(files.shared_libraries or []) interface_map = _files_map(files.interface_libraries or []) names = collections.uniq(static_map.keys() + pic_static_map.keys() + shared_map.keys() + interface_map.keys()) cc_toolchain = find_cpp_toolchain(ctx) feature_configuration = _configure_features( ctx = ctx, cc_toolchain = cc_toolchain, ) for name_ in names: libs.append(cc_common.create_library_to_link( actions = ctx.actions, feature_configuration = feature_configuration, cc_toolchain = cc_toolchain, static_library = static_map.get(name_), pic_static_library = pic_static_map.get(name_), dynamic_library = shared_map.get(name_), interface_library = interface_map.get(name_), alwayslink = ctx.attr.alwayslink, )) return depset(direct = libs) def _is_position_independent(file): return file.basename.endswith(".pic.a") def _filter(list_, predicate, inverse): result = [] for elem in list_: check = predicate(elem) if not inverse and check or inverse and not check: result.append(elem) return result def _files_map(files_list): by_names_map = {} for file_ in files_list: name_ = _file_name_no_ext(file_.basename) value = by_names_map.get(name_) if value: fail("Can not have libraries with the same name in the same category") by_names_map[name_] = file_ return by_names_map def _defines_from_deps(ctx): return depset(transitive = [dep[CcInfo].compilation_context.defines for dep in ctx.attr.deps]) def _build_cc_link_params( ctx, user_link_flags, static_libraries, dynamic_libraries, runtime_artifacts): static_shared = None static_no_shared = None if static_libraries != None and len(static_libraries) > 0: static_shared = cc_common.create_cc_link_params( ctx = ctx, user_link_flags = user_link_flags, libraries_to_link = _to_depset(static_libraries), ) static_no_shared = cc_common.create_cc_link_params( ctx = ctx, libraries_to_link = _to_depset(static_libraries), ) else: static_shared = cc_common.create_cc_link_params( ctx = ctx, user_link_flags = user_link_flags, libraries_to_link = _to_depset(dynamic_libraries), dynamic_libraries_for_runtime = _to_depset(runtime_artifacts), ) static_no_shared = cc_common.create_cc_link_params( ctx = ctx, libraries_to_link = _to_depset(dynamic_libraries), dynamic_libraries_for_runtime = _to_depset(runtime_artifacts), ) no_static_shared = None no_static_no_shared = None if dynamic_libraries != None and len(dynamic_libraries) > 0: no_static_shared = cc_common.create_cc_link_params( ctx = ctx, user_link_flags = user_link_flags, libraries_to_link = _to_depset(dynamic_libraries), dynamic_libraries_for_runtime = _to_depset(runtime_artifacts), ) no_static_no_shared = cc_common.create_cc_link_params( ctx = ctx, libraries_to_link = _to_depset(dynamic_libraries), dynamic_libraries_for_runtime = _to_depset(runtime_artifacts), ) else: no_static_shared = cc_common.create_cc_link_params( ctx = ctx, user_link_flags = user_link_flags, libraries_to_link = _to_depset(static_libraries), ) no_static_no_shared = cc_common.create_cc_link_params( ctx = ctx, libraries_to_link = _to_depset(static_libraries), ) return { "dynamic_mode_params_for_dynamic_library": no_static_shared, "dynamic_mode_params_for_executable": no_static_no_shared, "static_mode_params_for_dynamic_library": static_shared, "static_mode_params_for_executable": static_no_shared, } def targets_windows(ctx, cc_toolchain): """Returns true if build is targeting Windows Args: ctx: rule context cc_toolchain: optional - Cc toolchain """ toolchain = cc_toolchain if cc_toolchain else find_cpp_toolchain(ctx) feature_configuration = _configure_features( ctx = ctx, cc_toolchain = toolchain, ) return cc_common.is_enabled( feature_configuration = feature_configuration, feature_name = "targets_windows", ) def create_linking_info(ctx, user_link_flags, files): """Creates CcLinkingInfo for the passed user link options and libraries. Args: ctx (ctx): rule context user_link_flags (list of strings): link optins, provided by user files (LibrariesToLink): provider with the library files """ return cc_common.create_linking_context( linker_inputs = depset(direct = [ cc_common.create_linker_input( owner = ctx.label, libraries = _create_libraries_to_link(ctx, files), user_link_flags = depset(direct = user_link_flags), ), ]), ) # buildifier: disable=function-docstring def get_env_vars(ctx): cc_toolchain = find_cpp_toolchain(ctx) feature_configuration = _configure_features( ctx = ctx, cc_toolchain = cc_toolchain, ) copts = ctx.attr.copts if hasattr(ctx.attr, "copts") else [] vars = dict() for action_name in [C_COMPILE_ACTION_NAME, CPP_LINK_STATIC_LIBRARY_ACTION_NAME, CPP_LINK_EXECUTABLE_ACTION_NAME]: vars.update(cc_common.get_environment_variables( feature_configuration = feature_configuration, action_name = action_name, variables = cc_common.create_compile_variables( feature_configuration = feature_configuration, cc_toolchain = cc_toolchain, user_compile_flags = copts, ), )) return vars def is_debug_mode(ctx): # Compilation mode currently defaults to fastbuild. Use that if for some reason the variable is not set # https://docs.bazel.build/versions/master/command-line-reference.html#flag--compilation_mode return ctx.var.get("COMPILATION_MODE", "fastbuild") == "dbg" def get_tools_info(ctx): """Takes information about tools paths from cc_toolchain, returns CxxToolsInfo Args: ctx: rule context """ cc_toolchain = find_cpp_toolchain(ctx) feature_configuration = _configure_features( ctx = ctx, cc_toolchain = cc_toolchain, ) return CxxToolsInfo( cc = cc_common.get_tool_for_action( feature_configuration = feature_configuration, action_name = C_COMPILE_ACTION_NAME, ), cxx = cc_common.get_tool_for_action( feature_configuration = feature_configuration, action_name = CPP_COMPILE_ACTION_NAME, ), cxx_linker_static = cc_common.get_tool_for_action( feature_configuration = feature_configuration, action_name = CPP_LINK_STATIC_LIBRARY_ACTION_NAME, ), cxx_linker_executable = cc_common.get_tool_for_action( feature_configuration = feature_configuration, action_name = CPP_LINK_EXECUTABLE_ACTION_NAME, ), ) def get_flags_info(ctx, link_output_file = None): """Takes information about flags from cc_toolchain, returns CxxFlagsInfo Args: ctx: rule context link_output_file: output file to be specified in the link command line flags Returns: CxxFlagsInfo: A provider containing Cxx flags """ cc_toolchain_ = find_cpp_toolchain(ctx) feature_configuration = _configure_features( ctx = ctx, cc_toolchain = cc_toolchain_, ) copts = (ctx.fragments.cpp.copts + ctx.fragments.cpp.conlyopts) or [] cxxopts = (ctx.fragments.cpp.copts + ctx.fragments.cpp.cxxopts) or [] linkopts = ctx.fragments.cpp.linkopts or [] defines = _defines_from_deps(ctx) flags = CxxFlagsInfo( cc = cc_common.get_memory_inefficient_command_line( feature_configuration = feature_configuration, action_name = C_COMPILE_ACTION_NAME, variables = cc_common.create_compile_variables( feature_configuration = feature_configuration, cc_toolchain = cc_toolchain_, preprocessor_defines = defines, ), ), cxx = cc_common.get_memory_inefficient_command_line( feature_configuration = feature_configuration, action_name = CPP_COMPILE_ACTION_NAME, variables = cc_common.create_compile_variables( feature_configuration = feature_configuration, cc_toolchain = cc_toolchain_, preprocessor_defines = defines, add_legacy_cxx_options = True, ), ), cxx_linker_shared = cc_common.get_memory_inefficient_command_line( feature_configuration = feature_configuration, action_name = CPP_LINK_DYNAMIC_LIBRARY_ACTION_NAME, variables = cc_common.create_link_variables( cc_toolchain = cc_toolchain_, feature_configuration = feature_configuration, is_using_linker = True, is_linking_dynamic_library = True, ), ), cxx_linker_static = cc_common.get_memory_inefficient_command_line( feature_configuration = feature_configuration, action_name = CPP_LINK_STATIC_LIBRARY_ACTION_NAME, variables = cc_common.create_link_variables( cc_toolchain = cc_toolchain_, feature_configuration = feature_configuration, is_using_linker = False, is_linking_dynamic_library = False, output_file = link_output_file, ), ), cxx_linker_executable = cc_common.get_memory_inefficient_command_line( feature_configuration = feature_configuration, action_name = CPP_LINK_EXECUTABLE_ACTION_NAME, variables = cc_common.create_link_variables( cc_toolchain = cc_toolchain_, feature_configuration = feature_configuration, is_using_linker = True, is_linking_dynamic_library = False, ), ), assemble = cc_common.get_memory_inefficient_command_line( feature_configuration = feature_configuration, action_name = ASSEMBLE_ACTION_NAME, variables = cc_common.create_compile_variables( feature_configuration = feature_configuration, cc_toolchain = cc_toolchain_, preprocessor_defines = defines, ), ), ) return CxxFlagsInfo( cc = _add_if_needed(flags.cc, copts), cxx = _add_if_needed(flags.cxx, cxxopts), cxx_linker_shared = _add_if_needed(flags.cxx_linker_shared, linkopts), cxx_linker_static = flags.cxx_linker_static, cxx_linker_executable = _add_if_needed(flags.cxx_linker_executable, linkopts), assemble = _add_if_needed(flags.assemble, copts), ) def _add_if_needed(arr, add_arr): filtered = [] for to_add in add_arr: found = False for existing in arr: if existing == to_add: found = True if not found: filtered.append(to_add) return arr + filtered def absolutize_path_in_str(workspace_name, root_str, text, force = False): """Replaces relative paths in [the middle of] 'text', prepending them with 'root_str'. If there is nothing to replace, returns the 'text'. We only will replace relative paths starting with either 'external/' or '/', because we only want to point with absolute paths to external repositories or inside our current workspace. (And also to limit the possibility of error with such not exact replacing.) Args: workspace_name: workspace name text: the text to do replacement in root_str: the text to prepend to the found relative path force: If true, the `root_str` will always be prepended Returns: string: A formatted string """ new_text = _prefix(text, "external/", root_str) if new_text == text: new_text = _prefix(text, workspace_name + "/", root_str) # absolutize relative by adding our working directory # this works because we ru on windows under msys now if force and new_text == text and not text.startswith("/"): new_text = root_str + "/" + text return new_text def _prefix(text, from_str, prefix): text = text.replace('"', '\\"') (before, middle, after) = text.partition(from_str) if not middle or before.endswith("/"): return text return before + prefix + middle + after def _file_name_no_ext(basename): (before, separator, after) = basename.rpartition(".") return before