Files
envr/zig-vendor/zig-sqlite/build.zig
2026-04-26 17:29:37 -04:00

383 lines
13 KiB
Zig

const std = @import("std");
const debug = std.debug;
const heap = std.heap;
const mem = std.mem;
const ResolvedTarget = std.Build.ResolvedTarget;
const Query = std.Target.Query;
const builtin = @import("builtin");
const Preprocessor = @import("build/Preprocessor.zig");
fn getTarget(original_target: ResolvedTarget) ResolvedTarget {
var tmp = original_target;
if (tmp.result.isGnuLibC()) {
const min_glibc_version = std.SemanticVersion{
.major = 2,
.minor = 28,
.patch = 0,
};
const ver = tmp.result.os.version_range.linux.glibc;
if (ver.order(min_glibc_version) == .lt) {
std.debug.panic("sqlite requires glibc version >= 2.28", .{});
}
}
return tmp;
}
const TestTarget = struct {
query: Query,
single_threaded: bool = false,
};
const ci_targets = switch (builtin.target.cpu.arch) {
.x86_64 => switch (builtin.target.os.tag) {
.linux => [_]TestTarget{
TestTarget{ .query = .{ .cpu_arch = .x86_64, .abi = .musl } },
TestTarget{ .query = .{ .cpu_arch = .x86, .abi = .musl } },
TestTarget{ .query = .{ .cpu_arch = .aarch64, .abi = .musl } },
},
.windows => [_]TestTarget{
TestTarget{ .query = .{ .cpu_arch = .x86_64, .abi = .gnu } },
// Disabled due to https://github.com/ziglang/zig/issues/20047
// TestTarget{ .query = .{ .cpu_arch = .x86, .abi = .gnu } },
},
.macos => [_]TestTarget{
TestTarget{ .query = .{ .cpu_arch = .x86_64 } },
},
else => [_]TestTarget{},
},
else => [_]TestTarget{},
};
const all_test_targets = switch (builtin.target.cpu.arch) {
.x86_64 => switch (builtin.target.os.tag) {
.linux => [_]TestTarget{
TestTarget{ .query = .{} },
TestTarget{ .query = .{ .cpu_arch = .x86_64, .abi = .musl } },
TestTarget{ .query = .{ .cpu_arch = .x86, .abi = .musl } },
TestTarget{ .query = .{ .cpu_arch = .aarch64, .abi = .musl } },
TestTarget{ .query = .{ .cpu_arch = .riscv64, .abi = .musl } },
// Disabled because it fails for some unknown reason
// TestTarget{ .query = .{ .cpu_arch = .mips, .abi = .musl } },
TestTarget{ .query = .{ .cpu_arch = .x86_64, .os_tag = .windows } },
// Disabled due to https://github.com/ziglang/zig/issues/20047
// TestTarget{ .query = .{ .cpu_arch = .x86, .os_tag = .windows } },
TestTarget{ .query = .{ .cpu_arch = .x86_64, .os_tag = .macos } },
TestTarget{ .query = .{ .cpu_arch = .aarch64, .os_tag = .macos } },
},
.windows => [_]TestTarget{
TestTarget{ .query = .{ .cpu_arch = .x86_64, .abi = .gnu } },
// Disabled due to https://github.com/ziglang/zig/issues/20047
// TestTarget{ .query = .{ .cpu_arch = .x86, .abi = .gnu } },
},
.freebsd => [_]TestTarget{
TestTarget{ .query = .{} },
TestTarget{ .query = .{ .cpu_arch = .x86_64 } },
},
.macos => [_]TestTarget{
TestTarget{ .query = .{ .cpu_arch = .x86_64 } },
},
else => [_]TestTarget{
TestTarget{ .query = .{} },
},
},
.aarch64 => switch (builtin.target.os.tag) {
.linux, .windows, .freebsd, .macos => [_]TestTarget{
TestTarget{ .query = .{} },
},
else => [_]TestTarget{
TestTarget{ .query = .{} },
},
},
else => [_]TestTarget{
TestTarget{ .query = .{} },
},
};
fn computeTestTargets(isNative: bool, ci: ?bool) ?[]const TestTarget {
if (ci != null and ci.?) return &ci_targets;
if (isNative) {
// If the target is native we assume the user didn't change it with -Dtarget and run all test targets.
return &all_test_targets;
}
// Otherwise we run a single test target.
return null;
}
// This creates a SQLite static library from the SQLite dependency code.
fn makeSQLiteLib(b: *std.Build, dep: *std.Build.Dependency, c_flags: []const []const u8, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, sqlite_c: enum { with, without }) *std.Build.Step.Compile {
const mod = b.addModule("lib-sqlite", .{
.target = target,
.optimize = optimize,
.link_libc = true,
});
const lib = b.addLibrary(.{
.name = "sqlite",
.linkage = .static,
.root_module = mod,
});
mod.addIncludePath(dep.path("."));
mod.addIncludePath(b.path("c"));
if (sqlite_c == .with) {
mod.addCSourceFile(.{
.file = dep.path("sqlite3.c"),
.flags = c_flags,
});
}
mod.addCSourceFile(.{
.file = b.path("c/workaround.c"),
.flags = c_flags,
});
return lib;
}
pub fn build(b: *std.Build) !void {
const in_memory = b.option(bool, "in_memory", "Should the tests run with sqlite in memory (default true)") orelse true;
const dbfile = b.option([]const u8, "dbfile", "Always use this database file instead of a temporary one");
const ci = b.option(bool, "ci", "Build and test in the CI on GitHub");
const query = b.standardTargetOptionsQueryOnly(.{});
const target = b.resolveTargetQuery(query);
const optimize = b.standardOptimizeOption(.{});
// Upstream dependency
const sqlite_dep = b.dependency("sqlite", .{
.target = target,
.optimize = optimize,
});
// Define C flags to use
var flags: std.ArrayList([]const u8) = .empty;
defer flags.deinit(b.allocator);
try flags.append(b.allocator, "-std=c99");
inline for (std.meta.fields(EnableOptions)) |field| {
const opt = b.option(bool, field.name, "Enable " ++ field.name) orelse field.defaultValue().?;
if (opt) {
var buf: [field.name.len]u8 = undefined;
const name = std.ascii.upperString(&buf, field.name);
const flag = try std.fmt.allocPrint(b.allocator, "-DSQLITE_ENABLE_{s}", .{name});
try flags.append(b.allocator, flag);
}
}
const c_flags = flags.items;
//
// Main library and module
//
// const sqlite_lib, const sqlite_mod = blk: {
const sqlite_lib, _ = blk: {
const lib = makeSQLiteLib(b, sqlite_dep, c_flags, target, optimize, .with);
const mod = b.addModule("sqlite", .{
.root_source_file = b.path("sqlite.zig"),
.link_libc = true,
});
mod.addIncludePath(b.path("c"));
mod.addIncludePath(sqlite_dep.path("."));
mod.linkLibrary(lib);
break :blk .{ lib, mod };
};
b.installArtifact(sqlite_lib);
// const sqliteext_mod = blk: {
_ = blk: {
const lib = makeSQLiteLib(b, sqlite_dep, c_flags, target, optimize, .without);
const mod = b.addModule("sqliteext", .{
.root_source_file = b.path("sqlite.zig"),
.link_libc = true,
});
mod.addIncludePath(b.path("c"));
mod.linkLibrary(lib);
break :blk mod;
};
//
// Tests
//
const test_targets = computeTestTargets(query.isNative(), ci) orelse &[_]TestTarget{.{
.query = query,
}};
const test_step = b.step("test", "Run library tests");
// By default the tests will only be execute for native test targets, however they will be compiled
// for _all_ targets defined in `test_targets`.
//
// If you want to execute tests for other targets you can pass -fqemu, -fdarling, -fwine, -frosetta.
for (test_targets) |test_target| {
const cross_target = getTarget(b.resolveTargetQuery(test_target.query));
const single_threaded_txt = if (test_target.single_threaded) "single" else "multi";
const test_name = b.fmt("{s}-{s}-{s}", .{
try cross_target.result.zigTriple(b.allocator),
@tagName(optimize),
single_threaded_txt,
});
const test_sqlite_lib = makeSQLiteLib(b, sqlite_dep, c_flags, cross_target, optimize, .with);
const mod = b.addModule(test_name, .{
.target = cross_target,
.optimize = optimize,
.root_source_file = b.path("sqlite.zig"),
.single_threaded = test_target.single_threaded,
});
const tests = b.addTest(.{
.name = test_name,
.root_module = mod,
});
tests.root_module.addIncludePath(b.path("c"));
tests.root_module.addIncludePath(sqlite_dep.path("."));
tests.root_module.linkLibrary(test_sqlite_lib);
const tests_options = b.addOptions();
tests.root_module.addImport("build_options", tests_options.createModule());
tests_options.addOption(bool, "in_memory", in_memory);
tests_options.addOption(?[]const u8, "dbfile", dbfile);
const run_tests = b.addRunArtifact(tests);
test_step.dependOn(&run_tests.step);
}
// This builds an example shared library with the extension and a binary that tests it.
//\ const zigcrypto_install_artifact = addZigcrypto(b, sqliteext_mod, target, optimize);
//\ test_step.dependOn(&zigcrypto_install_artifact.step);
//\ const zigcrypto_test_run = addZigcryptoTestRun(b, sqlite_mod, target, optimize);
//\ zigcrypto_test_run.step.dependOn(&zigcrypto_install_artifact.step);
//\ test_step.dependOn(&zigcrypto_test_run.step);
//
// Tools
//
addPreprocessStep(b, sqlite_dep);
}
fn addPreprocessStep(b: *std.Build, sqlite_dep: *std.Build.Dependency) void {
var wf = b.addWriteFiles();
// Preprocessing step
const preprocess = PreprocessStep.create(b, .{
.source = sqlite_dep.path("."),
.target = wf.getDirectory(),
});
preprocess.step.dependOn(&wf.step);
const w = b.addUpdateSourceFiles();
w.addCopyFileToSource(preprocess.target.join(b.allocator, "loadable-ext-sqlite3.h") catch @panic("OOM"), "c/loadable-ext-sqlite3.h");
w.addCopyFileToSource(preprocess.target.join(b.allocator, "loadable-ext-sqlite3ext.h") catch @panic("OOM"), "c/loadable-ext-sqlite3ext.h");
w.step.dependOn(&preprocess.step);
const preprocess_headers = b.step("preprocess-headers", "Preprocess the headers for the loadable extensions");
preprocess_headers.dependOn(&w.step);
}
fn addZigcrypto(b: *std.Build, sqlite_mod: *std.Build.Module, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Step.InstallArtifact {
const mod = b.addModule("zigcryto", .{
.root_source_file = b.path("examples/zigcrypto.zig"),
.target = getTarget(target),
.optimize = optimize,
});
const exe = b.addLibrary(.{
.name = "zigcrypto",
.root_module = mod,
.version = null,
.linkage = .dynamic,
});
exe.root_module.addImport("sqlite", sqlite_mod);
const install_artifact = b.addInstallArtifact(exe, .{});
install_artifact.step.dependOn(&exe.step);
return install_artifact;
}
fn addZigcryptoTestRun(b: *std.Build, sqlite_mod: *std.Build.Module, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Step.Run {
const mod = b.addModule("zigcryto-test", .{
.root_source_file = b.path("examples/zigcrypto_test.zig"),
.target = getTarget(target),
.optimize = optimize,
});
const zigcrypto_test = b.addExecutable(.{
.name = "zigcrypto-test",
.root_module = mod,
});
zigcrypto_test.root_module.addImport("sqlite", sqlite_mod);
const install = b.addInstallArtifact(zigcrypto_test, .{});
install.step.dependOn(&zigcrypto_test.step);
const run = b.addRunArtifact(zigcrypto_test);
run.step.dependOn(&zigcrypto_test.step);
return run;
}
// See https://www.sqlite.org/compile.html for flags
const EnableOptions = struct {
// https://www.sqlite.org/fts5.html
fts5: bool = false,
};
const PreprocessStep = struct {
const Config = struct {
source: std.Build.LazyPath,
target: std.Build.LazyPath,
};
step: std.Build.Step,
source: std.Build.LazyPath,
target: std.Build.LazyPath,
fn create(owner: *std.Build, config: Config) *PreprocessStep {
const step = owner.allocator.create(PreprocessStep) catch @panic("OOM");
step.* = .{
.step = std.Build.Step.init(.{
.id = std.Build.Step.Id.custom,
.name = "preprocess",
.owner = owner,
.makeFn = make,
}),
.source = config.source,
.target = config.target,
};
return step;
}
fn make(step: *std.Build.Step, _: std.Build.Step.MakeOptions) !void {
const ps: *PreprocessStep = @fieldParentPtr("step", step);
const owner = step.owner;
const sqlite3_h = try ps.source.path(owner, "sqlite3.h").getPath3(owner, step).toString(owner.allocator);
const sqlite3ext_h = try ps.source.path(owner, "sqlite3ext.h").getPath3(owner, step).toString(owner.allocator);
const loadable_sqlite3_h = try ps.target.path(owner, "loadable-ext-sqlite3.h").getPath3(owner, step).toString(owner.allocator);
const loadable_sqlite3ext_h = try ps.target.path(owner, "loadable-ext-sqlite3ext.h").getPath3(owner, step).toString(owner.allocator);
var threaded: std.Io.Threaded = .init_single_threaded;
const io = threaded.io();
try Preprocessor.sqlite3(io, owner.allocator, sqlite3_h, loadable_sqlite3_h);
try Preprocessor.sqlite3ext(io, owner.allocator, sqlite3ext_h, loadable_sqlite3ext_h);
}
};