build(ENG-21330): Replace ccache with sccache (#24200)

This commit is contained in:
Marko Vejnovic
2025-11-05 14:30:56 -08:00
committed by GitHub
parent 995d988c73
commit 782f684b2e
13 changed files with 210 additions and 175 deletions

View File

@@ -120,18 +120,18 @@ function Refresh-Path {
function Add-To-Path {
$absolutePath = Resolve-Path $args[0]
$currentPath = [Environment]::GetEnvironmentVariable("Path", "Machine")
if ($currentPath -like "*$absolutePath*") {
if ($currentPath -like "*$absolutePath*") {
return
}
$newPath = $currentPath.TrimEnd(";") + ";" + $absolutePath
if ($newPath.Length -ge 2048) {
Write-Warning "PATH is too long, removing duplicate and old entries..."
$paths = $currentPath.Split(';', [StringSplitOptions]::RemoveEmptyEntries) |
$paths = $currentPath.Split(';', [StringSplitOptions]::RemoveEmptyEntries) |
Where-Object { $_ -and (Test-Path $_) } |
Select-Object -Unique
$paths += $absolutePath
$newPath = $paths -join ';'
while ($newPath.Length -ge 2048 -and $paths.Count -gt 1) {
@@ -274,7 +274,6 @@ function Install-Build-Essentials {
cmake `
make `
ninja `
ccache `
python `
golang `
nasm `
@@ -282,6 +281,7 @@ function Install-Build-Essentials {
strawberryperl `
mingw
Install-Rust
Install-Sccache
# Needed to remap stack traces
Install-PdbAddr2line
Install-Llvm
@@ -344,6 +344,10 @@ function Install-Rust {
Add-To-Path "$rustPath\cargo\bin"
}
function Install-Sccache {
Install-Package sccache -Version "0.12.0"
}
function Install-PdbAddr2line {
Execute-Command cargo install --examples "pdb-addr2line@0.11.2"
}

View File

@@ -26,8 +26,11 @@ error() {
}
execute() {
print "$ $@" >&2
if ! "$@"; then
local opts=$-
set -x
"$@"
{ local status=$?; set +x "$opts"; } 2> /dev/null
if [ "$status" -ne 0 ]; then
error "Command failed: $@"
fi
}
@@ -1035,7 +1038,7 @@ install_build_essentials() {
install_llvm
install_osxcross
install_gcc
install_ccache
install_sccache
install_rust
install_docker
}
@@ -1146,12 +1149,31 @@ install_gcc() {
execute_sudo ln -sf $(which llvm-symbolizer-$llvm_v) /usr/bin/llvm-symbolizer
}
install_ccache() {
case "$pm" in
apt | apk | brew)
install_packages ccache
;;
esac
install_sccache() {
# Alright, look, this function is cobbled together but it's only as cobbled
# together as this whole script is.
#
# For some reason, move_to_bin doesn't work here due to permissions so I'm
# avoiding that function. It's also wrong with permissions and so on.
#
# Unfortunately, we cannot use install_packages since many package managers
# don't compile `sccache` with S3 support.
local opts=$-
set -ef
local sccache_http
sccache_http="https://github.com/mozilla/sccache/releases/download/v0.12.0/sccache-v0.12.0-$(uname -m)-unknown-linux-musl.tar.gz"
local file
file=$(download_file "$sccache_http")
local tmpdir
tmpdir=$(mktemp -d)
execute tar -xzf "$file" -C "$tmpdir"
execute_sudo install -m755 "$tmpdir/sccache-v0.12.0-$(uname -m)-unknown-linux-musl/sccache" "/usr/local/bin"
set +ef -"$opts"
}
install_rust() {
@@ -1402,10 +1424,10 @@ install_chromium() {
apk)
install_packages \
chromium \
nss \
freetype \
harfbuzz \
ttf-freefont
nss \
freetype \
harfbuzz \
ttf-freefont
;;
apt)
install_packages \

View File

@@ -1,5 +1,5 @@
import { spawn as nodeSpawn } from "node:child_process";
import { chmodSync, cpSync, existsSync, mkdirSync, readFileSync } from "node:fs";
import { existsSync, readFileSync } from "node:fs";
import { basename, join, relative, resolve } from "node:path";
import {
formatAnnotationToHtml,
@@ -70,43 +70,8 @@ async function build(args) {
generateOptions["-S"] = process.cwd();
}
const cacheRead = isCacheReadEnabled();
const cacheWrite = isCacheWriteEnabled();
if (cacheRead || cacheWrite) {
const cachePath = getCachePath();
if (cacheRead && !existsSync(cachePath)) {
const mainCachePath = getCachePath(getDefaultBranch());
if (existsSync(mainCachePath)) {
mkdirSync(cachePath, { recursive: true });
try {
cpSync(mainCachePath, cachePath, { recursive: true, force: true });
} catch (error) {
const { code } = error;
switch (code) {
case "EPERM":
case "EACCES":
try {
chmodSync(mainCachePath, 0o777);
cpSync(mainCachePath, cachePath, { recursive: true, force: true });
} catch (error) {
console.warn("Failed to copy cache with permissions fix", error);
}
break;
default:
console.warn("Failed to copy cache", error);
}
}
}
}
generateOptions["-DCACHE_PATH"] = cmakePath(cachePath);
generateOptions["--fresh"] = undefined;
if (cacheRead && cacheWrite) {
generateOptions["-DCACHE_STRATEGY"] = "read-write";
} else if (cacheRead) {
generateOptions["-DCACHE_STRATEGY"] = "read-only";
} else if (cacheWrite) {
generateOptions["-DCACHE_STRATEGY"] = "write-only";
}
if (!generateOptions["-DCACHE_STRATEGY"]) {
generateOptions["-DCACHE_STRATEGY"] = "read-write";
}
const toolchain = generateOptions["--toolchain"];
@@ -136,58 +101,17 @@ async function build(args) {
await startGroup("CMake Build", () => spawn("cmake", buildArgs, { env }));
await startGroup("sccache stats", () => {
spawn("sccache", ["--show-stats"], { env });
});
printDuration("total", Date.now() - startTime);
}
function cmakePath(path) {
return path.replace(/\\/g, "/");
}
/** @param {string} str */
const toAlphaNumeric = str => str.replace(/[^a-z0-9]/gi, "-");
function getCachePath(branch) {
const {
BUILDKITE_BUILD_PATH: buildPath,
BUILDKITE_REPO: repository,
BUILDKITE_PULL_REQUEST_REPO: fork,
BUILDKITE_BRANCH,
BUILDKITE_STEP_KEY,
} = process.env;
// NOTE: settings that could be long should be truncated to avoid hitting max
// path length limit on windows (4096)
const repositoryKey = toAlphaNumeric(
// remove domain name, only leaving 'org/repo'
(fork || repository).replace(/^https?:\/\/github\.com\/?/, ""),
);
const branchName = toAlphaNumeric(branch || BUILDKITE_BRANCH);
const branchKey = branchName.startsWith("gh-readonly-queue-")
? branchName.slice(18, branchName.indexOf("-pr-"))
: branchName.slice(0, 32);
const stepKey = toAlphaNumeric(BUILDKITE_STEP_KEY);
return resolve(buildPath, "..", "cache", repositoryKey, branchKey, stepKey);
}
function isCacheReadEnabled() {
return (
isBuildkite() &&
process.env.BUILDKITE_CLEAN_CHECKOUT !== "true" &&
process.env.BUILDKITE_BRANCH !== getDefaultBranch()
);
}
function isCacheWriteEnabled() {
return isBuildkite();
}
function isBuildkite() {
return process.env.BUILDKITE === "true";
}
function getDefaultBranch() {
return process.env.BUILDKITE_PIPELINE_DEFAULT_BRANCH || "main";
}
function parseOptions(args, flags = []) {
const options = {};

View File

@@ -485,6 +485,12 @@ const aws = {
});
}
// Attach IAM instance profile for CI builds to enable S3 build cache access
let iamInstanceProfile;
if (options.ci) {
iamInstanceProfile = JSON.stringify({ Name: "buildkite-build-agent" });
}
const [instance] = await aws.runInstances({
["image-id"]: ImageId,
["instance-type"]: instanceType || (arch === "aarch64" ? "t4g.large" : "t3.large"),
@@ -499,6 +505,7 @@ const aws = {
["tag-specifications"]: JSON.stringify(tagSpecification),
["key-name"]: keyName,
["instance-market-options"]: marketOptions,
["iam-instance-profile"]: iamInstanceProfile,
});
const machine = aws.toMachine(instance, { ...options, username, keyPath });
@@ -1191,6 +1198,9 @@ async function main() {
const tags = {
"robobun": "true",
"robobun2": "true",
// This tag controls the IAM role required to be able to write to the shared S3 build cache.
// Don't want accidental polution from non-CI runs.
"Service": args["ci"] ? "buildkite-agent" : undefined,
"buildkite:token": args["buildkite-token"],
"tailscale:authkey": args["tailscale-authkey"],
...Object.fromEntries(args["tag"]?.map(tag => tag.split("=")) ?? []),