dev: On linux -dev_fast is on by default

Assuming the conditions are met for -dev_fast to work (linux, debug, x86-64
without sanitizers), the -dev_fast now defaults to true.

download-v8 make target always downloads the .a (as before) and on linux x86-64
it also downloads the .so.
This commit is contained in:
Karl Seguin committed 2026-08-14 10:43:34 +08:00
1 parent fb7123cf75
commit 8caaf7ad96
3 files changed
+88 -70

No files matched your search

+5 -6
View File
@@ -12,13 +12,12 @@ See [AGENTS.md](AGENTS.md) for the full set of test, formatting, and code conven
### Skip the V8 source build
By default the build compiles V8 from source, which takes several minutes. Run
`make download-v8` once to fetch the matching prebuilt archive from the
`make download-v8` once to fetch the matching prebuilt libraries from the
[`zig-v8-fork`](https://github.com/lightpanda-io/zig-v8-fork/releases) releases
instead; later `make build` / `make test` pick it up automatically.
On Linux x86_64, `make download-v8-shared` additionally fetches the shared
flavor (`libc_v8.so`) that `zig build -Ddev_fast=true` picks up automatically
for much faster debug rebuilds.
instead; later `make build` / `make test` pick them up automatically. On Linux
x86_64 this includes the shared flavor (`libc_v8.so`) used by `-Ddev_fast`
builds (defaults to true). Pass `-Ddev_fast=false` to get a static-V8 debug
build instead.
## Before opening a PR
+8 -18
View File
@@ -6,9 +6,8 @@ BC := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
# option test filter make test F="server"
F=
# Extra flags forwarded to every `$(ZIG) build` invocation. Most commonly used
# to point at a prebuilt V8 archive and skip the multi-minute source rebuild:
# ZIGFLAGS=-Dprebuilt_v8_path=/path/to/libc_v8.a make test
# Extra flags forwarded to every `$(ZIG) build` invocation, e.g.:
# ZIGFLAGS=-Ddev_fast=false make test
ZIGFLAGS ?=
# OS and ARCH
@@ -36,7 +35,8 @@ endif
# Prebuilt V8
# -----------
# Building V8 from source takes 10+ minutes. `make download-v8` fetches the
# matching prebuilt archive from the zig-v8-fork releases instead. The versions
# matching prebuilt archive from the zig-v8-fork releases instead; build.zig
# discovers the cached files itself, so the target only fetches. The versions
# are read from the install action so they can't drift from CI.
#
# The cache path is keyed on ZIG_V8_TAG as well as the archive name: a
@@ -57,14 +57,6 @@ V8_CACHE := .lp-cache/prebuilt-v8/$(ZIG_V8_TAG)/$(V8_ARCHIVE)
V8_SO_ASSET := libc_v8_$(V8_VERSION)_$(OS)_$(ARCH).so
V8_SO_CACHE := .lp-cache/prebuilt-v8/$(ZIG_V8_TAG)/libc_v8.so
# If the prebuilt archive is in place and the caller hasn't set ZIGFLAGS, point
# the build at it rather than building V8 from source.
ifeq ($(strip $(ZIGFLAGS)),)
ifneq ($(wildcard $(V8_CACHE)),)
ZIGFLAGS := -Dprebuilt_v8_path=$(V8_CACHE)
endif
endif
# Infos
# -----
@@ -85,9 +77,9 @@ help:
# $(ZIG) commands
# ------------
.PHONY: build build-v8-snapshot build-dev download-v8 download-v8-shared run run-release test bench data end2end clean
.PHONY: build build-v8-snapshot build-dev download-v8 run run-release test bench data end2end clean
## Download the prebuilt V8 archive (skips the 10+ min source build)
## Download the prebuilt V8 libraries (skips the 10+ min source build)
download-v8:
@mkdir -p $(dir $(V8_CACHE))
@test -f $(V8_CACHE) || ( \
@@ -96,16 +88,14 @@ download-v8:
https://github.com/lightpanda-io/zig-v8-fork/releases/download/$(ZIG_V8_TAG)/$(V8_ARCHIVE) \
|| (rm -f $(V8_CACHE); printf "\033[33mDownload ERROR\033[0m\n"; exit 1) )
@printf "\033[33mV8 ready: %s\033[0m\n" "$(V8_CACHE)"
## Download the prebuilt shared V8 used by -Ddev_fast builds (Linux x86_64)
download-v8-shared:
@mkdir -p $(dir $(V8_SO_CACHE))
ifeq ($(OS)_$(ARCH),linux_x86_64)
@test -f $(V8_SO_CACHE) || ( \
printf "\033[36mDownloading prebuilt shared V8 $(V8_VERSION) ($(ZIG_V8_TAG))...\033[0m\n"; \
curl -fL --progress-bar -o $(V8_SO_CACHE) \
https://github.com/lightpanda-io/zig-v8-fork/releases/download/$(ZIG_V8_TAG)/$(V8_SO_ASSET) \
|| (rm -f $(V8_SO_CACHE); printf "\033[33mDownload ERROR\033[0m\n"; exit 1) )
@printf "\033[33mShared V8 ready: %s\033[0m\n" "$(V8_SO_CACHE)"
endif
## Build v8 snapshot
build-v8-snapshot:
+75 -46
View File
@@ -1,4 +1,4 @@
// Copyright (C) 2023-2024 Lightpanda (Selecy SAS)
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
//
// Francis Bouvier <francis@lightpanda.io>
// Pierre Tachoire <pierre@lightpanda.io>
@@ -37,21 +37,19 @@ const Build = blk: {
pub fn build(b: *Build) !void {
const optimize = b.standardOptimizeOption(.{});
const requested_target = b.standardTargetOptions(.{});
// The three settings below only work as a set, so they get one knob rather
// than three defaults: the self-hosted backend needs the shared V8 (it
// cannot apply the CREL relocations in the archive) and needs zig's own CRT
// (a system crt1.o with SFrame unwind data has relocations its linker does
// not handle). Debug-only, opt-in, and a good deal faster to rebuild.
const dev_fast = b.option(bool, "dev_fast", "Linux debug builds: shared V8 + self-hosted backend. Implies -Dshared_v8, -Duse_llvm=false and a bundled-CRT target") orelse false;
const enable_tsan = b.option(bool, "tsan", "Enable Thread Sanitizer") orelse false;
const enable_asan = b.option(bool, "asan", "Enable Address Sanitizer") orelse false;
const enable_csan = b.option(std.zig.SanitizeC, "csan", "Enable C Sanitizers");
const target = if (dev_fast) b.resolveTargetQuery(.{
.cpu_arch = .x86_64,
.os_tag = .linux,
.abi = .gnu,
// https://codeberg.org/ziglang/zig/issues/31272
.glibc_version = .{ .major = 2, .minor = 43, .patch = 0 },
}) else b.standardTargetOptions(.{});
const prebuilt_v8_path_option = b.option([]const u8, "prebuilt_v8_path", "Path to a prebuilt libc_v8.a or libc_v8.so");
const dev_fast = b.option(bool, "dev_fast", "Linux debug builds: shared V8 + self-hosted backend. Implies -Dshared_v8, -Duse_llvm=false and a bundled-CRT target") orelse
(builtin.os.tag == .linux and builtin.cpu.arch == .x86_64 and
optimize == .Debug and requested_target.query.isNative() and
!enable_tsan and !enable_asan and
(prebuilt_v8_path_option == null or std.mem.endsWith(u8, prebuilt_v8_path_option.?, ".so")));
if (dev_fast) {
if (builtin.os.tag != .linux) {
@@ -62,16 +60,27 @@ pub fn build(b: *Build) !void {
std.debug.print("-Ddev_fast is Debug-only (optimize is {s})\n", .{@tagName(optimize)});
return error.DevFastRequiresDebug;
}
if (!requested_target.query.isNative()) {
std.debug.print("-Ddev_fast builds for the host; drop -Dtarget/-Dcpu\n", .{});
return error.DevFastRequiresNativeTarget;
}
}
// dev_fast links V8 shared, so it defaults to the libc_v8.so that
// `make download-v8-shared` caches (when present) rather than a
// multi-minute from-source build.
const prebuilt_v8_path = b.option([]const u8, "prebuilt_v8_path", "Path to a prebuilt libc_v8.a or libc_v8.so") orelse
if (dev_fast) findSharedV8Cache(b) else null;
const target = if (dev_fast) b.resolveTargetQuery(.{
.cpu_arch = .x86_64,
.os_tag = .linux,
.abi = .gnu,
// https://codeberg.org/ziglang/zig/issues/31272
.glibc_version = .{ .major = 2, .minor = 43, .patch = 0 },
}) else requested_target;
// Without an explicit -Dprebuilt_v8_path, pick up whatever `make
// download-v8` cached rather than building V8 from source.
const prebuilt_v8_path = prebuilt_v8_path_option orelse if (enable_tsan or enable_asan) null else findPrebuiltV8(b, target, dev_fast);
const snapshot_path = b.option([]const u8, "snapshot_path", "Path to v8 snapshot");
const wpt_extensions = b.option(bool, "wpt_extensions", "Extend WebAPI with WPT driver behavior") orelse false;
const shared_v8 = b.option(bool, "shared_v8", "Link V8 as a shared library") orelse dev_fast;
const shared_v8 = b.option(bool, "shared_v8", "Link V8 as a shared library") orelse
(dev_fast or (prebuilt_v8_path != null and std.mem.endsWith(u8, prebuilt_v8_path.?, ".so")));
const use_llvm = b.option(bool, "use_llvm", "Use the LLVM backend") orelse !dev_fast;
const version = resolveVersion(b);
@@ -86,10 +95,6 @@ pub fn build(b: *Build) !void {
opts.addOption(?[]const u8, "snapshot_path", snapshot_path);
opts.addOption(bool, "wpt_extensions", wpt_extensions);
const enable_tsan = b.option(bool, "tsan", "Enable Thread Sanitizer") orelse false;
const enable_asan = b.option(bool, "asan", "Enable Address Sanitizer") orelse false;
const enable_csan = b.option(std.zig.SanitizeC, "csan", "Enable C Sanitizers");
const lightpanda_module = blk: {
const mod = b.addModule("lightpanda", .{
.root_source_file = b.path("src/lightpanda.zig"),
@@ -251,10 +256,10 @@ pub fn build(b: *Build) !void {
}
}
/// Looks for the shared V8 that `make download-v8-shared` caches. The cache
/// Looks for the prebuilt V8 that `make download-v8` caches. The cache
/// path is keyed on the zig-v8 release tag, read from the install action so
/// it cannot drift from CI (the Makefile reads the same source of truth).
fn findSharedV8Cache(b: *Build) ?[]const u8 {
fn findPrebuiltV8(b: *Build, target: Build.ResolvedTarget, dev_fast: bool) ?[]const u8 {
const io = b.graph.io;
const action = std.Io.Dir.cwd().readFileAlloc(
io,
@@ -263,32 +268,56 @@ fn findSharedV8Cache(b: *Build) ?[]const u8 {
.limited(64 * 1024),
) catch return null;
const tag = blk: {
var in_zig_v8 = false;
var lines = std.mem.splitScalar(u8, action, '\n');
while (lines.next()) |line| {
if (std.mem.startsWith(u8, line, " ") and !std.mem.startsWith(u8, line, " ")) {
in_zig_v8 = std.mem.eql(u8, std.mem.trimEnd(u8, line, " \r"), " zig-v8:");
continue;
}
if (!in_zig_v8) continue;
const trimmed = std.mem.trim(u8, line, " \r");
if (std.mem.startsWith(u8, trimmed, "default:")) {
var it = std.mem.splitScalar(u8, trimmed, '\'');
_ = it.next();
break :blk it.next() orelse return null;
}
const tag = actionDefault(action, "zig-v8:") orelse return null;
if (tag.len == 0) {
return null;
}
// The .so must keep the name the exe's DT_NEEDED records; the archive
// name encodes V8 version, os and arch.
const path = if (dev_fast)
b.fmt("{s}/prebuilt-v8/{s}/libc_v8.so", .{ b.pathFromRoot(".lp-cache"), tag })
else blk: {
const version = actionDefault(action, "v8:") orelse return null;
break :blk b.fmt("{s}/prebuilt-v8/{s}/libc_v8_{s}_{s}_{s}.a", .{
b.pathFromRoot(".lp-cache"),
tag,
version,
@tagName(target.result.os.tag),
@tagName(target.result.cpu.arch),
});
};
std.Io.Dir.cwd().access(io, path, .{}) catch {
if (dev_fast) {
std.debug.print("No cached libc_v8.so; building V8 from source. `make download-v8` skips this.\n", .{});
}
return null;
};
if (tag.len == 0) return null;
const path = b.fmt("{s}/prebuilt-v8/{s}/libc_v8.so", .{ b.pathFromRoot(".lp-cache"), tag });
std.Io.Dir.cwd().access(io, path, .{}) catch return null;
std.debug.print("Using prebuilt shared V8: {s}\n", .{path});
std.debug.print("Using prebuilt V8: {s}\n", .{path});
return path;
}
// Returns the quoted `default:` value of a top-level `key` in the install
// action's yaml.
fn actionDefault(action: []const u8, key: []const u8) ?[]const u8 {
var in_key = false;
var lines = std.mem.splitScalar(u8, action, '\n');
while (lines.next()) |line| {
if (std.mem.startsWith(u8, line, " ") and !std.mem.startsWith(u8, line, " ")) {
in_key = std.mem.eql(u8, std.mem.trimEnd(u8, line[2..], " \r"), key);
continue;
}
if (!in_key) continue;
const trimmed = std.mem.trim(u8, line, " \r");
if (std.mem.startsWith(u8, trimmed, "default:")) {
var it = std.mem.splitScalar(u8, trimmed, '\'');
_ = it.next();
return it.next();
}
}
return null;
}
fn linkV8(
b: *Build,
mod: *Build.Module,