Skip to content

Commit de9c139

Browse files
build(kernel): add 2-mode kernel build recipe + tagged CI job
Add a reproducible build recipe for the opt-in SEA-via-kernel backend and a CI job that exercises it, so the databricks_kernel-tagged path is built and unit-tested in CI instead of being dead code. - KERNEL_REV pins the kernel commit (PR #163 head) to build against. - cgo.go drops the machine-specific link line; per-platform cgo_<os>.go files carry ${SRCDIR}-relative LDFLAGS and force static linking (-l:...a). - build/kernel-lib.sh builds the kernel .a from source at KERNEL_REV (cargo --no-default-features --features tls-rustls), with a KERNEL_LOCAL_A override; Makefile adds kernel-lib / build-kernel / test-kernel and a kernel-lib-download stub for the future published-.a path. - go.yml gains build-and-test-kernel (CGO + rust + cargo-via-JFrog); the pure-Go CGO_ENABLED=0 jobs are untouched. Built artifacts are gitignored. Co-authored-by: Isaac
1 parent 08cbcfe commit de9c139

9 files changed

Lines changed: 428 additions & 17 deletions

File tree

.github/workflows/go.yml

Lines changed: 131 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ name: Go
22

33
on:
44
push:
5-
branches: [main]
5+
# TEMPORARY: mani/sea-kernel-build-recipe is here only to run CI on the
6+
# stacked build-recipe branch (whose PR base is the feature branch, not
7+
# main, so the pull_request trigger below won't fire for it). This entry
8+
# MUST be removed before merge — do not ship it to main.
9+
branches: [main, mani/sea-kernel-build-recipe]
610
pull_request:
711
branches: [main]
812

@@ -92,10 +96,8 @@ jobs:
9296
9397
# This matrix builds the default pure-Go driver (CGO_ENABLED=0), which does
9498
# NOT compile the SEA-via-kernel backend (//go:build cgo && databricks_kernel).
95-
# A dedicated CGO_ENABLED=1 `-tags databricks_kernel` test job needs the
96-
# kernel static library linked in CI, which arrives with the kernel
97-
# distribution work (a pinned-revision source build or a published .a).
98-
# Until then the kernel path's cgo files are not exercised here.
99+
# That opt-in path is exercised by the separate build-and-test-kernel job
100+
# below, which builds the kernel static library and links it with CGO.
99101
- name: Test
100102
run: make test
101103
env:
@@ -106,3 +108,127 @@ jobs:
106108

107109
- name: Build
108110
run: make linux
111+
112+
build-and-test-kernel:
113+
name: Test (kernel backend)
114+
# Exercises the opt-in SEA-via-kernel backend: builds the Rust kernel static
115+
# lib from the pinned KERNEL_REV and runs the `databricks_kernel`-tagged unit
116+
# tests with CGO. Separate from build-and-test so that job's CGO_ENABLED=0
117+
# pure-Go invariant is untouched. No warehouse creds here, so the live e2e /
118+
# parity tests self-skip; only the tagged unit tests run.
119+
runs-on:
120+
group: databricks-protected-runner-group
121+
labels: linux-ubuntu-latest
122+
123+
steps:
124+
- name: Check out code into the Go module directory
125+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
126+
127+
# Go module proxy (GOPROXY + ~/.netrc) via JFrog OIDC. Also exports
128+
# JFROG_ACCESS_TOKEN, which the cargo step below reuses.
129+
- name: Setup JFrog
130+
uses: ./.github/actions/setup-jfrog
131+
132+
# The protected runner blocks direct crates.io access (go/hardened-gha),
133+
# so point cargo at the JFrog crates proxy. This repo's setup-jfrog is
134+
# Go-only, so configure cargo inline here reusing its JFROG_ACCESS_TOKEN.
135+
# The token stays in ~/.cargo/credentials.toml (not a CARGO*-prefixed env
136+
# var) so rust-cache keys stay stable across runs.
137+
- name: Configure cargo to use JFrog
138+
shell: bash
139+
run: |
140+
set -euo pipefail
141+
mkdir -p ~/.cargo
142+
cat > ~/.cargo/config.toml << 'EOF'
143+
[source.crates-io]
144+
replace-with = "jfrog"
145+
146+
[source.jfrog]
147+
registry = "sparse+https://databricks.jfrog.io/artifactory/api/cargo/db-cargo-remote/index/"
148+
149+
[registries.jfrog]
150+
index = "sparse+https://databricks.jfrog.io/artifactory/api/cargo/db-cargo-remote/index/"
151+
credential-provider = ["cargo:token"]
152+
EOF
153+
cat > ~/.cargo/credentials.toml << EOF
154+
[registries.jfrog]
155+
token = "Bearer ${JFROG_ACCESS_TOKEN}"
156+
EOF
157+
chmod 600 ~/.cargo/credentials.toml
158+
159+
- name: Set up Go Toolchain
160+
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
161+
with:
162+
go-version: '1.25.x'
163+
cache: false
164+
165+
- name: Set up Rust Toolchain
166+
uses: actions-rust-lang/setup-rust-toolchain@46268bd060767258de96ed93c1251119784f2ab6 # v1.16.1
167+
with:
168+
toolchain: stable
169+
170+
# The kernel repo (databricks/databricks-sql-kernel) is private, and the
171+
# hardened runner has no ambient git credentials, so kernel-lib.sh's fetch
172+
# fails with "could not read Username". Mint a repo-scoped token from the
173+
# INTEGRATION_TEST_APP GitHub App (installed on the org with kernel read
174+
# access — the same mechanism the ODBC driver uses) and rewrite the kernel
175+
# HTTPS URL to carry it. This is transparent to kernel-lib.sh. Retire once
176+
# the kernel publishes a release artifact (KERNEL_LOCAL_A / download path).
177+
- name: Generate GitHub App token for databricks-sql-kernel
178+
id: kernel-token
179+
uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0
180+
with:
181+
app-id: ${{ secrets.INTEGRATION_TEST_APP_ID }}
182+
private-key: ${{ secrets.INTEGRATION_TEST_PRIVATE_KEY }}
183+
owner: databricks
184+
repositories: databricks-sql-kernel
185+
186+
- name: Rewrite kernel repo URL to authenticated HTTPS
187+
env:
188+
TOKEN: ${{ steps.kernel-token.outputs.token }}
189+
run: |
190+
git config --global \
191+
url."https://x-access-token:${TOKEN}@github.com/databricks/".insteadOf \
192+
"https://github.com/databricks/"
193+
194+
# Cache the built kernel .a keyed on KERNEL_REV: rebuild only when the pin
195+
# moves. The ~85MB archive dwarfs a rebuild trigger, so keep the key tight.
196+
- name: Cache kernel static lib
197+
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
198+
with:
199+
path: |
200+
internal/backend/kernel/lib
201+
internal/backend/kernel/include
202+
key: ${{ runner.os }}-kernellib-${{ hashFiles('KERNEL_REV') }}
203+
204+
# Cache the cargo registry + kernel build tree so a cache miss on the .a
205+
# is still an incremental Rust build, not a cold ~200-crate compile.
206+
- name: Cache cargo + kernel build tree
207+
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
208+
with:
209+
path: |
210+
~/.cargo/registry
211+
~/.cargo/git
212+
build/kernel-src/target
213+
key: ${{ runner.os }}-kernel-cargo-${{ hashFiles('KERNEL_REV') }}
214+
restore-keys: |
215+
${{ runner.os }}-kernel-cargo-
216+
217+
# make (for the recipe) and a C compiler (cgo compiles the kernel binding
218+
# stubs and links libdatabricks_sql_kernel.a). cc is preinstalled on the
219+
# protected runner — the kernel repo's own c-abi job relies on the same —
220+
# so these conditional installs are a no-op guard that keeps the job robust
221+
# to a future image change rather than a known gap.
222+
- name: Install build prerequisites (make, C compiler)
223+
run: |
224+
if ! command -v make &> /dev/null ; then
225+
apt-get update && apt-get install -y make
226+
fi
227+
if ! command -v cc &> /dev/null ; then
228+
apt-get update && apt-get install -y build-essential
229+
fi
230+
231+
# make test-kernel builds the kernel lib (make kernel-lib) if the cache
232+
# missed, then runs `CGO_ENABLED=1 go test -tags databricks_kernel ./...`.
233+
- name: Build kernel lib + run tagged tests
234+
run: make test-kernel

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,8 @@ _tmp*
6666
.vscode/
6767
__debug_bin
6868
.DS_Store
69+
70+
# Kernel (SEA) backend build artifacts — produced by `make kernel-lib`, never committed.
71+
/build/kernel-src/
72+
/internal/backend/kernel/lib/
73+
/internal/backend/kernel/include/

KERNEL_REV

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
9b90406

Makefile

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,46 @@ build: linux darwin ## Build the multi-arch binaries
8181
$(PLATFORMS):
8282
mkdir -p bin
8383
GOOS=$(os) GOARCH=amd64 $(GO) build $(GOBUILD_ARGS) -ldflags '$(LDFLAGS)' -o bin/$(BINARY)-$(os)-amd64 .
84+
85+
# ── Kernel (SEA) backend ──────────────────────────────────────────────────────
86+
# Opt-in cgo build that links the Rust SQL kernel's C ABI, gated behind the
87+
# `databricks_kernel` build tag. These targets are wholly separate from the
88+
# pure-Go defaults above (which stay CGO_ENABLED=0 and never touch the kernel).
89+
KERNEL_REV = $(shell cat KERNEL_REV)
90+
KERNEL_REPO ?= https://github.com/databricks/databricks-sql-kernel.git
91+
KERNEL_SRC ?= build/kernel-src
92+
# Target OS/arch (honors GOOS/GOARCH overrides) vs the actual host, so the build
93+
# step can reject a source cross-build that would drop a host .a into a foreign
94+
# target dir. Multi-OS support is via native per-OS runners (host == target) or
95+
# staging a prebuilt .a with KERNEL_LOCAL_A — not cross-compiling from source.
96+
KERNEL_GOOS = $(shell go env GOOS)
97+
KERNEL_GOARCH = $(shell go env GOARCH)
98+
KERNEL_GOHOSTOS = $(shell go env GOHOSTOS)
99+
KERNEL_GOHOSTARCH = $(shell go env GOHOSTARCH)
100+
KERNEL_LIB_DIR = internal/backend/kernel/lib/$(KERNEL_GOOS)_$(KERNEL_GOARCH)
101+
KERNEL_INC_DIR = internal/backend/kernel/include
102+
KERNEL_GO = CGO_ENABLED=1 go
103+
KERNEL_TAGS = -tags databricks_kernel
104+
105+
.PHONY: kernel-lib
106+
kernel-lib: ## Build the pinned kernel static lib + header into the cgo link dir (source build).
107+
KERNEL_REPO="$(KERNEL_REPO)" KERNEL_REV="$(KERNEL_REV)" KERNEL_SRC="$(KERNEL_SRC)" \
108+
KERNEL_LIB_DIR="$(KERNEL_LIB_DIR)" KERNEL_INC_DIR="$(KERNEL_INC_DIR)" \
109+
KERNEL_GOOS="$(KERNEL_GOOS)" KERNEL_GOARCH="$(KERNEL_GOARCH)" \
110+
KERNEL_GOHOSTOS="$(KERNEL_GOHOSTOS)" KERNEL_GOHOSTARCH="$(KERNEL_GOHOSTARCH)" \
111+
KERNEL_LOCAL_A="$(KERNEL_LOCAL_A)" KERNEL_LOCAL_HEADER="$(KERNEL_LOCAL_HEADER)" \
112+
./build/kernel-lib.sh
113+
114+
.PHONY: build-kernel
115+
build-kernel: kernel-lib ## Build the driver with the kernel backend linked.
116+
$(KERNEL_GO) build $(KERNEL_TAGS) ./...
117+
118+
.PHONY: test-kernel
119+
test-kernel: kernel-lib ## Run the kernel-tagged unit tests (no warehouse needed).
120+
$(KERNEL_GO) test $(KERNEL_TAGS) ./...
121+
122+
.PHONY: kernel-lib-download
123+
kernel-lib-download: ## Prod mode (download prebuilt .a): blocked until the kernel publishes release artifacts.
124+
@echo "kernel-lib-download: blocked — the kernel does not yet publish per-platform .a release artifacts."
125+
@echo "Use 'make kernel-lib' (source build) meanwhile. See the distribution design doc."
126+
@false

build/kernel-lib.sh

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
#!/usr/bin/env bash
2+
#
3+
# kernel-lib.sh — build (or copy in) the Databricks SQL kernel static library
4+
# for the cgo `databricks_kernel` backend.
5+
#
6+
# Mode 1 (source build, the default and the only thing that works today): clone
7+
# the kernel at the pinned KERNEL_REV, `cargo build` a self-contained static
8+
# archive with pure-Rust TLS, and drop the .a + C header where the per-platform
9+
# cgo_<os>.go files link them (${SRCDIR}/lib/<os>_<arch> and ${SRCDIR}/include,
10+
# both .gitignore'd). Kernel releases publish no binary assets yet, so building
11+
# from a pinned commit is the current distribution model (mirrors the ODBC
12+
# driver's Corrosion-from-source build).
13+
#
14+
# Local override: set KERNEL_LOCAL_A (and optionally KERNEL_LOCAL_HEADER) to
15+
# copy an already-built archive instead of cloning + building — the analogue of
16+
# the ODBC driver's KERNEL_LOCAL_PATH.
17+
#
18+
# Invoked by `make kernel-lib`, which supplies the environment below. It can
19+
# also be run directly with the same variables set.
20+
#
21+
# Required env (Makefile provides these):
22+
# KERNEL_REV kernel commit SHA to build (from the repo-root KERNEL_REV)
23+
# KERNEL_REPO git URL of the kernel repo
24+
# KERNEL_SRC working dir for the kernel checkout (gitignored)
25+
# KERNEL_LIB_DIR dest dir for the .a (…/lib/<os>_<arch>)
26+
# KERNEL_INC_DIR dest dir for the header (…/include)
27+
# Optional env:
28+
# KERNEL_LOCAL_A path to a prebuilt libdatabricks_sql_kernel.a to copy
29+
# KERNEL_LOCAL_HEADER path to a databricks_kernel.h to copy (defaults to a
30+
# header next to KERNEL_LOCAL_A, else the checkout's)
31+
32+
set -euo pipefail
33+
34+
: "${KERNEL_LIB_DIR:?KERNEL_LIB_DIR must be set (run via 'make kernel-lib')}"
35+
: "${KERNEL_INC_DIR:?KERNEL_INC_DIR must be set (run via 'make kernel-lib')}"
36+
37+
LIB_NAME="libdatabricks_sql_kernel.a"
38+
HEADER_NAME="databricks_kernel.h"
39+
40+
log() { printf '[kernel-lib] %s\n' "$*" >&2; }
41+
42+
emit_checksum() {
43+
# A reproducibility breadcrumb: the archive path + its content hash.
44+
local a="$KERNEL_LIB_DIR/$LIB_NAME"
45+
if command -v sha256sum >/dev/null 2>&1; then
46+
log "artifact: $a"
47+
log "sha256: $(sha256sum "$a" | cut -d' ' -f1)"
48+
elif command -v shasum >/dev/null 2>&1; then
49+
log "artifact: $a"
50+
log "sha256: $(shasum -a 256 "$a" | cut -d' ' -f1)"
51+
fi
52+
}
53+
54+
mkdir -p "$KERNEL_LIB_DIR" "$KERNEL_INC_DIR"
55+
56+
# ── Local override: copy a prebuilt archive, skip clone + cargo entirely. ──────
57+
if [ -n "${KERNEL_LOCAL_A:-}" ]; then
58+
[ -f "$KERNEL_LOCAL_A" ] || { log "KERNEL_LOCAL_A not found: $KERNEL_LOCAL_A"; exit 1; }
59+
local_header="${KERNEL_LOCAL_HEADER:-}"
60+
if [ -z "$local_header" ]; then
61+
# Prefer a header next to the archive; fall back to the checkout's include/.
62+
if [ -f "$(dirname "$KERNEL_LOCAL_A")/$HEADER_NAME" ]; then
63+
local_header="$(dirname "$KERNEL_LOCAL_A")/$HEADER_NAME"
64+
elif [ -n "${KERNEL_SRC:-}" ] && [ -f "$KERNEL_SRC/include/$HEADER_NAME" ]; then
65+
local_header="$KERNEL_SRC/include/$HEADER_NAME"
66+
fi
67+
fi
68+
[ -n "$local_header" ] && [ -f "$local_header" ] || {
69+
log "header not found; set KERNEL_LOCAL_HEADER to a $HEADER_NAME"; exit 1; }
70+
log "copying prebuilt archive from $KERNEL_LOCAL_A"
71+
cp "$KERNEL_LOCAL_A" "$KERNEL_LIB_DIR/$LIB_NAME"
72+
cp "$local_header" "$KERNEL_INC_DIR/$HEADER_NAME"
73+
emit_checksum
74+
exit 0
75+
fi
76+
77+
# ── Mode 1: build from source at the pinned rev. ──────────────────────────────
78+
: "${KERNEL_REV:?KERNEL_REV must be set (run via 'make kernel-lib')}"
79+
: "${KERNEL_REPO:?KERNEL_REPO must be set (run via 'make kernel-lib')}"
80+
: "${KERNEL_SRC:?KERNEL_SRC must be set (run via 'make kernel-lib')}"
81+
82+
# Cache short-circuit: if a previously built archive + header are present AND the
83+
# rev-stamp beside the archive matches KERNEL_REV, the artifact is already what
84+
# this pin would produce — skip the git fetch + cargo build entirely (and don't
85+
# even require a Rust toolchain). `kernel-lib` is .PHONY and CI re-invokes it on
86+
# every run after restoring the .a cache (keyed on KERNEL_REV), so without this
87+
# guard the build re-ran and overwrote the just-restored archive every time.
88+
# The stamp (not just file existence) is what makes this safe against a local
89+
# KERNEL_REV bump with a stale on-disk .a: a changed pin fails the match and
90+
# forces a rebuild. In CI the .a-cache key already includes KERNEL_REV, so a pin
91+
# change misses the cache and the artifact is simply absent — either way a bump
92+
# rebuilds.
93+
REV_STAMP="$KERNEL_LIB_DIR/.kernel-rev"
94+
if [ -f "$KERNEL_LIB_DIR/$LIB_NAME" ] && [ -f "$KERNEL_INC_DIR/$HEADER_NAME" ] &&
95+
[ -f "$REV_STAMP" ] && [ "$(cat "$REV_STAMP")" = "$KERNEL_REV" ]; then
96+
log "cache hit: $LIB_NAME already built for $KERNEL_REV — skipping source build"
97+
emit_checksum
98+
exit 0
99+
fi
100+
101+
# Reject a source cross-build. `cargo build` below has no --target, so it emits
102+
# the HOST triple's archive — but KERNEL_LIB_DIR is named for the TARGET
103+
# (GOOS/GOARCH). If they differ, copying the host .a into the target dir would
104+
# silently produce a wrong-arch artifact. Multi-OS is served by native per-OS
105+
# runners (host == target, so this passes) or by staging a prebuilt cross-target
106+
# .a via KERNEL_LOCAL_A (handled above, before this check) — not by cross-
107+
# building the kernel from source, which the distribution design defers to the
108+
# download path. Fail loud rather than mislink. Only enforced when the Makefile
109+
# passes the host vars; a direct script call without them skips the check.
110+
if [ -n "${KERNEL_GOOS:-}" ] && [ -n "${KERNEL_GOHOSTOS:-}" ] &&
111+
{ [ "$KERNEL_GOOS" != "$KERNEL_GOHOSTOS" ] || [ "${KERNEL_GOARCH:-}" != "${KERNEL_GOHOSTARCH:-}" ]; }; then
112+
log "refusing source cross-build: target ${KERNEL_GOOS}/${KERNEL_GOARCH} != host ${KERNEL_GOHOSTOS}/${KERNEL_GOHOSTARCH}."
113+
log "build on a native ${KERNEL_GOOS} runner, or stage a prebuilt archive with KERNEL_LOCAL_A=<path>."
114+
exit 1
115+
fi
116+
117+
command -v cargo >/dev/null 2>&1 || {
118+
log "cargo not found — the source build needs a Rust toolchain."
119+
log "install rustup, or use 'make kernel-lib KERNEL_LOCAL_A=<path>' with a prebuilt .a."
120+
exit 1
121+
}
122+
123+
# Make $KERNEL_SRC a git repo pointed at the kernel remote, WITHOUT assuming an
124+
# empty destination. CI caches build/kernel-src/target/ (for incremental Rust
125+
# builds) but not .git, so on a cache hit the dir exists with a target/ subtree
126+
# and no repo — a plain `git clone` would abort ("destination path already
127+
# exists and is not an empty directory"). `git init` is idempotent and works
128+
# whether the dir is absent, empty, or holds a restored target/; the kernel's
129+
# own .gitignore excludes /target, so the later checkout leaves it untouched.
130+
mkdir -p "$KERNEL_SRC"
131+
if [ ! -d "$KERNEL_SRC/.git" ]; then
132+
log "initializing git repo in $KERNEL_SRC (-> $KERNEL_REPO)"
133+
git -C "$KERNEL_SRC" init --quiet
134+
fi
135+
if git -C "$KERNEL_SRC" remote get-url origin >/dev/null 2>&1; then
136+
git -C "$KERNEL_SRC" remote set-url origin "$KERNEL_REPO"
137+
else
138+
git -C "$KERNEL_SRC" remote add origin "$KERNEL_REPO"
139+
fi
140+
141+
log "fetching + checking out $KERNEL_REV"
142+
git -C "$KERNEL_SRC" fetch --all --tags --quiet || true
143+
# checkout -f: this is a tool-managed, gitignored scratch checkout of a pinned
144+
# third-party repo, so force past any leftover files (e.g. a source tree left
145+
# behind if .git was lost). -f overwrites tracked-path collisions but leaves the
146+
# gitignored target/ alone, so a cache-restored build tree survives. Without -f,
147+
# an untracked-file conflict would masquerade as "commit not reachable" and send
148+
# us down the PR-head fallback for the wrong reason.
149+
#
150+
# KERNEL_REV may be a bare commit that only lives on a PR ref (e.g. #163's head
151+
# before it merges), so try the commit directly, then the PR head ref.
152+
if ! git -C "$KERNEL_SRC" checkout -f --quiet "$KERNEL_REV" 2>/dev/null; then
153+
log "commit not directly reachable; trying PR head refs"
154+
git -C "$KERNEL_SRC" fetch --quiet origin '+refs/pull/*/head:refs/remotes/origin/pr/*' || true
155+
git -C "$KERNEL_SRC" checkout -f --quiet "$KERNEL_REV" || {
156+
log "could not check out $KERNEL_REV — is it pushed/fetchable?"; exit 1; }
157+
fi
158+
log "kernel at $(git -C "$KERNEL_SRC" rev-parse --short HEAD)"
159+
160+
# --no-default-features --features tls-rustls: pure-Rust TLS (no system OpenSSL)
161+
# keeps the archive self-contained and cross-compile-tractable. The kernel's
162+
# default is tls-native, so the override is required.
163+
log "cargo build --release --no-default-features --features tls-rustls"
164+
( cd "$KERNEL_SRC" && cargo build --release --no-default-features --features tls-rustls )
165+
166+
src_a="$KERNEL_SRC/target/release/$LIB_NAME"
167+
src_h="$KERNEL_SRC/include/$HEADER_NAME"
168+
[ -f "$src_a" ] || { log "expected archive not produced: $src_a"; exit 1; }
169+
[ -f "$src_h" ] || { log "expected header not found: $src_h"; exit 1; }
170+
171+
cp "$src_a" "$KERNEL_LIB_DIR/$LIB_NAME"
172+
cp "$src_h" "$KERNEL_INC_DIR/$HEADER_NAME"
173+
# Record the rev this archive was built for so a later run can short-circuit
174+
# (see the cache short-circuit above). Written last, only after a successful
175+
# build, so a stamp never claims an artifact that isn't there.
176+
printf '%s\n' "$KERNEL_REV" > "$REV_STAMP"
177+
emit_checksum

0 commit comments

Comments
 (0)