Files
browser/build.zig
Karl Seguin 2feba3182a Replace std.log with a structured logger
Outputs in logfmt in release and a "pretty" print in debug mode. The format
along with the log level will become arguments to the binary at some point in
the future.
2025-05-27 19:57:58 +08:00

268 lines
8.2 KiB
Zig

// Copyright (C) 2023-2024 Lightpanda (Selecy SAS)
//
// Francis Bouvier <francis@lightpanda.io>
// Pierre Tachoire <pierre@lightpanda.io>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const builtin = @import("builtin");
/// Do not rename this constant. It is scanned by some scripts to determine
/// which zig version to install.
const recommended_zig_version = "0.14.1";
pub fn build(b: *std.Build) !void {
switch (comptime builtin.zig_version.order(std.SemanticVersion.parse(recommended_zig_version) catch unreachable)) {
.eq => {},
.lt => {
@compileError("The minimum version of Zig required to compile is '" ++ recommended_zig_version ++ "', found '" ++ builtin.zig_version_string ++ "'.");
},
.gt => {
std.debug.print(
"WARNING: Recommended Zig version '{s}', but found '{s}', build may fail...\n\n",
.{ recommended_zig_version, builtin.zig_version_string },
);
},
}
var opts = b.addOptions();
opts.addOption(
[]const u8,
"git_commit",
b.option([]const u8, "git_commit", "Current git commit") orelse "dev",
);
{
const log = @import("src/log.zig");
opts.addOption(
log.Level,
"log_level",
b.option(log.Level, "log_level", "The log level") orelse .info,
);
}
opts.addOption(
bool,
"log_unknown_properties",
b.option(bool, "log_unknown_properties", "Log access to unknown properties") orelse false,
);
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
{
// browser
// -------
// compile and install
const exe = b.addExecutable(.{
.name = "lightpanda",
.target = target,
.optimize = optimize,
.root_source_file = b.path("src/main.zig"),
});
try common(b, opts, exe);
b.installArtifact(exe);
// run
const run_cmd = b.addRunArtifact(exe);
if (b.args) |args| {
run_cmd.addArgs(args);
}
// step
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
}
{
// get v8
// -------
const v8 = b.dependency("v8", .{ .target = target, .optimize = optimize });
const get_v8 = b.addRunArtifact(v8.artifact("get-v8"));
const get_step = b.step("get-v8", "Get v8");
get_step.dependOn(&get_v8.step);
}
{
// build v8
// -------
const v8 = b.dependency("v8", .{ .target = target, .optimize = optimize });
const build_v8 = b.addRunArtifact(v8.artifact("build-v8"));
const build_step = b.step("build-v8", "Build v8");
build_step.dependOn(&build_v8.step);
}
{
// tests
// ----
// compile
const tests = b.addTest(.{
.root_source_file = b.path("src/main.zig"),
.test_runner = .{ .path = b.path("src/test_runner.zig"), .mode = .simple },
.target = target,
.optimize = optimize,
});
try common(b, opts, tests);
const run_tests = b.addRunArtifact(tests);
if (b.args) |args| {
run_tests.addArgs(args);
}
// step
const tests_step = b.step("test", "Run unit tests");
tests_step.dependOn(&run_tests.step);
}
{
// wpt
// -----
// compile and install
const wpt = b.addExecutable(.{
.name = "lightpanda-wpt",
.root_source_file = b.path("src/main_wpt.zig"),
.target = target,
.optimize = optimize,
});
try common(b, opts, wpt);
// run
const wpt_cmd = b.addRunArtifact(wpt);
if (b.args) |args| {
wpt_cmd.addArgs(args);
}
// step
const wpt_step = b.step("wpt", "WPT tests");
wpt_step.dependOn(&wpt_cmd.step);
}
}
fn common(b: *std.Build, opts: *std.Build.Step.Options, step: *std.Build.Step.Compile) !void {
const mod = step.root_module;
const target = mod.resolved_target.?;
const optimize = mod.optimize.?;
const dep_opts = .{ .target = target, .optimize = optimize };
try moduleNetSurf(b, step, target);
mod.addImport("tls", b.dependency("tls", dep_opts).module("tls"));
mod.addImport("tigerbeetle-io", b.dependency("tigerbeetle_io", .{}).module("tigerbeetle_io"));
{
// v8
const v8_opts = b.addOptions();
v8_opts.addOption(bool, "inspector_subtype", false);
const v8_mod = b.dependency("v8", dep_opts).module("v8");
v8_mod.addOptions("default_exports", v8_opts);
mod.addImport("v8", v8_mod);
}
mod.link_libcpp = true;
{
const release_dir = if (mod.optimize.? == .Debug) "debug" else "release";
const os = switch (target.result.os.tag) {
.linux => "linux",
.macos => "macos",
else => return error.UnsupportedPlatform,
};
var lib_path = try std.fmt.allocPrint(
mod.owner.allocator,
"v8/out/{s}/{s}/obj/zig/libc_v8.a",
.{ os, release_dir },
);
std.fs.cwd().access(lib_path, .{}) catch {
// legacy path
lib_path = try std.fmt.allocPrint(
mod.owner.allocator,
"v8/out/{s}/obj/zig/libc_v8.a",
.{release_dir},
);
};
mod.addObjectFile(mod.owner.path(lib_path));
}
switch (target.result.os.tag) {
.macos => {
// v8 has a dependency, abseil-cpp, which, on Mac, uses CoreFoundation
mod.addSystemFrameworkPath(.{ .cwd_relative = "/System/Library/Frameworks" });
mod.linkFramework("CoreFoundation", .{});
},
else => {},
}
mod.addImport("build_config", opts.createModule());
}
fn moduleNetSurf(b: *std.Build, step: *std.Build.Step.Compile, target: std.Build.ResolvedTarget) !void {
const os = target.result.os.tag;
const arch = target.result.cpu.arch;
// iconv
const libiconv_lib_path = try std.fmt.allocPrint(
b.allocator,
"vendor/libiconv/out/{s}-{s}/lib/libiconv.a",
.{ @tagName(os), @tagName(arch) },
);
const libiconv_include_path = try std.fmt.allocPrint(
b.allocator,
"vendor/libiconv/out/{s}-{s}/lib/libiconv.a",
.{ @tagName(os), @tagName(arch) },
);
step.addObjectFile(b.path(libiconv_lib_path));
step.addIncludePath(b.path(libiconv_include_path));
{
// mimalloc
const mimalloc = "vendor/mimalloc";
const lib_path = try std.fmt.allocPrint(
b.allocator,
mimalloc ++ "/out/{s}-{s}/lib/libmimalloc.a",
.{ @tagName(os), @tagName(arch) },
);
step.addObjectFile(b.path(lib_path));
step.addIncludePath(b.path(mimalloc ++ "/include"));
}
// netsurf libs
const ns = "vendor/netsurf";
const ns_include_path = try std.fmt.allocPrint(
b.allocator,
ns ++ "/out/{s}-{s}/include",
.{ @tagName(os), @tagName(arch) },
);
step.addIncludePath(b.path(ns_include_path));
const libs: [4][]const u8 = .{
"libdom",
"libhubbub",
"libparserutils",
"libwapcaplet",
};
inline for (libs) |lib| {
const ns_lib_path = try std.fmt.allocPrint(
b.allocator,
ns ++ "/out/{s}-{s}/lib/" ++ lib ++ ".a",
.{ @tagName(os), @tagName(arch) },
);
step.addObjectFile(b.path(ns_lib_path));
step.addIncludePath(b.path(ns ++ "/" ++ lib ++ "/src"));
}
}