mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-06-11 01:25:53 -04:00
Links to libidn2 and builds libcurl with it. This makes libcurl work, and by extension browser, work on international domain names, e.g. zig build run -- fetch "https://räksmörgås.se/" With it available, we can use it in our WebAPIs which should also support these domains, e.g: testing.expectEqual('xn--rksmrgs-5wao1o.se', new URL('https://räksmörgås.se').hostname); There is more integration to be done here, but this is a first step. claude wrote all of the build.zig code. I don't have a strong opinion about this feature, I just dislike that our WPT /url/* tests are at 1704 / 9095 and, this is the biggest chunk (although, this specific commit just does the basic integration and probably won't fix too many WPT cases directly).
1040 lines
41 KiB
Zig
1040 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 stdout = std.fs.File.stdout().writer(&.{});
|
|
try stdout.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);
|
|
|
|
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, legacy_test) 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 and legacy_test");
|
|
|
|
{
|
|
// 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);
|
|
}
|
|
|
|
{
|
|
// browser
|
|
const exe = b.addExecutable(.{
|
|
.name = "legacy_test",
|
|
.use_llvm = true,
|
|
.root_module = b.createModule(.{
|
|
.root_source_file = b.path("src/main_legacy_test.zig"),
|
|
.target = target,
|
|
.optimize = optimize,
|
|
.sanitize_c = enable_csan,
|
|
.sanitize_thread = enable_tsan,
|
|
.imports = &.{
|
|
.{ .name = "lightpanda", .module = lightpanda_module },
|
|
},
|
|
}),
|
|
});
|
|
extras_step.dependOn(&b.addInstallArtifact(exe, .{}).step);
|
|
|
|
const exe_check = b.addLibrary(.{
|
|
.name = "legacy_test_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("legacy_test", "Run the app");
|
|
run_step.dependOn(&run_cmd.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);
|
|
|
|
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", .{});
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/// 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);
|
|
}
|