Skip to main content
Glama
prebuilt_cxx_library_group.bzl13.9 kB
# Copyright (c) Meta Platforms, Inc. and affiliates. # # This source code is licensed under both the MIT license found in the # LICENSE-MIT file in the root directory of this source tree and the Apache # License, Version 2.0 found in the LICENSE-APACHE file in the root directory # of this source tree. load( "@prelude//cxx:cxx_toolchain_types.bzl", "LinkerType", "PicBehavior", ) load( "@prelude//cxx:preprocessor.bzl", "CPreprocessor", "CPreprocessorArgs", "cxx_inherited_preprocessor_infos", "cxx_merge_cpreprocessors", ) load( "@prelude//linking:link_groups.bzl", "merge_link_group_lib_info", ) load( "@prelude//linking:link_info.bzl", "Archive", "ArchiveLinkable", "LibOutputStyle", "LinkInfo", "LinkInfos", "LinkStrategy", "LinkedObject", "SharedLibLinkable", "create_merged_link_info", "get_lib_output_style", "get_output_styles_for_linkage", ) load( "@prelude//linking:linkable_graph.bzl", "create_linkable_graph", "create_linkable_graph_node", "create_linkable_node", ) load( "@prelude//linking:shared_libraries.bzl", "SharedLibraryInfo", "create_shared_libraries", "merge_shared_libraries", ) load("@prelude//linking:strip.bzl", "strip_debug_info") load("@prelude//linking:types.bzl", "Linkage") load("@prelude//unix:providers.bzl", "UnixEnv", "create_unix_env_info") load("@prelude//utils:expect.bzl", "expect") load("@prelude//utils:utils.bzl", "flatten_dict") load(":cxx_context.bzl", "get_cxx_toolchain_info") load( ":cxx_library_utility.bzl", "cxx_attr_dep_metadata", "cxx_inherited_link_info", "cxx_use_shlib_intfs", ) load( ":shared_library_interface.bzl", "shared_library_interface", ) def _linkage(ctx: AnalysisContext) -> Linkage: """ Construct the preferred linkage to use for the given prebuilt library. """ # If we have both shared and static libs, we support any linkage. if (ctx.attrs.shared_link and (ctx.attrs.static_link or ctx.attrs.static_pic_link)): return Linkage("any") # Otherwise, if we have a shared library, we only support shared linkage. if ctx.attrs.shared_link: return Linkage("shared") # Otherwise, if we have a static library, we only support static linkage. if ctx.attrs.static_link or ctx.attrs.static_pic_link: return Linkage("static") # Otherwise, header only libs use any linkage. return Linkage("any") def _parse_macro(arg: str) -> [(str, str), None]: """ Parse a lib reference macro (e.g. `$(lib 0)`, `$(rel-lib libfoo.so)`) into the format string used to format the arg, the name of the macro parsed, and the argument passed to the macro. """ # TODO(T110378124): This is obviously not ideal and longer-term we should # probably come up with a better UI for this rule or properly support these # macros. # If there's not macro, then there's nothing to do. if "$(" not in arg: return None # Extract the macro name and it's arg out of the string. Also, create a # format string with the remaining parts which can be used to format to an # actual arg. This is pretty ugly, but we don't have too complex a case to # support (e.g. a single macro with a single arg). start, rest = arg.split("$(") expect(start == "") pos = rest.find(" ") macro = rest[:pos] rest = rest[pos + 1:] pos = rest.find(")") param = rest[:pos] end = rest[pos + 1:] expect(end == "") return macro, param def _get_static_link_infos( ctx: AnalysisContext, linker_type: LinkerType, libs: list[Artifact], args: list[str]) -> LinkInfos: """ Format a pair of static link string args and static libs into args to be passed to the link, by resolving macro references to libraries. """ def archive_linkable(artifact): return ArchiveLinkable( archive = Archive(artifact = artifact), linker_type = linker_type, # We assume prebuilt C/C++ libs don't contain LTO code and # avoid potentially expensive processing of the to support # dist LTO. In additional, some prebuilt library groups # use `--start-group`/`--end-group` which breaks with our # dist LTO impl wrapping w/ `--start-lib`. supports_lto = False, ) pre_flags = [] post_flags = [] linkables = [] linkables_stripped = [] for arg in args: res = _parse_macro(arg) if res != None: # We require that link lines are written such that link flags wrap # linkables. So verify that we haven't already seen post linker # flags. expect(not post_flags) # Macros in the static link line are indexes to the list of static # archives. macro, param = res expect(macro == "lib") lib = libs[int(param)] linkables.append(archive_linkable(lib)) linkables_stripped.append(archive_linkable(strip_debug_info(ctx, lib.short_path, lib, anonymous = True))) elif linkables: # If we've already seen linkables, put remaining flags/args into # post-linker flags. post_flags.append(arg) else: pre_flags.append(arg) return LinkInfos( default = LinkInfo( pre_flags = pre_flags, post_flags = post_flags, linkables = linkables, metadata = cxx_attr_dep_metadata(ctx), ), stripped = LinkInfo( pre_flags = pre_flags, post_flags = post_flags, linkables = linkables_stripped, metadata = cxx_attr_dep_metadata(ctx), ), ) def _get_shared_link_infos( ctx: AnalysisContext, shared_libs: dict[str, Artifact], args: list[str], shlib_intfs: bool = True) -> LinkInfos: """ Format a pair of shared link string args and shared libs into args to be passed to the link, by resolving macro references to libraries. """ pre_flags = [] post_flags = [] linkables = [] for arg in args: res = _parse_macro(arg) if res != None: # We require that link lines are written such that link flags wrap # linkables. So verify that we haven't already seen post linker # flags. expect(not post_flags) # Macros in the shared link line are named references to the map # of all shared libs. macro, lib_name = res expect(macro in ("lib", "rel-lib")) shared_lib = shared_libs[lib_name] if shlib_intfs: shared_lib = shared_library_interface( ctx = ctx, shared_lib = shared_lib, anonymous = True, ) if macro == "lib": linkables.append(SharedLibLinkable(lib = shared_lib)) elif macro == "rel-lib": # rel-lib means link-without-soname. linkables.append(SharedLibLinkable(lib = shared_lib, link_without_soname = True)) elif linkables: # If we've already seen linkables, put remaining flags/args into # post-linker flags. post_flags.append(arg) else: pre_flags.append(arg) return LinkInfos( default = LinkInfo( pre_flags = pre_flags, post_flags = post_flags, linkables = linkables, metadata = cxx_attr_dep_metadata(ctx), ), ) # The `prebuilt_cxx_library_group` rule is meant to provide fine user control for # how a group libraries of libraries are added to the link line and was added for # `fbcode//third-party-buck/platform009/build/IntelComposerXE:mkl_lp64_iomp`, which # includes libraries with dep cycles, and so must be linked together with flags # like `--start-group`/`--end-group`. # # The link arguments for the various link styles are specified by pair of string # arguments with macros referencing a collection of libraries: # # - For static link styles, the string link args (e.g. specific in `static_link`) # contain macros of the form `$(lib <number>)`, where the number is an index # into the corresponding list of static libraries artifacts (e.g. specified in # `static_libs`). For example: # # static_link = ["-Wl,--start-group", "$(lib 0)", "$(lib 1)", "-Wl,--end-group"], # static_libs = ["libfoo1.a", "libfoo2.a"], # # - For shared linking, the string link args contain macros of the form # `$(lib <name>)` or `$(rel-lib <name>)`, where the name is key for shared # libraries specified in `shared_libs` or `provided_shared_libs`. The # `lib` macro examples to the full path of the shared library, whereas the # `rel-lib` macro expands to `-L<dirname> -l<name>` of the library and is # meant to be used in situations where shared library does not contain an # embedded soname. For example: # # shared_link = ["$(lib libfoo1.so)", "$(rel-lib libfoo2.so)"], # shared_libs = { # "libfoo1.so": "lib/libfoo1.so", # "libfoo2.so": "lib/libfoo2.so", # }, # def prebuilt_cxx_library_group_impl(ctx: AnalysisContext) -> list[Provider]: providers = [] deps = ctx.attrs.deps exported_deps = ctx.attrs.exported_deps # Figure out preprocessor stuff args = [] args.extend(ctx.attrs.exported_preprocessor_flags) for inc_dir in ctx.attrs.include_dirs: args += ["-isystem", inc_dir] preprocessor = CPreprocessor(args = CPreprocessorArgs(args = args)) inherited_pp_info = cxx_inherited_preprocessor_infos(exported_deps) providers.append(cxx_merge_cpreprocessors(ctx, [preprocessor], inherited_pp_info)) # Figure out all the link styles we'll be building archives/shlibs for. preferred_linkage = _linkage(ctx) inherited_non_exported_link = cxx_inherited_link_info(deps) inherited_exported_link = cxx_inherited_link_info(exported_deps) linker_type = get_cxx_toolchain_info(ctx).linker_info.type # Gather link infos, outputs, and shared libs for effective link style. outputs = {} libraries = {} solibs = {} for output_style in get_output_styles_for_linkage(preferred_linkage): outs = [] if output_style == LibOutputStyle("archive"): outs.extend(ctx.attrs.static_libs) infos = _get_static_link_infos( ctx, linker_type, ctx.attrs.static_libs, ctx.attrs.static_link, ) elif output_style == LibOutputStyle("pic_archive"): outs.extend(ctx.attrs.static_pic_libs) infos = _get_static_link_infos( ctx, linker_type, ctx.attrs.static_pic_libs, ctx.attrs.static_pic_link, ) else: # shared outs.extend(ctx.attrs.shared_libs.values()) infos = _get_shared_link_infos( ctx = ctx, shared_libs = flatten_dict([ctx.attrs.shared_libs, ctx.attrs.provided_shared_libs]), args = ctx.attrs.shared_link, shlib_intfs = ctx.attrs.supports_shared_library_interface and cxx_use_shlib_intfs(ctx), ) solibs.update({n: LinkedObject(output = lib, unstripped_output = lib) for n, lib in ctx.attrs.shared_libs.items()}) outputs[output_style] = outs libraries[output_style] = infos # This code is already compiled, so, the argument (probably) has little/no value. pic_behavior = PicBehavior("supported") # prebuilt_cxx_library_group default output is always the output used for the "static" link strategy. static_output_style = get_lib_output_style(LinkStrategy("static"), preferred_linkage, pic_behavior) providers.append(DefaultInfo(default_outputs = outputs[static_output_style])) # Provider for native link. providers.append(create_merged_link_info( ctx, pic_behavior, libraries, preferred_linkage = preferred_linkage, # Export link info from our (non-exported) deps (e.g. when we're linking # statically). deps = inherited_non_exported_link, # Export link info from our (exported) deps. exported_deps = inherited_exported_link, )) # Propagate shared libraries up the tree. shared_libs = create_shared_libraries(ctx, solibs) providers.append(merge_shared_libraries( ctx.actions, shared_libs, filter(None, [x.get(SharedLibraryInfo) for x in deps + exported_deps]), )) # Create, augment and provide the linkable graph. linkable_graph = create_linkable_graph( ctx, node = create_linkable_graph_node( ctx, linkable_node = create_linkable_node( ctx = ctx, deps = deps, exported_deps = exported_deps, preferred_linkage = preferred_linkage, link_infos = libraries, shared_libs = shared_libs, can_be_asset = getattr(ctx.attrs, "can_be_asset", False) or False, # TODO(cjhopman): this should be set to non-None default_soname = None, ), ), deps = deps + exported_deps, ) providers.append(linkable_graph) providers.append(merge_link_group_lib_info(deps = deps + exported_deps)) providers.append( create_unix_env_info( actions = ctx.actions, env = UnixEnv( label = ctx.label, native_libs = [shared_libs], ), deps = deps + exported_deps, ), ) return providers

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/systeminit/si'

If you have feedback or need assistance with the MCP directory API, please join our Discord server