Skip to content

Commit 4a04839

Browse files
committed
stop vendoring the sqlite C code, rework the build
* Use the zig package manager to fetch sqlite directly from upstream * Integrate the preprocessing tool directly into the build script This makes it simpler to upgrade the SQLite source code: * use `zig fetch` * run `zig build preprocess-headers`
1 parent cd7d1c3 commit 4a04839

File tree

8 files changed

+221
-274909
lines changed

8 files changed

+221
-274909
lines changed

build.zig

Lines changed: 155 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
const std = @import("std");
2-
const builtin = @import("builtin");
3-
const Step = std.Build.Step;
2+
const debug = std.debug;
3+
const heap = std.heap;
4+
const mem = std.mem;
45
const ResolvedTarget = std.Build.ResolvedTarget;
56
const Query = std.Target.Query;
7+
const builtin = @import("builtin");
8+
9+
const Preprocessor = @import("build/Preprocessor.zig");
610

711
fn getTarget(original_target: ResolvedTarget) ResolvedTarget {
812
var tmp = original_target;
@@ -104,6 +108,31 @@ fn computeTestTargets(isNative: bool, ci: ?bool) ?[]const TestTarget {
104108
return null;
105109
}
106110

111+
// This creates a SQLite static library from the SQLite dependency code.
112+
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 {
113+
const lib = b.addStaticLibrary(.{
114+
.name = "sqlite",
115+
.target = target,
116+
.optimize = optimize,
117+
});
118+
119+
lib.addIncludePath(dep.path("."));
120+
lib.addIncludePath(b.path("c"));
121+
if (sqlite_c == .with) {
122+
lib.addCSourceFile(.{
123+
.file = dep.path("sqlite3.c"),
124+
.flags = c_flags,
125+
});
126+
}
127+
lib.addCSourceFile(.{
128+
.file = b.path("c/workaround.c"),
129+
.flags = c_flags,
130+
});
131+
lib.linkLibC();
132+
133+
return lib;
134+
}
135+
107136
pub fn build(b: *std.Build) !void {
108137
const in_memory = b.option(bool, "in_memory", "Should the tests run with sqlite in memory (default true)") orelse true;
109138
const dbfile = b.option([]const u8, "dbfile", "Always use this database file instead of a temporary one");
@@ -113,6 +142,14 @@ pub fn build(b: *std.Build) !void {
113142
const target = b.resolveTargetQuery(query);
114143
const optimize = b.standardOptimizeOption(.{});
115144

145+
// Upstream dependency
146+
const sqlite_dep = b.dependency("sqlite", .{
147+
.target = target,
148+
.optimize = optimize,
149+
});
150+
151+
// Define C flags to use
152+
116153
var flags = std.ArrayList([]const u8).init(b.allocator);
117154
defer flags.deinit();
118155
try flags.append("-std=c99");
@@ -131,50 +168,41 @@ pub fn build(b: *std.Build) !void {
131168

132169
const c_flags = flags.items;
133170

134-
const sqlite_lib = b.addStaticLibrary(.{
135-
.name = "sqlite",
136-
.target = target,
137-
.optimize = optimize,
138-
});
171+
//
172+
// Main library and module
173+
//
139174

140-
sqlite_lib.addIncludePath(b.path("c/"));
141-
sqlite_lib.addCSourceFiles(.{
142-
.files = &[_][]const u8{
143-
"c/sqlite3.c",
144-
"c/workaround.c",
145-
},
146-
.flags = c_flags,
147-
});
148-
sqlite_lib.linkLibC();
149-
sqlite_lib.installHeader(b.path("c/sqlite3.h"), "sqlite3.h");
175+
const sqlite_lib, const sqlite_mod = blk: {
176+
const lib = makeSQLiteLib(b, sqlite_dep, c_flags, target, optimize, .with);
150177

151-
b.installArtifact(sqlite_lib);
178+
const mod = b.addModule("sqlite", .{
179+
.root_source_file = b.path("sqlite.zig"),
180+
.link_libc = true,
181+
});
182+
mod.addIncludePath(b.path("c"));
183+
mod.addIncludePath(sqlite_dep.path("."));
184+
mod.linkLibrary(lib);
152185

153-
// Create the public 'sqlite' module to be exported
154-
const sqlite_mod = b.addModule("sqlite", .{
155-
.root_source_file = b.path("sqlite.zig"),
156-
.link_libc = true,
157-
});
158-
sqlite_mod.addIncludePath(b.path("c/"));
159-
sqlite_mod.linkLibrary(sqlite_lib);
186+
break :blk .{ lib, mod };
187+
};
188+
_ = sqlite_lib;
160189

161-
// Tool to preprocess the sqlite header files.
162-
//
163-
// Due to limitations of translate-c the standard header files can't be used for building loadable extensions
164-
// so we have this tool which creates usable header files.
190+
const sqliteext_mod = blk: {
191+
const lib = makeSQLiteLib(b, sqlite_dep, c_flags, target, optimize, .without);
165192

166-
const preprocess_files_tool = b.addExecutable(.{
167-
.name = "preprocess-files",
168-
.root_source_file = b.path("tools/preprocess_files.zig"),
169-
.target = getTarget(target),
170-
.optimize = optimize,
171-
});
193+
const mod = b.addModule("sqliteext", .{
194+
.root_source_file = b.path("sqlite.zig"),
195+
.link_libc = true,
196+
});
197+
mod.addIncludePath(b.path("c"));
198+
mod.linkLibrary(lib);
172199

173-
// Add a top-level step to run the preprocess-files tool
174-
const preprocess_files_run = b.step("preprocess-files", "Run the preprocess-files tool");
200+
break :blk mod;
201+
};
175202

176-
const preprocess_files_tool_run = b.addRunArtifact(preprocess_files_tool);
177-
preprocess_files_run.dependOn(&preprocess_files_tool_run.step);
203+
//
204+
// Tests
205+
//
178206

179207
const test_targets = computeTestTargets(query.isNative(), ci) orelse &[_]TestTarget{.{
180208
.query = query,
@@ -195,19 +223,7 @@ pub fn build(b: *std.Build) !void {
195223
single_threaded_txt,
196224
});
197225

198-
const test_sqlite_lib = b.addStaticLibrary(.{
199-
.name = "sqlite",
200-
.target = cross_target,
201-
.optimize = optimize,
202-
});
203-
test_sqlite_lib.addCSourceFiles(.{
204-
.files = &[_][]const u8{
205-
"c/sqlite3.c",
206-
"c/workaround.c",
207-
},
208-
.flags = c_flags,
209-
});
210-
test_sqlite_lib.linkLibC();
226+
const test_sqlite_lib = makeSQLiteLib(b, sqlite_dep, c_flags, cross_target, optimize, .with);
211227

212228
const tests = b.addTest(.{
213229
.name = test_name,
@@ -217,6 +233,7 @@ pub fn build(b: *std.Build) !void {
217233
.single_threaded = test_target.single_threaded,
218234
});
219235
tests.addIncludePath(b.path("c"));
236+
tests.addIncludePath(sqlite_dep.path("."));
220237
tests.linkLibrary(test_sqlite_lib);
221238

222239
const tests_options = b.addOptions();
@@ -229,55 +246,118 @@ pub fn build(b: *std.Build) !void {
229246
test_step.dependOn(&run_tests.step);
230247
}
231248

232-
const lib = b.addStaticLibrary(.{
233-
.name = "sqlite",
234-
.target = getTarget(target),
235-
.optimize = optimize,
236-
});
237-
lib.addCSourceFile(.{ .file = b.path("c/sqlite3.c"), .flags = c_flags });
238-
lib.addIncludePath(b.path("c"));
239-
lib.linkLibC();
249+
// This builds an example shared library with the extension and a binary that tests it.
250+
251+
const zigcrypto_install_artifact = addZigcrypto(b, sqliteext_mod, target, optimize);
252+
test_step.dependOn(&zigcrypto_install_artifact.step);
253+
254+
const zigcrypto_test_run = addZigcryptoTestRun(b, sqlite_mod, target, optimize);
255+
test_step.dependOn(&zigcrypto_test_run.step);
240256

241257
//
242-
// Examples
258+
// Tools
243259
//
244260

245-
// Loadable extension
246-
//
247-
// This builds an example shared library with the extension and a binary that tests it.
261+
addPreprocessStep(b, sqlite_dep);
262+
}
263+
264+
fn addPreprocessStep(b: *std.Build, sqlite_dep: *std.Build.Dependency) void {
265+
var wf = b.addWriteFiles();
266+
267+
// Preprocessing step
268+
const preprocess = PreprocessStep.create(b, .{
269+
.source = sqlite_dep.path("."),
270+
.target = wf.getDirectory(),
271+
});
272+
preprocess.step.dependOn(&wf.step);
273+
274+
const w = b.addUpdateSourceFiles();
275+
w.addCopyFileToSource(preprocess.target.join(b.allocator, "loadable-ext-sqlite3.h") catch @panic("OOM"), "c/loadable-ext-sqlite3.h");
276+
w.addCopyFileToSource(preprocess.target.join(b.allocator, "loadable-ext-sqlite3ext.h") catch @panic("OOM"), "c/loadable-ext-sqlite3ext.h");
277+
w.step.dependOn(&preprocess.step);
248278

249-
const zigcrypto_loadable_ext = b.addSharedLibrary(.{
279+
const preprocess_headers = b.step("preprocess-headers", "Preprocess the headers for the loadable extensions");
280+
preprocess_headers.dependOn(&w.step);
281+
}
282+
283+
fn addZigcrypto(b: *std.Build, sqlite_mod: *std.Build.Module, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Step.InstallArtifact {
284+
const exe = b.addSharedLibrary(.{
250285
.name = "zigcrypto",
251286
.root_source_file = b.path("examples/zigcrypto.zig"),
252287
.version = null,
253288
.target = getTarget(target),
254289
.optimize = optimize,
255290
});
256-
zigcrypto_loadable_ext.addIncludePath(b.path("c"));
257-
zigcrypto_loadable_ext.root_module.addImport("sqlite", sqlite_mod);
258-
zigcrypto_loadable_ext.linkLibrary(lib);
291+
exe.root_module.addImport("sqlite", sqlite_mod);
259292

260-
const install_zigcrypto_loadable_ext = b.addInstallArtifact(zigcrypto_loadable_ext, .{});
293+
const install_artifact = b.addInstallArtifact(exe, .{});
294+
install_artifact.step.dependOn(&exe.step);
261295

296+
return install_artifact;
297+
}
298+
299+
fn addZigcryptoTestRun(b: *std.Build, sqlite_mod: *std.Build.Module, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode) *std.Build.Step.Run {
262300
const zigcrypto_test = b.addExecutable(.{
263301
.name = "zigcrypto-test",
264302
.root_source_file = b.path("examples/zigcrypto_test.zig"),
265303
.target = getTarget(target),
266304
.optimize = optimize,
267305
});
268-
zigcrypto_test.addIncludePath(b.path("c"));
269306
zigcrypto_test.root_module.addImport("sqlite", sqlite_mod);
270-
zigcrypto_test.linkLibrary(lib);
271307

272-
const install_zigcrypto_test = b.addInstallArtifact(zigcrypto_test, .{});
308+
const install = b.addInstallArtifact(zigcrypto_test, .{});
309+
install.step.dependOn(&zigcrypto_test.step);
310+
311+
const run = b.addRunArtifact(zigcrypto_test);
312+
run.step.dependOn(&zigcrypto_test.step);
273313

274-
const zigcrypto_compile_run = b.step("zigcrypto", "Build the 'zigcrypto' SQLite loadable extension");
275-
zigcrypto_compile_run.dependOn(&install_zigcrypto_loadable_ext.step);
276-
zigcrypto_compile_run.dependOn(&install_zigcrypto_test.step);
314+
return run;
277315
}
278316

279317
// See https://www.sqlite.org/compile.html for flags
280318
const EnableOptions = struct {
281319
// https://www.sqlite.org/fts5.html
282320
fts5: bool = false,
283321
};
322+
323+
const PreprocessStep = struct {
324+
const Config = struct {
325+
source: std.Build.LazyPath,
326+
target: std.Build.LazyPath,
327+
};
328+
329+
step: std.Build.Step,
330+
331+
source: std.Build.LazyPath,
332+
target: std.Build.LazyPath,
333+
334+
fn create(owner: *std.Build, config: Config) *PreprocessStep {
335+
const step = owner.allocator.create(PreprocessStep) catch @panic("OOM");
336+
step.* = .{
337+
.step = std.Build.Step.init(.{
338+
.id = std.Build.Step.Id.custom,
339+
.name = "preprocess",
340+
.owner = owner,
341+
.makeFn = make,
342+
}),
343+
.source = config.source,
344+
.target = config.target,
345+
};
346+
347+
return step;
348+
}
349+
350+
fn make(step: *std.Build.Step, _: std.Build.Step.MakeOptions) !void {
351+
const ps: *PreprocessStep = @fieldParentPtr("step", step);
352+
const owner = step.owner;
353+
354+
const sqlite3_h = try ps.source.path(owner, "sqlite3.h").getPath3(owner, step).toString(owner.allocator);
355+
const sqlite3ext_h = try ps.source.path(owner, "sqlite3ext.h").getPath3(owner, step).toString(owner.allocator);
356+
357+
const loadable_sqlite3_h = try ps.target.path(owner, "loadable-ext-sqlite3.h").getPath3(owner, step).toString(owner.allocator);
358+
const loadable_sqlite3ext_h = try ps.target.path(owner, "loadable-ext-sqlite3ext.h").getPath3(owner, step).toString(owner.allocator);
359+
360+
try Preprocessor.sqlite3(owner.allocator, sqlite3_h, loadable_sqlite3_h);
361+
try Preprocessor.sqlite3ext(owner.allocator, sqlite3ext_h, loadable_sqlite3ext_h);
362+
}
363+
};

build.zig.zon

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
.{
22
.name = "sqlite",
3-
.version = "3.47.2",
3+
.version = "3.48.0",
4+
.dependencies = .{
5+
.sqlite = .{
6+
.url = "https://sqlite.org/2025/sqlite-amalgamation-3480000.zip",
7+
.hash = "1220972595d70da33d69d519392742482cb9762935cecb99924e31f3898d2a330861",
8+
},
9+
},
410
.paths = .{"."},
511
}

tools/preprocess_files.zig renamed to build/Preprocessor.zig

Lines changed: 12 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
const std = @import("std");
22
const debug = std.debug;
3-
const fmt = std.fmt;
4-
const heap = std.heap;
53
const mem = std.mem;
64

75
// This tool is used to preprocess the sqlite3 headers to make them usable to build loadable extensions.
@@ -155,14 +153,8 @@ const Processor = struct {
155153
}
156154
};
157155

158-
fn preprocessSqlite3HeaderFile(gpa: mem.Allocator) !void {
159-
var arena = heap.ArenaAllocator.init(gpa);
160-
defer arena.deinit();
161-
const allocator = arena.allocator();
162-
163-
//
164-
165-
const data = try readOriginalData(allocator, "c/sqlite3.h");
156+
pub fn sqlite3(allocator: mem.Allocator, input_path: []const u8, output_path: []const u8) !void {
157+
const data = try readOriginalData(allocator, input_path);
166158

167159
var processor = try Processor.init(allocator, data);
168160

@@ -198,21 +190,17 @@ fn preprocessSqlite3HeaderFile(gpa: mem.Allocator) !void {
198190
processor.rangeDelete();
199191
}
200192

201-
// Write the result to the file
202-
var output_file = try std.fs.cwd().createFile("./c/loadable-ext-sqlite3.h", .{ .mode = 0o0644 });
193+
// Write the result
194+
195+
var output_file = try std.fs.cwd().createFile(output_path, .{ .mode = 0o0644 });
203196
defer output_file.close();
204197

198+
try output_file.writeAll("/* sqlite3.h edited by the zig-sqlite build script */\n");
205199
try processor.dump(output_file.writer());
206200
}
207201

208-
fn preprocessSqlite3ExtHeaderFile(gpa: mem.Allocator) !void {
209-
var arena = heap.ArenaAllocator.init(gpa);
210-
defer arena.deinit();
211-
const allocator = arena.allocator();
212-
213-
//
214-
215-
const data = try readOriginalData(allocator, "c/sqlite3ext.h");
202+
pub fn sqlite3ext(allocator: mem.Allocator, input_path: []const u8, output_path: []const u8) !void {
203+
const data = try readOriginalData(allocator, input_path);
216204

217205
var processor = try Processor.init(allocator, data);
218206

@@ -238,17 +226,11 @@ fn preprocessSqlite3ExtHeaderFile(gpa: mem.Allocator) !void {
238226
processor.rangeDelete();
239227
}
240228

241-
// Write the result to the file
242-
var output_file = try std.fs.cwd().createFile("./c/loadable-ext-sqlite3ext.h", .{ .mode = 0o0644 });
229+
// Write the result
230+
231+
var output_file = try std.fs.cwd().createFile(output_path, .{ .mode = 0o0644 });
243232
defer output_file.close();
244233

234+
try output_file.writeAll("/* sqlite3ext.h edited by the zig-sqlite build script */\n");
245235
try processor.dump(output_file.writer());
246236
}
247-
248-
pub fn main() !void {
249-
var gpa = heap.GeneralPurposeAllocator(.{}){};
250-
defer if (gpa.deinit() == .leak) debug.panic("leaks detected\n", .{});
251-
252-
try preprocessSqlite3HeaderFile(gpa.allocator());
253-
try preprocessSqlite3ExtHeaderFile(gpa.allocator());
254-
}

0 commit comments

Comments
 (0)