Skip to main content
Glama
link.bzl8.26 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_library_utility.bzl", "cxx_inherited_link_info") load( "@prelude//cxx:cxx_link_utility.bzl", "ExecutableSharedLibArguments", "executable_shared_lib_arguments", "make_link_args", ) load("@prelude//cxx:cxx_toolchain_types.bzl", "CxxToolchainInfo") load("@prelude//cxx:linker.bzl", "get_default_shared_library_name", "get_shared_library_name_linker_flags") load( "@prelude//linking:link_info.bzl", "LinkStyle", "get_link_args_for_strategy", "to_link_strategy", ) load( "@prelude//linking:shared_libraries.bzl", "SharedLibraryInfo", "merge_shared_libraries", "traverse_shared_library_info", ) load("@prelude//linking:stamp_build_info.bzl", "stamp_build_info") load("@prelude//os_lookup:defs.bzl", "Os", "OsLookup") load( "@prelude//utils:utils.bzl", "filter_and_map_idx", ) load( ":packages.bzl", "GoPkg", # @Unused used as type "make_importcfg", "merge_pkgs", ) load(":toolchain.bzl", "GoToolchainInfo", "get_toolchain_env_vars") # Provider wrapping packages used for linking. GoPkgLinkInfo = provider(fields = { "pkgs": provider_field(typing.Any, default = None), # {str: "artifact"} }) GoBuildMode = enum( "executable", # non-pic executable "c_shared", # pic C-shared library "c_archive", # pic C-static library ) def _build_mode_param(mode: GoBuildMode) -> str: if mode == GoBuildMode("executable"): return "exe" if mode == GoBuildMode("c_shared"): return "c-shared" if mode == GoBuildMode("c_archive"): return "c-archive" fail("unexpected: {}", mode) def get_inherited_link_pkgs(deps: list[Dependency]) -> dict[str, GoPkg]: return merge_pkgs([d[GoPkgLinkInfo].pkgs for d in deps if GoPkgLinkInfo in d]) # TODO(cjhopman): Is link_style a LibOutputStyle or a LinkStrategy here? Based # on returning an empty thing for link_style != shared, it seems likely its # intended to be LibOutputStyle, but it's called in places that are passing what # appears to be a LinkStrategy. def _process_shared_dependencies( ctx: AnalysisContext, artifact: Artifact, deps: list[Dependency], link_style: LinkStyle) -> ExecutableSharedLibArguments: """ Provides files and linker args needed to for binaries with shared library linkage. - the runtime files needed to run binary linked with shared libraries - linker arguments for shared libraries """ if link_style != LinkStyle("shared"): return ExecutableSharedLibArguments() shlib_info = merge_shared_libraries( ctx.actions, deps = filter_and_map_idx(SharedLibraryInfo, deps), ) shared_libs = traverse_shared_library_info(shlib_info) return executable_shared_lib_arguments( ctx, ctx.attrs._cxx_toolchain[CxxToolchainInfo], artifact, shared_libs, ) def link( ctx: AnalysisContext, main: GoPkg, pkgs: dict[str, GoPkg] = {}, deps: list[Dependency] = [], build_mode: GoBuildMode = GoBuildMode("executable"), link_mode: [str, None] = None, link_style: LinkStyle = LinkStyle("static"), linker_flags: list[typing.Any] = [], external_linker_flags: list[typing.Any] = [], race: bool = False, asan: bool = False): go_toolchain = ctx.attrs._go_toolchain[GoToolchainInfo] if go_toolchain.env_go_os == "windows": executable_extension = ".exe" shared_extension = ".dll" archive_extension = ".lib" else: executable_extension = "" shared_extension = ".so" archive_extension = ".a" if build_mode == GoBuildMode("c_shared"): file_extension = shared_extension use_shared_code = True # PIC link_style = LinkStyle("shared") elif build_mode == GoBuildMode("c_archive"): file_extension = archive_extension use_shared_code = True # PIC link_style = LinkStyle("static_pic") else: # GoBuildMode("executable") file_extension = executable_extension use_shared_code = False # non-PIC final_output_name = ctx.label.name + file_extension output = ctx.actions.declare_output(ctx.label.name + "-tmp" + file_extension) cmd = cmd_args() cmd.add(go_toolchain.linker) cmd.add(go_toolchain.linker_flags) cmd.add("-o", output.as_output()) cmd.add("-buildmode=" + _build_mode_param(build_mode)) cmd.add("-buildid=") # Setting to a static buildid helps make the binary reproducible. if race: cmd.add("-race") if asan: cmd.add("-asan") # Add inherited Go pkgs to library search path. all_pkgs = merge_pkgs([ pkgs, get_inherited_link_pkgs(deps), ]) identifier_prefix = ctx.label.name + "_" + _build_mode_param(build_mode) importcfg = make_importcfg(ctx, identifier_prefix, all_pkgs, use_shared_code) cmd.add("-importcfg", importcfg) executable_args = _process_shared_dependencies(ctx, output, deps, link_style) if link_mode == None: if build_mode == GoBuildMode("c_shared"): link_mode = "external" if build_mode == GoBuildMode("c_archive"): link_mode = "external" if link_mode != None: cmd.add("-linkmode", link_mode) cxx_toolchain_available = CxxToolchainInfo in ctx.attrs._cxx_toolchain if cxx_toolchain_available: cxx_toolchain = ctx.attrs._cxx_toolchain[CxxToolchainInfo] is_win = ctx.attrs._exec_os_type[OsLookup].os == Os("windows") # Gather external link args from deps. ext_links = get_link_args_for_strategy(ctx, cxx_inherited_link_info(deps), to_link_strategy(link_style)) ext_link_args_output = make_link_args( ctx, ctx.actions, cxx_toolchain, [ext_links], ) ext_link_args = cmd_args(hidden = ext_link_args_output.hidden) ext_link_args.add(cmd_args(executable_args.extra_link_args, quote = "shell")) ext_link_args.add(external_linker_flags) ext_link_args.add(ext_link_args_output.link_args) if build_mode == GoBuildMode("c_shared") and go_toolchain.env_go_os != "windows": soname = get_default_shared_library_name(cxx_toolchain.linker_info, ctx.label) soname_flags = get_shared_library_name_linker_flags(cxx_toolchain.linker_info.type, soname) ext_link_args.add(soname_flags) # Delegate to C++ linker... # TODO: It feels a bit inefficient to generate a wrapper file for every # link. Is there some way to etract the first arg of `RunInfo`? Or maybe # we can generate the platform-specific stuff once and re-use? cxx_link_cmd = cmd_args( [ cxx_toolchain.linker_info.linker, ext_link_args, "%*" if is_win else "\"$@\"", ], delimiter = " ", ) linker_wrapper, _ = ctx.actions.write( "__{}_cxx_link_wrapper__.{}".format(identifier_prefix, "bat" if is_win else "sh"), ([] if is_win else ["#!/bin/sh"]) + [cxx_link_cmd], allow_args = True, is_executable = True, ) cmd.add("-extld", linker_wrapper, cmd_args(hidden = cxx_link_cmd)) cmd.add("-extldflags", cmd_args( cxx_toolchain.linker_info.linker_flags, go_toolchain.external_linker_flags, delimiter = " ", quote = "shell", )) cmd.add(linker_flags) cmd.add(main.pkg_shared if use_shared_code else main.pkg) env = get_toolchain_env_vars(go_toolchain) ctx.actions.run(cmd, env = env, category = "go_link", identifier = identifier_prefix) output = stamp_build_info(ctx, output) final_output = ctx.actions.copy_file(final_output_name, output) return (final_output, executable_args.runtime_files, executable_args.external_debug_info)

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