summaryrefslogtreecommitdiff
path: root/build
diff options
context:
space:
mode:
authorGravatar Vincent Rischmann2025-01-12 18:31:10 +0100
committerGravatar Vincent Rischmann2025-02-16 00:02:44 +0100
commit4a0483910c9752d69bbd91af1ab12a6443d3b0a6 (patch)
treea0bcaee9d1b3f902f3b580210205ba6cd1f63515 /build
parentMerge pull request #180 from nektro/patch-1 (diff)
downloadzig-sqlite-4a0483910c9752d69bbd91af1ab12a6443d3b0a6.tar.gz
zig-sqlite-4a0483910c9752d69bbd91af1ab12a6443d3b0a6.tar.xz
zig-sqlite-4a0483910c9752d69bbd91af1ab12a6443d3b0a6.zip
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`
Diffstat (limited to 'build')
-rw-r--r--build/Preprocessor.zig236
1 files changed, 236 insertions, 0 deletions
diff --git a/build/Preprocessor.zig b/build/Preprocessor.zig
new file mode 100644
index 0000000..a069523
--- /dev/null
+++ b/build/Preprocessor.zig
@@ -0,0 +1,236 @@
1const std = @import("std");
2const debug = std.debug;
3const mem = std.mem;
4
5// This tool is used to preprocess the sqlite3 headers to make them usable to build loadable extensions.
6//
7// Due to limitations of `zig translate-c` (used by @cImport) the code produced by @cImport'ing the sqlite3ext.h header is unusable.
8// The sqlite3ext.h header redefines the SQLite API like this:
9//
10// #define sqlite3_open_v2 sqlite3_api->open_v2
11//
12// This is not supported by `zig translate-c`, if there's already a definition for a function the aliasing macros won't do anything:
13// translate-c keeps generating the code for the function defined in sqlite3.h
14//
15// Even if there's no definition already (we could for example remove the definition manually from the sqlite3.h file),
16// the code generated fails to compile because it references the variable sqlite3_api which is not defined
17//
18// And even if the sqlite3_api is defined before, the generated code fails to compile because the functions are defined as consts and
19// can only reference comptime stuff, however sqlite3_api is a runtime variable.
20//
21// The only viable option is to completely reomve the original function definitions and redefine all functions in Zig which forward
22// calls to the sqlite3_api object.
23//
24// This works but it requires fairly extensive modifications of both sqlite3.h and sqlite3ext.h which is time consuming to do manually;
25// this tool is intended to automate all these modifications.
26
27fn readOriginalData(allocator: mem.Allocator, path: []const u8) ![]const u8 {
28 var file = try std.fs.cwd().openFile(path, .{});
29 defer file.close();
30
31 var reader = file.reader();
32
33 const data = reader.readAllAlloc(allocator, 1024 * 1024);
34 return data;
35}
36
37const Processor = struct {
38 const Range = union(enum) {
39 delete: struct {
40 start: usize,
41 end: usize,
42 },
43 replace: struct {
44 start: usize,
45 end: usize,
46 replacement: []const u8,
47 },
48 };
49
50 allocator: mem.Allocator,
51
52 data: []const u8,
53 pos: usize,
54
55 range_start: usize,
56 ranges: std.ArrayList(Range),
57
58 fn init(allocator: mem.Allocator, data: []const u8) !Processor {
59 return .{
60 .allocator = allocator,
61 .data = data,
62 .pos = 0,
63 .range_start = 0,
64 .ranges = try std.ArrayList(Range).initCapacity(allocator, 4096),
65 };
66 }
67
68 fn readable(self: *Processor) []const u8 {
69 if (self.pos >= self.data.len) return "";
70
71 return self.data[self.pos..];
72 }
73
74 fn previousByte(self: *Processor) ?u8 {
75 if (self.pos <= 0) return null;
76 return self.data[self.pos - 1];
77 }
78
79 fn skipUntil(self: *Processor, needle: []const u8) bool {
80 const pos = mem.indexOfPos(u8, self.data, self.pos, needle);
81 if (pos) |p| {
82 self.pos = p;
83 return true;
84 }
85 return false;
86 }
87
88 fn consume(self: *Processor, needle: []const u8) void {
89 debug.assert(self.startsWith(needle));
90
91 self.pos += needle.len;
92 }
93
94 fn startsWith(self: *Processor, needle: []const u8) bool {
95 if (self.pos >= self.data.len) return false;
96
97 const data = self.data[self.pos..];
98 return mem.startsWith(u8, data, needle);
99 }
100
101 fn rangeStart(self: *Processor) void {
102 self.range_start = self.pos;
103 }
104
105 fn rangeDelete(self: *Processor) void {
106 self.ranges.appendAssumeCapacity(Range{
107 .delete = .{
108 .start = self.range_start,
109 .end = self.pos,
110 },
111 });
112 }
113
114 fn rangeReplace(self: *Processor, replacement: []const u8) void {
115 self.ranges.appendAssumeCapacity(Range{
116 .replace = .{
117 .start = self.range_start,
118 .end = self.pos,
119 .replacement = replacement,
120 },
121 });
122 }
123
124 fn dump(self: *Processor, writer: anytype) !void {
125 var pos: usize = 0;
126 for (self.ranges.items) |range| {
127 switch (range) {
128 .delete => |dr| {
129 const to_write = self.data[pos..dr.start];
130 try writer.writeAll(to_write);
131 pos = dr.end;
132 },
133 .replace => |rr| {
134 const to_write = self.data[pos..rr.start];
135 try writer.writeAll(to_write);
136 try writer.writeAll(rr.replacement);
137 pos = rr.end;
138 },
139 }
140
141 // debug.print("excluded range: start={d} end={d} slice=\"{s}\"\n", .{
142 // range.start,
143 // range.end,
144 // processor.data[range.start..range.end],
145 // });
146 }
147
148 // Finally append the remaining data in the buffer (the last range will probably not be the end of the file)
149 if (pos < self.data.len) {
150 const remaining_data = self.data[pos..];
151 try writer.writeAll(remaining_data);
152 }
153 }
154};
155
156pub fn sqlite3(allocator: mem.Allocator, input_path: []const u8, output_path: []const u8) !void {
157 const data = try readOriginalData(allocator, input_path);
158
159 var processor = try Processor.init(allocator, data);
160
161 while (true) {
162 // Everything function definition is declared with SQLITE_API.
163 // Stop the loop if there's none in the remaining data.
164 if (!processor.skipUntil("SQLITE_API ")) break;
165
166 // If the byte just before is not a LN it's not a function definition.
167 // There are a couple instances where SQLITE_API appears in a comment.
168 const previous_byte = processor.previousByte() orelse 0;
169 if (previous_byte != '\n') {
170 processor.consume("SQLITE_API ");
171 continue;
172 }
173
174 // Now we assume we're at the start of a function definition.
175 //
176 // We keep track of every function definition by marking its start and end position in the data.
177
178 processor.rangeStart();
179
180 processor.consume("SQLITE_API ");
181 if (processor.startsWith("SQLITE_EXTERN ")) {
182 // This is not a function definition, ignore it.
183 // try processor.unmark();
184 continue;
185 }
186
187 _ = processor.skipUntil(");\n");
188 processor.consume(");\n");
189
190 processor.rangeDelete();
191 }
192
193 // Write the result
194
195 var output_file = try std.fs.cwd().createFile(output_path, .{ .mode = 0o0644 });
196 defer output_file.close();
197
198 try output_file.writeAll("/* sqlite3.h edited by the zig-sqlite build script */\n");
199 try processor.dump(output_file.writer());
200}
201
202pub fn sqlite3ext(allocator: mem.Allocator, input_path: []const u8, output_path: []const u8) !void {
203 const data = try readOriginalData(allocator, input_path);
204
205 var processor = try Processor.init(allocator, data);
206
207 // Replace the include line
208
209 debug.assert(processor.skipUntil("#include \"sqlite3.h\""));
210
211 processor.rangeStart();
212 processor.consume("#include \"sqlite3.h\"");
213 processor.rangeReplace("#include \"loadable-ext-sqlite3.h\"");
214
215 // Delete all #define macros
216
217 while (true) {
218 if (!processor.skipUntil("#define sqlite3_")) break;
219
220 processor.rangeStart();
221
222 processor.consume("#define sqlite3_");
223 _ = processor.skipUntil("\n");
224 processor.consume("\n");
225
226 processor.rangeDelete();
227 }
228
229 // Write the result
230
231 var output_file = try std.fs.cwd().createFile(output_path, .{ .mode = 0o0644 });
232 defer output_file.close();
233
234 try output_file.writeAll("/* sqlite3ext.h edited by the zig-sqlite build script */\n");
235 try processor.dump(output_file.writer());
236}