diff --git a/antlir/distro/deps/BUCK b/antlir/distro/deps/BUCK index e36363108a4..65a27da2d4a 100644 --- a/antlir/distro/deps/BUCK +++ b/antlir/distro/deps/BUCK @@ -1,7 +1,10 @@ +# @oss-disable load("//antlir/antlir2/bzl/feature:defs.bzl", "feature") load("//antlir/antlir2/bzl/image:defs.bzl", "image") load("//antlir/antlir2/bzl/package:defs.bzl", "package") load("//antlir/antlir2/image_command_alias:image_command_alias.bzl", "image_command_alias") +load("//antlir/bzl:build_defs.bzl", "rust_binary") +load(":unavailable.bzl", "unavailable") oncall("antlir") @@ -64,3 +67,22 @@ image_command_alias( rootless = True, visibility = ["PUBLIC"], ) + +rust_binary( + name = "projects", + srcs = ["projects.rs"], + # @oss-disable + test_srcs = ["projects.bzl"], + deps = [ + "anyhow", + "serde", + "serde_json", + "serde_starlark", + "//antlir/signedsource:signedsource", + ], +) + +# Generic fallthrough library when antlir doesn't know about a dependency +unavailable( + name = "unknown", +) diff --git a/antlir/distro/deps/projects.bxl b/antlir/distro/deps/projects.bxl new file mode 100644 index 00000000000..f27ee7f2eb9 --- /dev/null +++ b/antlir/distro/deps/projects.bxl @@ -0,0 +1,21 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +def _query_impl(ctx: bxl.Context): + libs = ctx.uquery().kind("alias|cxx_.*", "antlir//antlir/distro/deps/...") + deps = [] + for target in libs: + if target.label.package == "antlir/distro/deps": + # only consider deps inside a sub-project + continue + project = target.label.package.removeprefix("antlir/distro/deps/") + name = target.label.name + deps.append(struct(project = project, name = name)) + ctx.output.print_json(deps) + +query = bxl_main( + impl = _query_impl, + cli_args = {}, +) diff --git a/antlir/distro/deps/projects.bzl b/antlir/distro/deps/projects.bzl new file mode 100644 index 00000000000..aa2a3031bfe --- /dev/null +++ b/antlir/distro/deps/projects.bzl @@ -0,0 +1,14 @@ +# @generated SignedSource<<2e84df60629fb37ec96b032917e29d5b>> +PROJECTS = [ + ("glibc", "c"), + ("glibc", "dl"), + ("glibc", "m"), + ("glibc", "pthread"), + ("jsoncpp", "jsoncpp"), + ("libgcc", "atomic"), + ("libgcc", "gcc_s"), + ("libgcc", "stdc++"), + ("libgcc", "stdc++-legacy"), + ("openmp", "headers"), + ("rpm", "librpm"), +] diff --git a/antlir/distro/deps/projects.rs b/antlir/distro/deps/projects.rs new file mode 100644 index 00000000000..989e40c5d09 --- /dev/null +++ b/antlir/distro/deps/projects.rs @@ -0,0 +1,98 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +use std::path::Path; +use std::path::PathBuf; +use std::process::Command; + +use anyhow::ensure; +use anyhow::Context; +use anyhow::Result; +use serde::ser::SerializeTuple; +use serde::ser::Serializer; +use serde::Deserialize; +use serde::Serialize; +use signedsource::sign_with_generated_header; +use signedsource::Comment; + +#[derive(Debug, Deserialize)] +#[serde(rename = "struct")] +struct Dep { + project: String, + name: String, +} + +impl Serialize for Dep { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut t = serializer.serialize_tuple(2)?; + t.serialize_element(&self.project)?; + t.serialize_element(&self.name)?; + t.end() + } +} + +fn output_path() -> Result { + let out = Command::new("buck2") + .arg("root") + .output() + .context("while running buck2 root")?; + ensure!(out.status.success(), "buck2 root failed"); + let stdout = String::from_utf8(out.stdout).context("buck2 root output not utf8")?; + let root = Path::new(stdout.trim()); + Ok(root.join("antlir/distro/deps/projects.bzl")) +} + +fn gen_bzl(deps: &[Dep]) -> String { + let mut src = String::new(); + src.push_str("PROJECTS = "); + src.push_str(&serde_starlark::to_string(deps).expect("failed to serialize")); + sign_with_generated_header(Comment::Starlark, &src) +} + +fn get_deps() -> Result> { + let out = Command::new("buck2") + .arg("bxl") + .arg("--reuse-current-config") + .arg("antlir//antlir/distro/deps/projects.bxl:query") + .output() + .context("while running bxl")?; + ensure!( + out.status.success(), + "bxl failed: {}", + String::from_utf8_lossy(&out.stderr) + ); + serde_json::from_slice(&out.stdout).context("while deserializing deps") +} + +fn main() -> Result<()> { + let out_path = output_path().context("while determining output path")?; + // before trying to get the dependencies, replace the bzl file with some + // known-good contents (empty) so that this binary can be ergonomically used + // to resolve merge conflicts + std::fs::write(&out_path, gen_bzl(&[])).context("while writing empty bzl")?; + + let deps = get_deps().context("while getting deps")?; + let src = gen_bzl(&deps); + std::fs::write(&out_path, src) + .with_context(|| format!("while writing bzl at {}", out_path.display()))?; + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_is_in_sync() { + let deps = get_deps().expect("failed to get deps"); + let src = gen_bzl(&deps); + assert_eq!(src, include_str!("projects.bzl"),); + } +} diff --git a/antlir/distro/deps/unavailable.bzl b/antlir/distro/deps/unavailable.bzl new file mode 100644 index 00000000000..9cb32bee493 --- /dev/null +++ b/antlir/distro/deps/unavailable.bzl @@ -0,0 +1,36 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +# @oss-disable + +def _unavailable_impl(ctx): + fail(""" + This target is unavailable in the antlir system toolchain. It must be + defined in antlir/distro/deps in order to be usable. + + If you're seeing this after running 'buck2 build $target', try `buck2 cquery 'somepath($target, {})'` + """.format(ctx.label.raw_target())) + +_unavailable = rule( + impl = _unavailable_impl, + attrs = { + "labels": attrs.list(attrs.string(), default = []), + }, +) + +def unavailable(name: str): + """ + To have a working unconfigured buck2 target graph, we need to declare some + libraries as stubs that are not expected to actually be usable but exist to + keep the unconfigured graph happy. + """ + _unavailable( + name = name, + labels = [ + # By definition, this thing won't build, so don't ever let CI try + # @oss-disable + ], + visibility = ["PUBLIC"], + ) diff --git a/ci/test_target_graph.bxl b/ci/test_target_graph.bxl index a5fc1bbfd5f..541241523c2 100644 --- a/ci/test_target_graph.bxl +++ b/ci/test_target_graph.bxl @@ -4,7 +4,7 @@ # LICENSE file in the root directory of this source tree. def _impl(ctx): - targets = ctx.cquery().eval("//...") + targets = ctx.cquery().eval("set(//...) - kind(_unavailable, //...)") ctx.output.print("All BUCK files evaluate")