blob: 52f6b59053a58ff00938151221b7f9d40cb018b3 [file] [log] [blame]
// Copyright 2024 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::io::Write;
use std::path::Path;
use anyhow::{Context, Result};
use itertools::Itertools;
use crate::{processors::build_event::BuildEventProcessor, proto::ebuild_metadata::EbuildMetadata};
const PREBUILT_HEADER: &str = r#"# Provides the CAS location for all ebuild binary packages
# used by the build. This is useful when modifying primordial code such as
# build_packges, or some of the eclasses.
#
# Place this file at `$WORKSPACE/prebuilts.bzl` (i.e., ~/chromiumos/src/prebuilts.bzl),
# then pass `--config=prebuilts` to your `bazel build` command.
"#;
fn load_metadata(path: &Path) -> Result<EbuildMetadata> {
let file = std::fs::File::open(path).with_context(|| format!("Failed to open {path:?}"))?;
let metadata: EbuildMetadata =
serde_json::from_reader(file).with_context(|| format!("Failed parsing {path:?}"))?;
Ok(metadata)
}
pub fn compute_prebuilts(
output_path: &Path,
workspace_dir: &Path,
processor: &BuildEventProcessor,
) -> Result<()> {
let mut output =
std::fs::File::create(output_path).with_context(|| format!("path: {output_path:?}"))?;
let Some(remote_instance_name) = processor.get_command_flag("remote_instance_name") else {
write!(output, "# --remote_instance_name was not set.")?;
return Ok(());
};
write!(output, "{PREBUILT_HEADER}")?;
let metadata_files = processor.get_output_group_files("ebuild_metadata")?;
let metadata = metadata_files
.into_iter()
.map(|relative_path| workspace_dir.join(relative_path))
.filter_map(|path| match load_metadata(&path) {
Ok(metadata) => Some(Ok(metadata)),
Err(e) => {
// Bazel currently has a bug where some output group files will be missing.
// We just ignore these failures for now.
// https://github.com/bazelbuild/bazel/issues/20737
eprintln!("Failed to load: {path:?}: {e}");
match writeln!(output, "# Failed to load: {path:?}: {e}") {
Ok(_) => None,
Err(e) => Some(Err(e.into())),
}
}
})
.collect::<Result<Vec<_>>>()?;
let args = metadata
.iter()
.sorted_by_key(|metadata| &metadata.label)
.map(|metadata| {
format!(
"--{}_prebuilt=cas://{}/{}/{}",
metadata.label, remote_instance_name, metadata.sha256, metadata.size
)
})
.collect_vec();
let contents = if args.is_empty() {
String::new()
} else {
let mut contents = std::iter::once("build:prebuilts")
.chain(args.iter().map(String::as_str))
.join(" \\\n ");
contents.push('\n');
contents
};
write!(output, "{contents}")?;
Ok(())
}
#[cfg(test)]
mod tests {
use crate::load_build_events_jsonl;
use runfiles::Runfiles;
use std::path::PathBuf;
use tempfile::tempdir;
use super::*;
#[test]
fn basic() -> Result<()> {
let testdata_path = PathBuf::from("cros/bazel/portage/tools/process_artifacts/testdata");
let runfiles = Runfiles::create()?;
let events = load_build_events_jsonl(
&runfiles.rlocation(testdata_path.join("ebuild_metadata.bep.jsonl")),
)?;
let processor = BuildEventProcessor::from(&events);
// Set up a workspace directory containing fake artifacts.
let workspace_dir = tempfile::TempDir::new()?;
let workspace_dir = workspace_dir.path();
for (test_path, relative_path) in [
(
"linux-headers.metadata.json",
"bazel-out/k8-fastbuild/bin/external/_main~portage~portage/internal/packages/stage1/target/host/chromiumos/sys-kernel/linux-headers/4.14-r92_metadata.json"
), (
"glibc.metadata.json",
"bazel-out/k8-fastbuild/bin/external/_main~portage~portage/internal/packages/stage1/target/host/chromiumos/sys-libs/glibc/2.37-r7_metadata.json"
), (
"os-headers.metadata.json",
"bazel-out/k8-fastbuild/bin/external/_main~portage~portage/internal/packages/stage1/target/host/portage-stable/virtual/os-headers/0-r2_metadata.json"
),
] {
let path = workspace_dir.join(relative_path);
std::fs::create_dir_all(path.parent().unwrap())?;
std::fs::copy(&runfiles.rlocation(
testdata_path.join(test_path)
), path)?;
}
let tmpdir = tempdir()?;
let output_path = tmpdir.path().join("prebuilts.bzl");
// Create an archive.
compute_prebuilts(&output_path, workspace_dir, &processor)?;
let output = std::fs::read_to_string(&output_path)?;
assert_eq!(
output,
format!(
"{}{}",
PREBUILT_HEADER,
r#"build:prebuilts \
--@@_main~portage~portage//internal/packages/stage1/target/host/chromiumos/sys-kernel/linux-headers:4.14-r92_prebuilt=cas://projects/chromeos-bot/instances/cros-rbe-nonrelease/c38045633a8cec7411aa0618e63896b52b0234ddd7136a34a69ef2af9134b74d/1322061 \
--@@_main~portage~portage//internal/packages/stage1/target/host/chromiumos/sys-libs/glibc:2.37-r7_prebuilt=cas://projects/chromeos-bot/instances/cros-rbe-nonrelease/3cd79108475a96af2874f9fbd602093748c3d60d4e7e2265c2f55dc82fa8a21c/21002060 \
--@@_main~portage~portage//internal/packages/stage1/target/host/portage-stable/virtual/os-headers:0-r2_prebuilt=cas://projects/chromeos-bot/instances/cros-rbe-nonrelease/a623fd5b2aca004886fb9a1ff070d999101e07b4353535e889eca0612c2c7d56/14851
"#
)
);
Ok(())
}
#[test]
fn missing_metadata() -> Result<()> {
let testdata_path = PathBuf::from("cros/bazel/portage/tools/process_artifacts/testdata");
let runfiles = Runfiles::create()?;
let events = load_build_events_jsonl(
&runfiles.rlocation(testdata_path.join("ebuild_metadata.bep.jsonl")),
)?;
let processor = BuildEventProcessor::from(&events);
// Set up a workspace directory containing fake artifacts.
let workspace_dir = tempfile::TempDir::new()?;
let workspace_dir = workspace_dir.path();
for (test_path, relative_path) in [
(
"linux-headers.metadata.json",
"bazel-out/k8-fastbuild/bin/external/_main~portage~portage/internal/packages/stage1/target/host/chromiumos/sys-kernel/linux-headers/4.14-r92_metadata.json"
), (
"glibc.metadata.json",
"bazel-out/k8-fastbuild/bin/external/_main~portage~portage/internal/packages/stage1/target/host/chromiumos/sys-libs/glibc/2.37-r7_metadata.json"
),
] {
let path = workspace_dir.join(relative_path);
std::fs::create_dir_all(path.parent().unwrap())?;
std::fs::copy(&runfiles.rlocation(
testdata_path.join(test_path)
), path)?;
}
let tmpdir = tempdir()?;
let output_path = tmpdir.path().join("prebuilts.bzl");
// Create an archive.
compute_prebuilts(&output_path, workspace_dir, &processor)?;
let output = std::fs::read_to_string(&output_path)?;
assert_eq!(
output,
format!(
r#"{}# Failed to load: "{}/bazel-out/k8-fastbuild/bin/external/_main~portage~portage/internal/packages/stage1/target/host/portage-stable/virtual/os-headers/0-r2_metadata.json": Failed to open "{}/bazel-out/k8-fastbuild/bin/external/_main~portage~portage/internal/packages/stage1/target/host/portage-stable/virtual/os-headers/0-r2_metadata.json"
{}"#,
PREBUILT_HEADER,
workspace_dir.display(),
workspace_dir.display(),
r#"build:prebuilts \
--@@_main~portage~portage//internal/packages/stage1/target/host/chromiumos/sys-kernel/linux-headers:4.14-r92_prebuilt=cas://projects/chromeos-bot/instances/cros-rbe-nonrelease/c38045633a8cec7411aa0618e63896b52b0234ddd7136a34a69ef2af9134b74d/1322061 \
--@@_main~portage~portage//internal/packages/stage1/target/host/chromiumos/sys-libs/glibc:2.37-r7_prebuilt=cas://projects/chromeos-bot/instances/cros-rbe-nonrelease/3cd79108475a96af2874f9fbd602093748c3d60d4e7e2265c2f55dc82fa8a21c/21002060
"#
)
);
Ok(())
}
}