fwd/vendor/cxx/tools/buck/prelude/rust/link_info.bzl
John Doty 9c435dc440 Vendor dependencies
Let's see how I like this workflow.
2022-12-19 08:38:22 -08:00

165 lines
6.3 KiB
Python

# 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.
# Implementation of the Rust build rules.
load("@prelude//cxx:cxx_toolchain_types.bzl", "CxxPlatformInfo")
load(
"@prelude//linking:link_info.bzl",
"LinkStyle",
"MergedLinkInfo",
"merge_link_infos",
)
load(
"@prelude//linking:shared_libraries.bzl",
"SharedLibraryInfo",
)
load("@prelude//utils:platform_flavors_util.bzl", "by_platform")
load("@prelude//utils:utils.bzl", "flatten")
# Override dylib crates to static_pic, so that Rust code is always
# statically linked.
# In v1 we always linked Rust deps statically, even for "shared" link style
# That shouldn't be necessary, but fully shared needs some more debugging,
# so default to v1 behaviour. (Should be controlled with the `rust.force_rlib` option)
FORCE_RLIB = True
# Output of a Rust compilation
RustLinkInfo = provider(fields = [
# crate - crate name
"crate",
# styles - information about each LinkStyle as RustLinkStyleInfo
# {LinkStyle: RustLinkStyleInfo}
"styles",
# Propagate non-rust native linkable dependencies through rust libraries.
"non_rust_exported_link_deps",
# Propagate non-rust native linkable info through rust libraries.
"non_rust_link_info",
# Propagate non-rust shared libraries through rust libraries.
"non_rust_shared_libs",
])
# Information which is keyed on link_style
RustLinkStyleInfo = record(
# Path to library or binary
rlib = field("artifact"),
# Transitive dependencies which are relevant to consumer
# This is a dict from artifact to None (we don't have sets)
transitive_deps = field({"artifact": None}),
# Path for library metadata (used for check or pipelining)
rmeta = field("artifact"),
# Transitive rmeta deps
transitive_rmeta_deps = field({"artifact": None}),
)
def style_info(info: RustLinkInfo.type, link_style: LinkStyle.type) -> RustLinkStyleInfo.type:
if FORCE_RLIB and link_style == LinkStyle("shared"):
link_style = LinkStyle("static_pic")
return info.styles[link_style]
def cxx_by_platform(ctx: "context", xs: [(str.type, "_a")]) -> "_a":
platform = ctx.attrs._cxx_toolchain[CxxPlatformInfo].name
return flatten(by_platform([platform], xs))
# A Rust dependency
RustDependency = record(
# The actual dependency
dep = field("dependency"),
# The local name, if any (for `named_deps`)
name = field([None, str.type]),
# Any flags for the dependency (`flagged_deps`), which are passed on to rustc.
flags = field([str.type]),
)
# Returns all first-order dependencies, resolving the ones from "platform_deps"
def resolve_deps(ctx: "context") -> [RustDependency.type]:
return [
RustDependency(name = name, dep = dep, flags = flags)
# The `getattr`s are needed for when we're operating on
# `prebuilt_rust_library` rules, which don't have those attrs.
for name, dep, flags in [(None, dep, []) for dep in ctx.attrs.deps + cxx_by_platform(ctx, ctx.attrs.platform_deps)] +
[(name, dep, []) for name, dep in getattr(ctx.attrs, "named_deps", {}).items()] +
[(None, dep, flags) for dep, flags in getattr(ctx.attrs, "flagged_deps", []) +
cxx_by_platform(ctx, getattr(ctx.attrs, "platform_flagged_deps", []))]
]
# Returns native link dependencies.
def _non_rust_link_deps(ctx: "context") -> ["dependency"]:
"""
Return all first-order native linkable dependencies of all transitive Rust
libraries.
This emulates v1's graph walk, where it traverses through Rust libraries
looking for non-Rust native link infos (and terminating the search there).
"""
first_order_deps = [dep.dep for dep in resolve_deps(ctx)]
return [
d
for d in first_order_deps
if RustLinkInfo not in d and MergedLinkInfo in d
]
# Returns native link dependencies.
def _non_rust_link_infos(ctx: "context") -> ["MergedLinkInfo"]:
"""
Return all first-order native link infos of all transitive Rust libraries.
This emulates v1's graph walk, where it traverses through Rust libraries
looking for non-Rust native link infos (and terminating the search there).
MergedLinkInfo is a mapping from link style to all the transitive deps
rolled up in a tset.
"""
return [d[MergedLinkInfo] for d in _non_rust_link_deps(ctx)]
# Returns native link dependencies.
def _non_rust_shared_lib_infos(ctx: "context") -> ["SharedLibraryInfo"]:
"""
Return all transitive shared libraries for non-Rust native linkabes.
This emulates v1's graph walk, where it traverses through -- and ignores --
Rust libraries to collect all transitive shared libraries.
"""
first_order_deps = [dep.dep for dep in resolve_deps(ctx)]
return [
d[SharedLibraryInfo]
for d in first_order_deps
if RustLinkInfo not in d and SharedLibraryInfo in d
]
# Returns native link dependencies.
def _rust_link_infos(ctx: "context") -> ["RustLinkInfo"]:
first_order_deps = resolve_deps(ctx)
return filter(None, [d.dep.get(RustLinkInfo) for d in first_order_deps])
def normalize_crate(label: str.type) -> str.type:
return label.replace("-", "_")
def inherited_non_rust_exported_link_deps(ctx: "context") -> ["dependency"]:
deps = {}
for dep in _non_rust_link_deps(ctx):
deps[dep.label] = dep
for info in _rust_link_infos(ctx):
for dep in info.non_rust_exported_link_deps:
deps[dep.label] = dep
return deps.values()
def inherited_non_rust_link_info(ctx: "context") -> "MergedLinkInfo":
infos = []
infos.extend(_non_rust_link_infos(ctx))
infos.extend([d.non_rust_link_info for d in _rust_link_infos(ctx)])
return merge_link_infos(ctx, infos)
def inherited_non_rust_shared_libs(ctx: "context") -> ["SharedLibraryInfo"]:
infos = []
infos.extend(_non_rust_shared_lib_infos(ctx))
infos.extend([d.non_rust_shared_libs for d in _rust_link_infos(ctx)])
return infos
def attr_crate(ctx: "context") -> str.type:
return ctx.attrs.crate or normalize_crate(ctx.label.name)