mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-06-11 01:25:53 -04:00
1037 lines
41 KiB
Zig
1037 lines
41 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");
|
|
|
|
const lightpanda_version = std.SemanticVersion.parse(@import("build.zig.zon").version) catch unreachable;
|
|
const min_zig_version = std.SemanticVersion.parse(@import("build.zig.zon").minimum_zig_version) catch unreachable;
|
|
|
|
const Build = blk: {
|
|
if (builtin.zig_version.order(min_zig_version) == .lt) {
|
|
const message = std.fmt.comptimePrint(
|
|
\\Zig version is too old:
|
|
\\ current Zig version: {f}
|
|
\\ minimum Zig version: {f}
|
|
, .{ builtin.zig_version, min_zig_version });
|
|
@compileError(message);
|
|
} else {
|
|
break :blk std.Build;
|
|
}
|
|
};
|
|
|
|
pub fn build(b: *Build) !void {
|
|
const target = b.standardTargetOptions(.{});
|
|
const optimize = b.standardOptimizeOption(.{});
|
|
|
|
const prebuilt_v8_path = b.option([]const u8, "prebuilt_v8_path", "Path to prebuilt libc_v8.a");
|
|
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 version = resolveVersion(b);
|
|
var stderr = std.fs.File.stderr().writer(&.{});
|
|
try stderr.interface.print("Lightpanda {f}\n", .{version});
|
|
|
|
const version_string = b.fmt("{f}", .{version});
|
|
const version_encoded = std.mem.replaceOwned(u8, b.allocator, version_string, "+", "%2B") catch @panic("OOM");
|
|
|
|
var opts = b.addOptions();
|
|
opts.addOption([]const u8, "version", version_string);
|
|
opts.addOption([]const u8, "version_encoded", version_encoded);
|
|
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"),
|
|
.target = target,
|
|
.optimize = optimize,
|
|
.link_libc = true,
|
|
.link_libcpp = true,
|
|
.sanitize_c = enable_csan,
|
|
.sanitize_thread = enable_tsan,
|
|
});
|
|
mod.addImport("lightpanda", mod); // allow circular "lightpanda" import
|
|
mod.addImport("build_config", opts.createModule());
|
|
|
|
// Format check
|
|
const fmt_step = b.step("fmt", "Check code formatting");
|
|
const fmt = b.addFmt(.{
|
|
.paths = &.{ "src", "build.zig", "build.zig.zon" },
|
|
.check = true,
|
|
});
|
|
fmt_step.dependOn(&fmt.step);
|
|
|
|
// Set default behavior
|
|
b.default_step.dependOn(fmt_step);
|
|
|
|
try linkV8(b, mod, enable_asan, enable_tsan, prebuilt_v8_path);
|
|
try linkCurl(b, mod, enable_tsan);
|
|
try linkHtml5Ever(b, mod);
|
|
linkZenai(b, mod);
|
|
linkIsocline(b, mod);
|
|
|
|
break :blk mod;
|
|
};
|
|
|
|
linkSqlite(b, lightpanda_module, enable_csan, enable_tsan);
|
|
|
|
// Check compilation
|
|
const check = b.step("check", "Check if lightpanda compiles");
|
|
|
|
const check_lib = b.addLibrary(.{
|
|
.name = "lightpanda_check",
|
|
.root_module = lightpanda_module,
|
|
});
|
|
check.dependOn(&check_lib.step);
|
|
|
|
// Extras (snapshot_creator) are off the default install to
|
|
// avoid paying for three exe compiles on every edit. Build explicitly
|
|
// with `zig build extras`.
|
|
const extras_step = b.step("extras", "Build snapshot_creator");
|
|
|
|
{
|
|
// browser
|
|
const exe = b.addExecutable(.{
|
|
.name = "lightpanda",
|
|
.use_llvm = true,
|
|
.root_module = b.createModule(.{
|
|
.root_source_file = b.path("src/main.zig"),
|
|
.target = target,
|
|
.optimize = optimize,
|
|
.sanitize_c = enable_csan,
|
|
.sanitize_thread = enable_tsan,
|
|
.imports = &.{
|
|
.{ .name = "lightpanda", .module = lightpanda_module },
|
|
},
|
|
}),
|
|
});
|
|
b.installArtifact(exe);
|
|
|
|
const exe_check = b.addLibrary(.{
|
|
.name = "lightpanda_exe_check",
|
|
.root_module = exe.root_module,
|
|
});
|
|
check.dependOn(&exe_check.step);
|
|
|
|
const run_cmd = b.addRunArtifact(exe);
|
|
if (b.args) |args| {
|
|
run_cmd.addArgs(args);
|
|
}
|
|
const run_step = b.step("run", "Run the app");
|
|
run_step.dependOn(&run_cmd.step);
|
|
|
|
const version_info_step = b.step("version", "Print the resolved version information");
|
|
const version_info_run = b.addRunArtifact(exe);
|
|
version_info_run.addArg("version");
|
|
version_info_step.dependOn(&version_info_run.step);
|
|
}
|
|
|
|
{
|
|
// snapshot creator
|
|
const exe = b.addExecutable(.{
|
|
.name = "lightpanda-snapshot-creator",
|
|
.use_llvm = true,
|
|
.root_module = b.createModule(.{
|
|
.root_source_file = b.path("src/main_snapshot_creator.zig"),
|
|
.target = target,
|
|
.optimize = optimize,
|
|
.imports = &.{
|
|
.{ .name = "lightpanda", .module = lightpanda_module },
|
|
},
|
|
}),
|
|
});
|
|
extras_step.dependOn(&b.addInstallArtifact(exe, .{}).step);
|
|
|
|
const exe_check = b.addLibrary(.{
|
|
.name = "snapshot_creator_check",
|
|
.root_module = exe.root_module,
|
|
});
|
|
check.dependOn(&exe_check.step);
|
|
|
|
const run_cmd = b.addRunArtifact(exe);
|
|
if (b.args) |args| {
|
|
run_cmd.addArgs(args);
|
|
}
|
|
const run_step = b.step("snapshot_creator", "Generate a v8 snapshot");
|
|
run_step.dependOn(&run_cmd.step);
|
|
}
|
|
|
|
{
|
|
// test
|
|
const tests = b.addTest(.{
|
|
.root_module = lightpanda_module,
|
|
.use_llvm = true,
|
|
.test_runner = .{ .path = b.path("src/test_runner.zig"), .mode = .simple },
|
|
});
|
|
const run_tests = b.addRunArtifact(tests);
|
|
const test_step = b.step("test", "Run unit tests");
|
|
test_step.dependOn(&run_tests.step);
|
|
}
|
|
}
|
|
|
|
fn linkV8(
|
|
b: *Build,
|
|
mod: *Build.Module,
|
|
is_asan: bool,
|
|
is_tsan: bool,
|
|
prebuilt_v8_path: ?[]const u8,
|
|
) !void {
|
|
const target = mod.resolved_target.?;
|
|
|
|
const dep = b.dependency("v8", .{
|
|
.target = target,
|
|
.optimize = mod.optimize.?,
|
|
.is_asan = is_asan,
|
|
.is_tsan = is_tsan,
|
|
.inspector_subtype = false,
|
|
.v8_enable_sandbox = is_tsan,
|
|
.cache_root = b.pathFromRoot(".lp-cache"),
|
|
.prebuilt_v8_path = prebuilt_v8_path,
|
|
});
|
|
mod.addImport("v8", dep.module("v8"));
|
|
}
|
|
|
|
fn linkHtml5Ever(b: *Build, mod: *Build.Module) !void {
|
|
const is_debug = if (mod.optimize.? == .Debug) true else false;
|
|
|
|
const exec_cargo = b.addSystemCommand(&.{
|
|
"cargo", "build",
|
|
"--profile", if (is_debug) "dev" else "release",
|
|
"--manifest-path", "src/html5ever/Cargo.toml",
|
|
});
|
|
|
|
// Track Rust sources so edits invalidate the cargo step's cache.
|
|
// Without this, Zig keys the step on argv only and won't re-run cargo
|
|
// when lib.rs/Cargo.toml change.
|
|
for ([_][]const u8{
|
|
"src/html5ever/Cargo.toml",
|
|
"src/html5ever/Cargo.lock",
|
|
"src/html5ever/lib.rs",
|
|
"src/html5ever/sink.rs",
|
|
"src/html5ever/types.rs",
|
|
}) |path| {
|
|
exec_cargo.addFileInput(b.path(path));
|
|
}
|
|
|
|
// TODO: We can prefer `--artifact-dir` once it become stable.
|
|
const out_dir = exec_cargo.addPrefixedOutputDirectoryArg("--target-dir=", "html5ever");
|
|
|
|
const html5ever_step = b.step("html5ever", "Install html5ever dependency (requires cargo)");
|
|
html5ever_step.dependOn(&exec_cargo.step);
|
|
|
|
const obj = out_dir.path(b, if (is_debug) "debug" else "release").path(b, "liblitefetch_html5ever.a");
|
|
mod.addObjectFile(obj);
|
|
}
|
|
|
|
fn linkSqlite(b: *Build, mod: *Build.Module, enable_csan: ?std.zig.SanitizeC, is_tsan: bool) void {
|
|
const dep = b.dependency("sqlite3", .{
|
|
.target = mod.resolved_target.?,
|
|
.optimize = mod.optimize.?,
|
|
});
|
|
|
|
const lib = dep.artifact("sqlite3");
|
|
lib.root_module.sanitize_c = enable_csan;
|
|
lib.root_module.sanitize_thread = is_tsan;
|
|
|
|
const macros = [_]struct { []const u8, []const u8 }{
|
|
.{ "SQLITE_DEFAULT_FILE_PERMISSIONS", "0600" },
|
|
.{ "SQLITE_DEFAULT_MEMSTATUS", "0" },
|
|
.{ "SQLITE_DEFAULT_WAL_SYNCHRONOUS", "1" },
|
|
.{ "SQLITE_DQS", "0" },
|
|
.{ "SQLITE_ENABLE_API_ARMOR", "1" },
|
|
.{ "SQLITE_ENABLE_UNLOCK_NOTIFY", "1" },
|
|
.{ "SQLITE_TEMP_STORE", "3" },
|
|
.{ "SQLITE_THREADSAFE", "1" },
|
|
.{ "SQLITE_UNTESTABLE", "1" },
|
|
.{ "SQLITE_USE_ALLOCA", "1" },
|
|
.{ "SQLITE_OMIT_AUTHORIZATION", "1" },
|
|
.{ "SQLITE_OMIT_AUTOMATIC_INDEX", "1" },
|
|
.{ "SQLITE_OMIT_AUTORESET", "1" },
|
|
.{ "SQLITE_OMIT_AUTOVACUUM", "1" },
|
|
.{ "SQLITE_OMIT_BETWEEN_OPTIMIZATION", "1" },
|
|
.{ "SQLITE_OMIT_CASE_SENSITIVE_LIKE_PRAGMA", "1" },
|
|
.{ "SQLITE_OMIT_COMPLETE", "1" },
|
|
.{ "SQLITE_OMIT_DECLTYPE", "1" },
|
|
.{ "SQLITE_OMIT_DEPRECATED", "1" },
|
|
.{ "SQLITE_OMIT_DESERIALIZE", "1" },
|
|
.{ "SQLITE_OMIT_GET_TABLE", "1" },
|
|
.{ "SQLITE_OMIT_INCRBLOB", "1" },
|
|
.{ "SQLITE_OMIT_JSON", "1" },
|
|
.{ "SQLITE_OMIT_LIKE_OPTIMIZATION", "1" },
|
|
.{ "SQLITE_OMIT_LOAD_EXTENSION", "1" },
|
|
.{ "SQLITE_OMIT_PROGRESS_CALLBACK", "1" },
|
|
.{ "SQLITE_OMIT_SHARED_CACHE", "1" },
|
|
.{ "SQLITE_OMIT_TCL_VARIABLE", "1" },
|
|
.{ "SQLITE_OMIT_TEMPDB", "1" },
|
|
.{ "SQLITE_OMIT_TRACE", "1" },
|
|
.{ "SQLITE_OMIT_UTF16", "1" },
|
|
.{ "SQLITE_OMIT_XFER_OPT", "1" },
|
|
};
|
|
for (macros) |m| {
|
|
lib.root_module.addCMacro(m[0], m[1]);
|
|
}
|
|
|
|
mod.linkLibrary(lib);
|
|
}
|
|
|
|
fn linkCurl(b: *Build, mod: *Build.Module, is_tsan: bool) !void {
|
|
const target = mod.resolved_target.?;
|
|
|
|
const curl = buildCurl(b, target, mod.optimize.?, is_tsan);
|
|
mod.linkLibrary(curl);
|
|
|
|
const zlib = buildZlib(b, target, mod.optimize.?, is_tsan);
|
|
curl.root_module.linkLibrary(zlib);
|
|
|
|
const brotli = buildBrotli(b, target, mod.optimize.?, is_tsan);
|
|
for (brotli) |lib| curl.root_module.linkLibrary(lib);
|
|
|
|
const nghttp2 = buildNghttp2(b, target, mod.optimize.?, is_tsan);
|
|
curl.root_module.linkLibrary(nghttp2);
|
|
|
|
const boringssl = buildBoringSsl(b, target, mod.optimize.?);
|
|
for (boringssl) |lib| curl.root_module.linkLibrary(lib);
|
|
|
|
const libidn2 = buildLibidn2(b, target, mod.optimize.?, is_tsan);
|
|
curl.root_module.linkLibrary(libidn2);
|
|
// Also expose libidn2 to the consuming module so src/sys/idna.zig's
|
|
// @cImport of <idn2.h> resolves. Without this, lightpanda_module only
|
|
// sees idn2.h transitively if a system libidn2 happens to be installed.
|
|
mod.linkLibrary(libidn2);
|
|
|
|
switch (target.result.os.tag) {
|
|
.macos => {
|
|
// needed for proxying on mac
|
|
mod.addSystemFrameworkPath(.{ .cwd_relative = "/System/Library/Frameworks" });
|
|
mod.linkFramework("CoreFoundation", .{});
|
|
mod.linkFramework("SystemConfiguration", .{});
|
|
},
|
|
else => {},
|
|
}
|
|
}
|
|
|
|
fn buildZlib(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, is_tsan: bool) *Build.Step.Compile {
|
|
const dep = b.dependency("zlib", .{});
|
|
|
|
const mod = b.createModule(.{
|
|
.target = target,
|
|
.optimize = optimize,
|
|
.link_libc = true,
|
|
.sanitize_thread = is_tsan,
|
|
});
|
|
|
|
const lib = b.addLibrary(.{ .name = "z", .root_module = mod });
|
|
lib.installHeadersDirectory(dep.path(""), "", .{});
|
|
lib.addCSourceFiles(.{
|
|
.root = dep.path(""),
|
|
.flags = &.{
|
|
"-DHAVE_SYS_TYPES_H",
|
|
"-DHAVE_STDINT_H",
|
|
"-DHAVE_STDDEF_H",
|
|
"-DHAVE_UNISTD_H",
|
|
},
|
|
.files = &.{
|
|
"adler32.c", "compress.c", "crc32.c",
|
|
"deflate.c", "gzclose.c", "gzlib.c",
|
|
"gzread.c", "gzwrite.c", "infback.c",
|
|
"inffast.c", "inflate.c", "inftrees.c",
|
|
"trees.c", "uncompr.c", "zutil.c",
|
|
},
|
|
});
|
|
|
|
return lib;
|
|
}
|
|
|
|
fn buildBrotli(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, is_tsan: bool) [3]*Build.Step.Compile {
|
|
const dep = b.dependency("brotli", .{});
|
|
|
|
const mod = b.createModule(.{
|
|
.target = target,
|
|
.optimize = optimize,
|
|
.link_libc = true,
|
|
.sanitize_thread = is_tsan,
|
|
});
|
|
mod.addIncludePath(dep.path("c/include"));
|
|
|
|
const brotlicmn = b.addLibrary(.{ .name = "brotlicommon", .root_module = mod });
|
|
const brotlidec = b.addLibrary(.{ .name = "brotlidec", .root_module = mod });
|
|
const brotlienc = b.addLibrary(.{ .name = "brotlienc", .root_module = mod });
|
|
|
|
brotlicmn.installHeadersDirectory(dep.path("c/include/brotli"), "brotli", .{});
|
|
brotlicmn.addCSourceFiles(.{
|
|
.root = dep.path("c/common"),
|
|
.files = &.{
|
|
"transform.c", "shared_dictionary.c", "platform.c",
|
|
"dictionary.c", "context.c", "constants.c",
|
|
},
|
|
});
|
|
brotlidec.addCSourceFiles(.{
|
|
.root = dep.path("c/dec"),
|
|
.files = &.{
|
|
"bit_reader.c", "decode.c", "huffman.c",
|
|
"prefix.c", "state.c", "static_init.c",
|
|
},
|
|
});
|
|
brotlienc.addCSourceFiles(.{
|
|
.root = dep.path("c/enc"),
|
|
.files = &.{
|
|
"backward_references.c", "backward_references_hq.c", "bit_cost.c",
|
|
"block_splitter.c", "brotli_bit_stream.c", "cluster.c",
|
|
"command.c", "compound_dictionary.c", "compress_fragment.c",
|
|
"compress_fragment_two_pass.c", "dictionary_hash.c", "encode.c",
|
|
"encoder_dict.c", "entropy_encode.c", "fast_log.c",
|
|
"histogram.c", "literal_cost.c", "memory.c",
|
|
"metablock.c", "static_dict.c", "static_dict_lut.c",
|
|
"static_init.c", "utf8_util.c",
|
|
},
|
|
});
|
|
|
|
return .{ brotlicmn, brotlidec, brotlienc };
|
|
}
|
|
|
|
fn buildBoringSsl(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) [2]*Build.Step.Compile {
|
|
const dep = b.dependency("boringssl-zig", .{
|
|
.target = target,
|
|
.optimize = optimize,
|
|
.force_pic = true,
|
|
});
|
|
|
|
const ssl = dep.artifact("ssl");
|
|
ssl.bundle_ubsan_rt = false;
|
|
|
|
const crypto = dep.artifact("crypto");
|
|
crypto.bundle_ubsan_rt = false;
|
|
|
|
return .{ ssl, crypto };
|
|
}
|
|
|
|
fn buildNghttp2(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, is_tsan: bool) *Build.Step.Compile {
|
|
const dep = b.dependency("nghttp2", .{});
|
|
|
|
const mod = b.createModule(.{
|
|
.target = target,
|
|
.optimize = optimize,
|
|
.link_libc = true,
|
|
.sanitize_thread = is_tsan,
|
|
});
|
|
mod.addIncludePath(dep.path("lib/includes"));
|
|
|
|
const config = b.addConfigHeader(.{
|
|
.include_path = "nghttp2ver.h",
|
|
.style = .{ .cmake = dep.path("lib/includes/nghttp2/nghttp2ver.h.in") },
|
|
}, .{
|
|
.PACKAGE_VERSION = "1.68.90",
|
|
.PACKAGE_VERSION_NUM = 0x016890,
|
|
});
|
|
mod.addConfigHeader(config);
|
|
|
|
const lib = b.addLibrary(.{ .name = "nghttp2", .root_module = mod });
|
|
|
|
lib.installConfigHeader(config);
|
|
lib.installHeadersDirectory(dep.path("lib/includes/nghttp2"), "nghttp2", .{});
|
|
lib.addCSourceFiles(.{
|
|
.root = dep.path("lib"),
|
|
.flags = &.{
|
|
"-DNGHTTP2_STATICLIB",
|
|
"-DHAVE_TIME_H",
|
|
"-DHAVE_ARPA_INET_H",
|
|
"-DHAVE_NETINET_IN_H",
|
|
},
|
|
.files = &.{
|
|
"sfparse.c", "nghttp2_alpn.c", "nghttp2_buf.c",
|
|
"nghttp2_callbacks.c", "nghttp2_debug.c", "nghttp2_extpri.c",
|
|
"nghttp2_frame.c", "nghttp2_hd.c", "nghttp2_hd_huffman.c",
|
|
"nghttp2_hd_huffman_data.c", "nghttp2_helper.c", "nghttp2_http.c",
|
|
"nghttp2_map.c", "nghttp2_mem.c", "nghttp2_option.c",
|
|
"nghttp2_outbound_item.c", "nghttp2_pq.c", "nghttp2_priority_spec.c",
|
|
"nghttp2_queue.c", "nghttp2_rcbuf.c", "nghttp2_session.c",
|
|
"nghttp2_stream.c", "nghttp2_submit.c", "nghttp2_version.c",
|
|
"nghttp2_ratelim.c", "nghttp2_time.c",
|
|
},
|
|
});
|
|
|
|
return lib;
|
|
}
|
|
|
|
fn buildLibidn2(
|
|
b: *Build,
|
|
target: Build.ResolvedTarget,
|
|
optimize: std.builtin.OptimizeMode,
|
|
is_tsan: bool,
|
|
) *Build.Step.Compile {
|
|
const dep = b.dependency("libidn2", .{});
|
|
|
|
const os = target.result.os.tag;
|
|
const is_darwin = os.isDarwin();
|
|
|
|
const mod = b.createModule(.{
|
|
.target = target,
|
|
.optimize = optimize,
|
|
.link_libc = true,
|
|
.sanitize_thread = is_tsan,
|
|
});
|
|
|
|
// libidn2's autoconf+gnulib stack expects a config.h with hundreds of
|
|
// HAVE_*/_GL_ATTRIBUTE_* defines — including ~800 lines of attribute-
|
|
// detection macros emitted from gnulib-common.m4 via AH_VERBATIM. We
|
|
// vendor a single autoconf-generated config.h rather than try to
|
|
// reproduce that machinery in the Zig build system.
|
|
mod.addIncludePath(b.path("vendor/libidn2"));
|
|
|
|
// Substitute the gnulib-style .in.h templates. All @VAR@ in them are
|
|
// either DLL-visibility markers (empty for static POSIX) or
|
|
// HAVE_UNISTRING_WOE32DLL_H (0).
|
|
inline for (.{ "unitypes", "unistr", "uniconv", "unictype", "uninorm" }) |name| {
|
|
mod.addConfigHeader(renderUnistringHeader(b, dep, name));
|
|
}
|
|
|
|
mod.addIncludePath(dep.path("lib"));
|
|
mod.addIncludePath(dep.path("unistring"));
|
|
// gl/ holds gnulib helpers — only malloca and version-etc headers are
|
|
// referenced from the sources we compile; we don't need the full gl/ shim
|
|
// layer (system header replacements).
|
|
mod.addIncludePath(dep.path("gl"));
|
|
|
|
const lib = b.addLibrary(.{ .name = "idn2", .root_module = mod });
|
|
lib.installHeader(dep.path("lib/idn2.h"), "idn2.h");
|
|
|
|
if (is_darwin) {
|
|
// unistring's striconveh.c calls real iconv_*, which on macOS lives
|
|
// in libiconv (separate from libSystem). On glibc Linux iconv is in
|
|
// libc itself; on musl it would also need a separate -liconv.
|
|
mod.linkSystemLibrary("iconv", .{});
|
|
|
|
// libidn2's lib/lookup.c calls strchrnul() without including
|
|
// <string.h>; the prototype is declared in vendor/libidn2/config.h
|
|
// alongside the existing strverscmp shim. macOS libc lacked the
|
|
// symbol entirely before 15.4 — provide it here so the link
|
|
// succeeds. Mirrors how gl/strverscmp.c is wired up below.
|
|
lib.addCSourceFile(.{
|
|
.file = b.path("vendor/libidn2/darwin/strchrnul.c"),
|
|
.flags = &.{},
|
|
});
|
|
}
|
|
|
|
lib.addCSourceFiles(.{
|
|
.root = dep.path("lib"),
|
|
.flags = &.{ "-DHAVE_CONFIG_H", "-DIDN2_STATIC" },
|
|
.files = &.{
|
|
"bidi.c", "context.c", "data.c", "decode.c",
|
|
"error.c", "free.c", "idna.c", "lookup.c",
|
|
"punycode.c", "register.c", "tables.c", "tr46map.c",
|
|
"version.c",
|
|
},
|
|
});
|
|
lib.addCSourceFiles(.{
|
|
.root = dep.path("gl"),
|
|
.flags = &.{"-DHAVE_CONFIG_H"},
|
|
// malloca.c provides striconveha's stack-or-heap allocator; strverscmp
|
|
// is a glibc extension absent on macOS that lib/version.c needs.
|
|
.files = &.{ "malloca.c", "strverscmp.c" },
|
|
});
|
|
lib.addCSourceFiles(.{
|
|
.root = dep.path("unistring"),
|
|
.flags = &.{"-DHAVE_CONFIG_H"},
|
|
.files = &.{
|
|
"c-ctype.c", "c-strcasecmp.c", "c-strncasecmp.c",
|
|
"free.c", "iconv.c", "iconv_close.c",
|
|
"iconv_open.c", "localcharset.c", "stdlib.c",
|
|
"striconveh.c", "striconveha.c", "unistd.c",
|
|
"uniconv/u8-conv-from-enc.c", "uniconv/u8-strconv-from-enc.c", "uniconv/u8-strconv-from-locale.c",
|
|
"uniconv/u8-strconv-to-enc.c", "uniconv/u8-strconv-to-locale.c", "unictype/bidi_of.c",
|
|
"unictype/categ_M.c", "unictype/categ_none.c", "unictype/categ_of.c",
|
|
"unictype/categ_test.c", "unictype/combiningclass.c", "unictype/joiningtype_of.c",
|
|
"unictype/scripts.c", "uninorm/canonical-decomposition.c", "uninorm/composition.c",
|
|
"uninorm/decompose-internal.c", "uninorm/decomposition-table.c", "uninorm/nfc.c",
|
|
"uninorm/nfd.c", "uninorm/u32-normalize.c", "unistr/u32-cmp.c",
|
|
"unistr/u32-cpy-alloc.c", "unistr/u32-cpy.c", "unistr/u32-mbtouc-unsafe.c",
|
|
"unistr/u32-strlen.c", "unistr/u32-to-u8.c", "unistr/u32-uctomb.c",
|
|
"unistr/u8-check.c", "unistr/u8-mblen.c", "unistr/u8-mbtouc.c",
|
|
"unistr/u8-mbtouc-aux.c", "unistr/u8-mbtouc-unsafe.c", "unistr/u8-mbtouc-unsafe-aux.c",
|
|
"unistr/u8-mbtoucr.c", "unistr/u8-prev.c", "unistr/u8-strlen.c",
|
|
"unistr/u8-to-u32.c", "unistr/u8-uctomb.c", "unistr/u8-uctomb-aux.c",
|
|
},
|
|
});
|
|
|
|
return lib;
|
|
}
|
|
|
|
/// Process one of unistring's `.in.h` template headers into a real `.h`.
|
|
/// All `@VAR@` substitutions in these headers are either DLL-visibility markers
|
|
/// (empty for static POSIX builds) or `HAVE_UNISTRING_WOE32DLL_H` (0).
|
|
fn renderUnistringHeader(b: *Build, dep: *Build.Dependency, name: []const u8) *Build.Step.ConfigHeader {
|
|
const in_rel = b.fmt("unistring/{s}.in.h", .{name});
|
|
const out_name = b.fmt("{s}.h", .{name});
|
|
const lazy = dep.path(in_rel);
|
|
const path = lazy.getPath3(b, null);
|
|
|
|
const file = path.root_dir.handle.openFile(path.sub_path, .{}) catch |e| {
|
|
std.debug.panic("openFile {s}: {s}", .{ path.sub_path, @errorName(e) });
|
|
};
|
|
defer file.close();
|
|
const contents = file.readToEndAlloc(b.allocator, 4 << 20) catch @panic("OOM");
|
|
|
|
const ch = b.addConfigHeader(.{
|
|
.include_path = out_name,
|
|
.style = .{ .autoconf_at = lazy },
|
|
}, .{});
|
|
|
|
var seen = std.StringHashMap(void).init(b.allocator);
|
|
var i: usize = 0;
|
|
while (std.mem.indexOfScalarPos(u8, contents, i, '@')) |s| {
|
|
const a = s + 1;
|
|
const e = std.mem.indexOfScalarPos(u8, contents, a, '@') orelse break;
|
|
const var_name = contents[a..e];
|
|
if (!isAtConfigName(var_name)) {
|
|
// Stray '@' (e.g. an email address in a comment); advance past it
|
|
// alone so we don't mis-pair with a later '@'.
|
|
i = s + 1;
|
|
continue;
|
|
}
|
|
const owned = b.allocator.dupe(u8, var_name) catch @panic("OOM");
|
|
const gop = seen.getOrPut(owned) catch @panic("OOM");
|
|
if (!gop.found_existing) {
|
|
if (std.mem.eql(u8, var_name, "HAVE_UNISTRING_WOE32DLL_H")) {
|
|
ch.addValue(owned, c_int, 0);
|
|
} else {
|
|
ch.addValue(owned, []const u8, "");
|
|
}
|
|
}
|
|
i = e + 1;
|
|
}
|
|
return ch;
|
|
}
|
|
|
|
fn isAtConfigName(s: []const u8) bool {
|
|
if (s.len == 0) return false;
|
|
for (s, 0..) |c, idx| {
|
|
const ok = switch (c) {
|
|
'A'...'Z', '_' => true,
|
|
'0'...'9' => idx > 0,
|
|
else => false,
|
|
};
|
|
if (!ok) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
fn buildCurl(
|
|
b: *Build,
|
|
target: Build.ResolvedTarget,
|
|
optimize: std.builtin.OptimizeMode,
|
|
is_tsan: bool,
|
|
) *Build.Step.Compile {
|
|
const dep = b.dependency("curl", .{});
|
|
|
|
const mod = b.createModule(.{
|
|
.target = target,
|
|
.optimize = optimize,
|
|
.link_libc = true,
|
|
.sanitize_thread = is_tsan,
|
|
});
|
|
mod.addIncludePath(dep.path("lib"));
|
|
mod.addIncludePath(dep.path("include"));
|
|
|
|
const os = target.result.os.tag;
|
|
const abi = target.result.abi;
|
|
|
|
const is_gnu = abi.isGnu();
|
|
const is_ios = os == .ios;
|
|
const is_android = abi.isAndroid();
|
|
const is_linux = os == .linux;
|
|
const is_darwin = os.isDarwin();
|
|
const is_windows = os == .windows;
|
|
const is_netbsd = os == .netbsd;
|
|
const is_openbsd = os == .openbsd;
|
|
const is_freebsd = os == .freebsd;
|
|
|
|
const byte_size = struct {
|
|
fn it(b2: *std.Build, target2: Build.ResolvedTarget, name: []const u8, comptime ctype: std.Target.CType) []const u8 {
|
|
const size = target2.result.cTypeByteSize(ctype);
|
|
return std.fmt.allocPrint(b2.allocator, "#define SIZEOF_{s} {d}", .{ name, size }) catch @panic("OOM");
|
|
}
|
|
}.it;
|
|
|
|
const config = .{
|
|
.HAVE_LIBZ = true,
|
|
.HAVE_BROTLI = true,
|
|
.USE_NGHTTP2 = true,
|
|
|
|
.USE_OPENSSL = true,
|
|
.OPENSSL_IS_BORINGSSL = true,
|
|
.CURL_CA_PATH = null,
|
|
.CURL_CA_BUNDLE = null,
|
|
.CURL_CA_FALLBACK = false,
|
|
.CURL_CA_SEARCH_SAFE = false,
|
|
.CURL_DEFAULT_SSL_BACKEND = "openssl",
|
|
|
|
.CURL_DISABLE_AWS = true,
|
|
.CURL_DISABLE_DICT = true,
|
|
.CURL_DISABLE_DOH = true,
|
|
.CURL_DISABLE_FILE = true,
|
|
.CURL_DISABLE_FTP = true,
|
|
.CURL_DISABLE_GOPHER = true,
|
|
.CURL_DISABLE_KERBEROS_AUTH = true,
|
|
.CURL_DISABLE_IMAP = true,
|
|
.CURL_DISABLE_IPFS = true,
|
|
.CURL_DISABLE_LDAP = true,
|
|
.CURL_DISABLE_LDAPS = true,
|
|
.CURL_DISABLE_MQTT = true,
|
|
.CURL_DISABLE_NTLM = true,
|
|
.CURL_DISABLE_PROGRESS_METER = true,
|
|
.CURL_DISABLE_POP3 = true,
|
|
.CURL_DISABLE_RTSP = true,
|
|
.CURL_DISABLE_SMB = true,
|
|
.CURL_DISABLE_SMTP = true,
|
|
.CURL_DISABLE_TELNET = true,
|
|
.CURL_DISABLE_TFTP = true,
|
|
.CURL_DISABLE_WEBSOCKETS = false, // Enable WebSocket support
|
|
|
|
.ssize_t = null,
|
|
._FILE_OFFSET_BITS = 64,
|
|
|
|
.USE_IPV6 = true,
|
|
// Route IDN hostnames through libidn2 (vendored, see buildLibidn2).
|
|
// Without this, libcurl ships UTF-8 host bytes to SNI/cert validation
|
|
// and breaks for non-ASCII hostnames like räksmörgås.se.
|
|
.HAVE_LIBIDN2 = true,
|
|
.HAVE_IDN2_H = true,
|
|
.CURL_OS = switch (os) {
|
|
.linux => if (is_android) "\"android\"" else "\"linux\"",
|
|
else => std.fmt.allocPrint(b.allocator, "\"{s}\"", .{@tagName(os)}) catch @panic("OOM"),
|
|
},
|
|
|
|
// Adjusts the sizes of variables
|
|
.SIZEOF_INT_CODE = byte_size(b, target, "INT", .int),
|
|
.SIZEOF_LONG_CODE = byte_size(b, target, "LONG", .long),
|
|
.SIZEOF_LONG_LONG_CODE = byte_size(b, target, "LONG_LONG", .longlong),
|
|
|
|
.SIZEOF_OFF_T_CODE = byte_size(b, target, "OFF_T", .longlong),
|
|
.SIZEOF_CURL_OFF_T_CODE = byte_size(b, target, "CURL_OFF_T", .longlong),
|
|
.SIZEOF_CURL_SOCKET_T_CODE = byte_size(b, target, "CURL_SOCKET_T", .int),
|
|
|
|
.SIZEOF_SIZE_T_CODE = byte_size(b, target, "SIZE_T", .longlong),
|
|
.SIZEOF_TIME_T_CODE = byte_size(b, target, "TIME_T", .longlong),
|
|
|
|
// headers availability
|
|
.HAVE_ARPA_INET_H = !is_windows,
|
|
.HAVE_DIRENT_H = true,
|
|
.HAVE_FCNTL_H = true,
|
|
.HAVE_IFADDRS_H = !is_windows,
|
|
.HAVE_IO_H = is_windows,
|
|
.HAVE_LIBGEN_H = true,
|
|
.HAVE_LINUX_TCP_H = is_linux and is_gnu,
|
|
.HAVE_LOCALE_H = true,
|
|
.HAVE_NETDB_H = !is_windows,
|
|
.HAVE_NETINET_IN6_H = is_android,
|
|
.HAVE_NETINET_IN_H = !is_windows,
|
|
.HAVE_NETINET_TCP_H = !is_windows,
|
|
.HAVE_NETINET_UDP_H = !is_windows,
|
|
.HAVE_NET_IF_H = !is_windows,
|
|
.HAVE_POLL_H = !is_windows,
|
|
.HAVE_PWD_H = !is_windows,
|
|
.HAVE_STDATOMIC_H = true,
|
|
.HAVE_STDBOOL_H = true,
|
|
.HAVE_STDDEF_H = true,
|
|
.HAVE_STDINT_H = true,
|
|
.HAVE_STRINGS_H = true,
|
|
.HAVE_STROPTS_H = false,
|
|
.HAVE_SYS_EVENTFD_H = is_linux or is_freebsd or is_netbsd,
|
|
.HAVE_SYS_FILIO_H = !is_linux and !is_windows,
|
|
.HAVE_SYS_IOCTL_H = !is_windows,
|
|
.HAVE_SYS_PARAM_H = true,
|
|
.HAVE_SYS_POLL_H = !is_windows,
|
|
.HAVE_SYS_RESOURCE_H = !is_windows,
|
|
.HAVE_SYS_SELECT_H = !is_windows,
|
|
.HAVE_SYS_SOCKIO_H = !is_linux and !is_windows,
|
|
.HAVE_SYS_TYPES_H = true,
|
|
.HAVE_SYS_UN_H = !is_windows,
|
|
.HAVE_SYS_UTIME_H = is_windows,
|
|
.HAVE_TERMIOS_H = !is_windows,
|
|
.HAVE_TERMIO_H = is_linux,
|
|
.HAVE_UNISTD_H = true,
|
|
.HAVE_UTIME_H = true,
|
|
.STDC_HEADERS = true,
|
|
|
|
// general environment
|
|
.CURL_KRB5_VERSION = null,
|
|
.HAVE_ALARM = !is_windows,
|
|
.HAVE_ARC4RANDOM = is_android,
|
|
.HAVE_ATOMIC = true,
|
|
.HAVE_BOOL_T = true,
|
|
.HAVE_BUILTIN_AVAILABLE = true,
|
|
.HAVE_CLOCK_GETTIME_MONOTONIC = !is_darwin and !is_windows,
|
|
.HAVE_CLOCK_GETTIME_MONOTONIC_RAW = is_linux,
|
|
.HAVE_FILE_OFFSET_BITS = true,
|
|
.HAVE_GETEUID = !is_windows,
|
|
.HAVE_GETPPID = !is_windows,
|
|
.HAVE_GETTIMEOFDAY = true,
|
|
.HAVE_GLIBC_STRERROR_R = is_gnu,
|
|
.HAVE_GMTIME_R = !is_windows,
|
|
.HAVE_LOCALTIME_R = !is_windows,
|
|
.HAVE_LONGLONG = !is_windows,
|
|
.HAVE_MACH_ABSOLUTE_TIME = is_darwin,
|
|
.HAVE_MEMRCHR = !is_darwin and !is_windows,
|
|
.HAVE_POSIX_STRERROR_R = !is_gnu and !is_windows,
|
|
.HAVE_PTHREAD_H = !is_windows,
|
|
.HAVE_SETLOCALE = true,
|
|
.HAVE_SETRLIMIT = !is_windows,
|
|
.HAVE_SIGACTION = !is_windows,
|
|
.HAVE_SIGINTERRUPT = !is_windows,
|
|
.HAVE_SIGNAL = true,
|
|
.HAVE_SIGSETJMP = !is_windows,
|
|
.HAVE_SIZEOF_SA_FAMILY_T = false,
|
|
.HAVE_SIZEOF_SUSECONDS_T = false,
|
|
.HAVE_SNPRINTF = true,
|
|
.HAVE_STRCASECMP = !is_windows,
|
|
.HAVE_STRCMPI = false,
|
|
.HAVE_STRDUP = true,
|
|
.HAVE_STRERROR_R = !is_windows,
|
|
.HAVE_STRICMP = false,
|
|
.HAVE_STRUCT_TIMEVAL = true,
|
|
.HAVE_TIME_T_UNSIGNED = false,
|
|
.HAVE_UTIME = true,
|
|
.HAVE_UTIMES = !is_windows,
|
|
.HAVE_WRITABLE_ARGV = !is_windows,
|
|
.HAVE__SETMODE = is_windows,
|
|
.USE_THREADS_POSIX = !is_windows,
|
|
|
|
// filesystem, network
|
|
.HAVE_ACCEPT4 = is_linux or is_freebsd or is_netbsd or is_openbsd,
|
|
.HAVE_BASENAME = true,
|
|
.HAVE_CLOSESOCKET = is_windows,
|
|
.HAVE_DECL_FSEEKO = !is_windows,
|
|
.HAVE_EVENTFD = is_linux or is_freebsd or is_netbsd,
|
|
.HAVE_FCNTL = !is_windows,
|
|
.HAVE_FCNTL_O_NONBLOCK = !is_windows,
|
|
.HAVE_FNMATCH = !is_windows,
|
|
.HAVE_FREEADDRINFO = true,
|
|
.HAVE_FSEEKO = !is_windows,
|
|
.HAVE_FSETXATTR = is_darwin or is_linux or is_netbsd,
|
|
.HAVE_FSETXATTR_5 = is_linux or is_netbsd,
|
|
.HAVE_FSETXATTR_6 = is_darwin,
|
|
.HAVE_FTRUNCATE = true,
|
|
.HAVE_GETADDRINFO = true,
|
|
.HAVE_GETADDRINFO_THREADSAFE = is_linux or is_freebsd or is_netbsd,
|
|
.HAVE_GETHOSTBYNAME_R = is_linux or is_freebsd,
|
|
.HAVE_GETHOSTBYNAME_R_3 = false,
|
|
.HAVE_GETHOSTBYNAME_R_3_REENTRANT = false,
|
|
.HAVE_GETHOSTBYNAME_R_5 = false,
|
|
.HAVE_GETHOSTBYNAME_R_5_REENTRANT = false,
|
|
.HAVE_GETHOSTBYNAME_R_6 = is_linux,
|
|
.HAVE_GETHOSTBYNAME_R_6_REENTRANT = is_linux,
|
|
.HAVE_GETHOSTNAME = true,
|
|
.HAVE_GETIFADDRS = if (is_windows) false else !is_android or target.result.os.versionRange().linux.android >= 24,
|
|
.HAVE_GETPASS_R = is_netbsd,
|
|
.HAVE_GETPEERNAME = true,
|
|
.HAVE_GETPWUID = !is_windows,
|
|
.HAVE_GETPWUID_R = !is_windows,
|
|
.HAVE_GETRLIMIT = !is_windows,
|
|
.HAVE_GETSOCKNAME = true,
|
|
.HAVE_IF_NAMETOINDEX = !is_windows,
|
|
.HAVE_INET_NTOP = !is_windows,
|
|
.HAVE_INET_PTON = !is_windows,
|
|
.HAVE_IOCTLSOCKET = is_windows,
|
|
.HAVE_IOCTLSOCKET_CAMEL = false,
|
|
.HAVE_IOCTLSOCKET_CAMEL_FIONBIO = false,
|
|
.HAVE_IOCTLSOCKET_FIONBIO = is_windows,
|
|
.HAVE_IOCTL_FIONBIO = !is_windows,
|
|
.HAVE_IOCTL_SIOCGIFADDR = !is_windows,
|
|
.HAVE_MSG_NOSIGNAL = !is_windows,
|
|
.HAVE_OPENDIR = true,
|
|
.HAVE_PIPE = !is_windows,
|
|
.HAVE_PIPE2 = is_linux or is_freebsd or is_netbsd or is_openbsd,
|
|
.HAVE_POLL = !is_windows,
|
|
.HAVE_REALPATH = !is_windows,
|
|
.HAVE_RECV = true,
|
|
.HAVE_SA_FAMILY_T = !is_windows,
|
|
.HAVE_SCHED_YIELD = !is_windows,
|
|
.HAVE_SELECT = true,
|
|
.HAVE_SEND = true,
|
|
.HAVE_SENDMMSG = !is_darwin and !is_windows,
|
|
.HAVE_SENDMSG = !is_windows,
|
|
.HAVE_SETMODE = !is_linux,
|
|
.HAVE_SETSOCKOPT_SO_NONBLOCK = false,
|
|
.HAVE_SOCKADDR_IN6_SIN6_ADDR = !is_windows,
|
|
.HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID = true,
|
|
.HAVE_SOCKET = true,
|
|
.HAVE_SOCKETPAIR = !is_windows,
|
|
.HAVE_STRUCT_SOCKADDR_STORAGE = true,
|
|
.HAVE_SUSECONDS_T = is_android or is_ios,
|
|
.USE_UNIX_SOCKETS = !is_windows,
|
|
};
|
|
|
|
const curl_config = b.addConfigHeader(.{
|
|
.include_path = "curl_config.h",
|
|
.style = .{ .cmake = dep.path("lib/curl_config-cmake.h.in") },
|
|
}, .{
|
|
.CURL_EXTERN_SYMBOL = "__attribute__ ((__visibility__ (\"default\"))",
|
|
});
|
|
curl_config.addValues(config);
|
|
|
|
const lib = b.addLibrary(.{ .name = "curl", .root_module = mod });
|
|
lib.addConfigHeader(curl_config);
|
|
lib.installHeadersDirectory(dep.path("include/curl"), "curl", .{});
|
|
lib.addCSourceFiles(.{
|
|
.root = dep.path("lib"),
|
|
.flags = &.{
|
|
"-D_GNU_SOURCE",
|
|
"-DHAVE_CONFIG_H",
|
|
"-DCURL_STATICLIB",
|
|
"-DBUILDING_LIBCURL",
|
|
},
|
|
.files = &.{
|
|
// You can include all files from lib, libcurl uses #ifdef-guards to exclude code for disabled functions
|
|
"altsvc.c", "amigaos.c", "asyn-ares.c",
|
|
"asyn-base.c", "asyn-thrdd.c", "bufq.c",
|
|
"bufref.c", "cf-h1-proxy.c", "cf-h2-proxy.c",
|
|
"cf-haproxy.c", "cf-https-connect.c", "cf-ip-happy.c",
|
|
"cf-socket.c", "cfilters.c", "conncache.c",
|
|
"connect.c", "content_encoding.c", "cookie.c",
|
|
"cshutdn.c", "curl_addrinfo.c", "curl_endian.c",
|
|
"curl_fnmatch.c", "curl_fopen.c", "curl_get_line.c",
|
|
"curl_gethostname.c", "curl_gssapi.c", "curl_memrchr.c",
|
|
"curl_ntlm_core.c", "curl_range.c", "curl_rtmp.c",
|
|
"curl_sasl.c", "curl_sha512_256.c", "curl_share.c",
|
|
"curl_sspi.c", "curl_threads.c", "curl_trc.c",
|
|
"curlx/base64.c", "curlx/dynbuf.c", "curlx/fopen.c",
|
|
"curlx/inet_ntop.c", "curlx/inet_pton.c", "curlx/multibyte.c",
|
|
"curlx/nonblock.c", "curlx/strcopy.c", "curlx/strerr.c",
|
|
"curlx/strparse.c", "curlx/timediff.c", "curlx/timeval.c",
|
|
"curlx/version_win32.c", "curlx/wait.c", "curlx/warnless.c",
|
|
"curlx/winapi.c", "cw-out.c", "cw-pause.c",
|
|
"dict.c", "dllmain.c", "doh.c",
|
|
"dynhds.c", "easy.c", "easygetopt.c",
|
|
"easyoptions.c", "escape.c", "fake_addrinfo.c",
|
|
"file.c", "fileinfo.c", "formdata.c",
|
|
"ftp.c", "ftplistparser.c", "getenv.c",
|
|
"getinfo.c", "gopher.c", "hash.c",
|
|
"headers.c", "hmac.c", "hostip.c",
|
|
"hostip4.c", "hostip6.c", "hsts.c",
|
|
"http.c", "http1.c", "http2.c",
|
|
"http_aws_sigv4.c", "http_chunks.c", "http_digest.c",
|
|
"http_negotiate.c", "http_ntlm.c", "http_proxy.c",
|
|
"httpsrr.c", "idn.c", "if2ip.c",
|
|
"imap.c", "ldap.c", "llist.c",
|
|
"macos.c", "md4.c", "md5.c",
|
|
"memdebug.c", "mime.c", "mprintf.c",
|
|
"mqtt.c", "multi.c", "multi_ev.c",
|
|
"multi_ntfy.c", "netrc.c", "noproxy.c",
|
|
"openldap.c", "parsedate.c", "pingpong.c",
|
|
"pop3.c", "progress.c", "psl.c",
|
|
"rand.c", "ratelimit.c", "request.c",
|
|
"rtsp.c", "select.c", "sendf.c",
|
|
"setopt.c", "sha256.c", "slist.c",
|
|
"smb.c", "smtp.c", "socketpair.c",
|
|
"socks.c", "socks_gssapi.c", "socks_sspi.c",
|
|
"splay.c", "strcase.c", "strdup.c",
|
|
"strequal.c", "strerror.c", "system_win32.c",
|
|
"telnet.c", "tftp.c", "transfer.c",
|
|
"uint-bset.c", "uint-hash.c", "uint-spbset.c",
|
|
"uint-table.c", "url.c", "urlapi.c",
|
|
"vauth/cleartext.c", "vauth/cram.c", "vauth/digest.c",
|
|
"vauth/digest_sspi.c", "vauth/gsasl.c", "vauth/krb5_gssapi.c",
|
|
"vauth/krb5_sspi.c", "vauth/ntlm.c", "vauth/ntlm_sspi.c",
|
|
"vauth/oauth2.c", "vauth/spnego_gssapi.c", "vauth/spnego_sspi.c",
|
|
"vauth/vauth.c", "version.c", "vquic/curl_ngtcp2.c",
|
|
"vquic/curl_osslq.c", "vquic/curl_quiche.c", "vquic/vquic-tls.c",
|
|
"vquic/vquic.c", "vssh/libssh.c", "vssh/libssh2.c",
|
|
"vssh/vssh.c", "vtls/apple.c", "vtls/cipher_suite.c",
|
|
"vtls/gtls.c", "vtls/hostcheck.c", "vtls/keylog.c",
|
|
"vtls/mbedtls.c", "vtls/openssl.c", "vtls/rustls.c",
|
|
"vtls/schannel.c", "vtls/schannel_verify.c", "vtls/vtls.c",
|
|
"vtls/vtls_scache.c", "vtls/vtls_spack.c", "vtls/wolfssl.c",
|
|
"vtls/x509asn1.c", "ws.c",
|
|
},
|
|
});
|
|
|
|
return lib;
|
|
}
|
|
|
|
fn linkZenai(b: *Build, mod: *Build.Module) void {
|
|
const dep = b.dependency("zenai", .{});
|
|
mod.addImport("zenai", dep.module("zenai"));
|
|
}
|
|
|
|
fn linkIsocline(b: *Build, mod: *Build.Module) void {
|
|
const dep = b.dependency("isocline", .{});
|
|
mod.addIncludePath(dep.path("include"));
|
|
mod.addCSourceFile(.{
|
|
.file = dep.path("src/isocline.c"),
|
|
});
|
|
}
|
|
|
|
/// Resolves the semantic version of the build.
|
|
///
|
|
/// The base version is read from `build.zig.zon`. This can be overridden
|
|
/// using the `-Dversion` command-line flag:
|
|
/// - If the flag contains a full semantic version (e.g., `1.2.3`), it replaces
|
|
/// the base version entirely.
|
|
/// - If the flag contains a simple string (e.g., `nightly`), it replaces only
|
|
/// the pre-release tag of the base version (e.g., `1.0.0-dev` -> `1.0.0-nightly`).
|
|
///
|
|
/// For versions that have a pre-release tag and no explicit build metadata,
|
|
/// this function automatically enriches the version with the git commit count
|
|
/// and short hash (e.g., `1.0.0-dev.5243+dbe45229`).
|
|
fn resolveVersion(b: *std.Build) std.SemanticVersion {
|
|
const opt_version = b.option([]const u8, "version", "Override the version of this build");
|
|
|
|
const version = if (opt_version) |v|
|
|
std.SemanticVersion.parse(v) catch blk: {
|
|
var fallback = lightpanda_version;
|
|
fallback.pre = v;
|
|
break :blk fallback;
|
|
}
|
|
else
|
|
lightpanda_version;
|
|
|
|
// Only enrich versions that have a pre-release field and no explicit build metadata.
|
|
if (version.pre == null or version.build != null) return version;
|
|
|
|
// For dev/nightly versions, calculate the commit count and hash
|
|
const git_hash_raw = runGit(b, &.{ "rev-parse", "--short", "HEAD" }) catch return version;
|
|
const commit_hash = std.mem.trim(u8, git_hash_raw, " \n\r");
|
|
|
|
const git_count_raw = runGit(b, &.{ "rev-list", "--count", "HEAD" }) catch return version;
|
|
const commit_count = std.mem.trim(u8, git_count_raw, " \n\r");
|
|
|
|
return .{
|
|
.major = version.major,
|
|
.minor = version.minor,
|
|
.patch = version.patch,
|
|
.pre = b.fmt("{s}.{s}", .{ version.pre.?, commit_count }),
|
|
.build = commit_hash,
|
|
};
|
|
}
|
|
|
|
/// Helper function to run git commands and return stdout
|
|
fn runGit(b: *std.Build, args: []const []const u8) ![]const u8 {
|
|
var code: u8 = undefined;
|
|
const dir = b.pathFromRoot(".");
|
|
var command: std.ArrayList([]const u8) = .empty;
|
|
defer command.deinit(b.allocator);
|
|
try command.appendSlice(b.allocator, &.{ "git", "-C", dir });
|
|
try command.appendSlice(b.allocator, args);
|
|
return b.runAllowFail(command.items, &code, .Ignore);
|
|
}
|