1use std::{collections::HashMap, env, iter, ops::Deref, path::PathBuf};
17
18use strum::{EnumDiscriminants, IntoDiscriminant};
19
20pub fn build(location: Location, config: impl AsRef<Config>) -> Build {
22 let config = config.as_ref();
23
24 let mut compiler = cc::Build::new();
25 compiler.file(location.input());
26
27 config.apply(&mut compiler);
28 compiler.warnings(false);
29
30 compiler.out_dir(&location.dest);
31 compiler.compile("sqlite3");
32
33 Build::new(location)
34}
35
36#[derive(PartialEq, Eq, Clone, Debug)]
37pub struct Build {
38 location: Location,
39}
40
41impl Build {
42 const fn new(location: Location) -> Build {
43 Self { location }
44 }
45
46 pub fn sources(&self) -> impl Iterator<Item = PathBuf> {
47 iter::once(self.input())
48 }
49}
50
51impl Deref for Build {
52 type Target = Location;
53
54 fn deref(&self) -> &Self::Target {
55 &self.location
56 }
57}
58
59#[derive(PartialEq, Eq, Clone, Debug)]
61pub struct Location {
62 src: PathBuf,
63 dest: PathBuf,
64}
65
66impl Location {
67 pub fn new(dest: impl Into<PathBuf>) -> Self {
69 Self {
70 src: PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("amalgamation"),
71 dest: dest.into(),
72 }
73 }
74
75 pub fn input(&self) -> PathBuf {
77 self.src.join("sqlite3.c")
78 }
79
80 pub fn header(&self) -> PathBuf {
82 self.src.join("sqlite3.h")
83 }
84
85 pub fn dest(&self) -> PathBuf {
87 self.dest.clone()
88 }
89
90 pub fn sources(&self) -> impl Iterator<Item = PathBuf> {
92 iter::once(self.input()).chain(iter::once(self.header()))
93 }
94}
95
96impl Default for Location {
97 fn default() -> Self {
98 Self::new(PathBuf::from(
99 env::var_os("OUT_DIR").expect("$OUT_DIR not set"),
100 ))
101 }
102}
103
104#[derive(PartialEq, Eq, Clone, Debug)]
108pub struct Config {
109 settings: HashMap<SettingKey, Setting>,
110}
111
112impl Config {
113 pub fn new(settings: impl IntoIterator<Item = Setting>) -> Self {
114 Self {
115 settings: settings
116 .into_iter()
117 .map(|setting| (setting.discriminant(), setting))
118 .collect(),
119 }
120 }
121
122 pub fn get(&self, key: SettingKey) -> Option<Setting> {
123 self.settings.get(&key).copied()
124 }
125
126 pub fn set(&mut self, setting: Setting) {
127 self.settings.insert(setting.discriminant(), setting);
128 }
129
130 fn apply(&self, build: &mut cc::Build) {
131 for setting in self.settings.values() {
132 setting.apply(build);
133 }
134 }
135}
136
137impl AsRef<Config> for Config {
138 fn as_ref(&self) -> &Config {
139 self
140 }
141}
142
143impl Default for Config {
144 fn default() -> Self {
145 Self::new([
146 Setting::Sync(Synchronous::Full),
147 Setting::WalSync(Synchronous::Normal),
148 Setting::Threading(Threading::MultiThread),
149 Setting::DoubleQuotedStrings {
150 in_ddl: false,
151 in_dml: false,
152 },
153 Setting::DefaultForeignKeys(true),
154 Setting::DefaultMemoryStatus(false),
155 Setting::EnableAlloca(true),
156 Setting::EnableAuthorization(false),
157 Setting::EnableAutomaticIndex(true),
158 Setting::EnableAutomaticInitialize(true), Setting::EnableAutomaticReset(false),
160 Setting::EnableBlobIo(false),
161 Setting::EnableColumnDeclaredType(false),
162 Setting::EnableDatabasePagesVirtualTable(false),
163 Setting::EnableDatabaseStatisticsVirtualTable(false),
164 Setting::EnableDatabaseUri(true),
165 Setting::EnableDeprecated(false),
166 Setting::EnableGetTable(false),
167 Setting::EnableMemoryManagement(true),
168 Setting::EnableProgressCallback(false),
169 Setting::EnableSharedCache(false),
170 Setting::EnableTrace(false),
171 Setting::EnableUtf16(false),
172 Setting::EnableVirtualTables(true),
173 Setting::EnableWriteAheadLog(true),
174 Setting::LikeOperatorMatchesBlob(false),
175 Setting::MaxExpressionDepth(0),
176 #[cfg(debug_assertions)]
177 Setting::EnableApiArmor(true),
178 #[cfg(debug_assertions)]
179 Setting::Debug(true),
180 ])
181 }
182}
183
184#[derive(EnumDiscriminants, PartialEq, Eq, Clone, Copy, Debug)]
188#[strum_discriminants(name(SettingKey))]
189#[strum_discriminants(derive(Hash))]
190pub enum Setting {
191 #[doc(alias = "SQLITE_DQS")]
192 DoubleQuotedStrings { in_ddl: bool, in_dml: bool },
193 #[doc(alias = "SQLITE_THREADSAFE")]
194 Threading(Threading),
195 #[doc(alias = "SQLITE_DEBUG")]
196 Debug(bool),
197 #[doc(alias = "SQLITE_DEFAULT_SYNCHRONOUS")]
198 Sync(Synchronous),
199 #[doc(alias = "SQLITE_DEFAULT_WAL_SYNCHRONOUS")]
200 WalSync(Synchronous),
201 #[doc(alias = "SQLITE_DEFAULT_AUTOMATIC_INDEX")]
202 DefaultAutomaticIndex(bool),
203 #[doc(alias = "SQLITE_DEFAULT_AUTOVACUUM")]
204 DefaultAutomaticVacuum(bool),
205 #[doc(alias = "SQLITE_DEFAULT_FOREIGN_KEYS")]
206 DefaultForeignKeys(bool),
207 #[doc(alias = "SQLITE_DEFAULT_MEMSTATUS")]
208 DefaultMemoryStatus(bool),
209 #[doc(alias = "SQLITE_USE_ALLOCA")]
210 EnableAlloca(bool),
211 #[doc(alias = "SQLITE_ENABLE_API_ARMOR")]
212 EnableApiArmor(bool),
213 #[doc(alias = "SQLITE_OMIT_AUTHORIZATION")]
214 EnableAuthorization(bool),
215 #[doc(alias = "SQLITE_OMIT_AUTOMATIC_INDEX")]
216 EnableAutomaticIndex(bool),
217 #[doc(alias = "SQLITE_OMIT_AUTOINIT")]
218 EnableAutomaticInitialize(bool),
219 #[doc(alias = "SQLITE_OMIT_AUTORESET")]
220 EnableAutomaticReset(bool),
221 #[doc(alias = "SQLITE_OMIT_INCRBLOB")]
222 EnableBlobIo(bool),
223 #[doc(alias = "SQLITE_OMIT_DECLTYPE")]
224 EnableColumnDeclaredType(bool),
225 #[doc(alias = "SQLITE_ENABLE_COLUMN_METADATA")]
226 EnableColumnMetadata(bool),
227 #[doc(alias = "SQLITE_ENABLE_DBPAGE_VTAB")]
228 EnableDatabasePagesVirtualTable(bool),
229 #[doc(alias = "SQLITE_ENABLE_DBSTAT_VTAB")]
230 EnableDatabaseStatisticsVirtualTable(bool),
231 #[doc(alias = "SQLITE_USE_URI")]
232 EnableDatabaseUri(bool),
233 #[doc(alias = "SQLITE_OMIT_DEPRECATED")]
234 EnableDeprecated(bool),
235 #[doc(alias = "SQLITE_ENABLE_GEOPOLY")]
236 EnableGeopoly(bool),
237 #[doc(alias = "SQLITE_OMIT_GET_TABLE")]
238 EnableGetTable(bool),
239 #[doc(alias = "SQLITE_ENABLE_FTS3")]
240 #[doc(alias = "SQLITE_ENABLE_FTS4")]
241 EnableFts3(bool),
242 #[doc(alias = "SQLITE_ENABLE_FTS5")]
243 EnableFts5(bool),
244 #[doc(alias = "SQLITE_ENABLE_JSON1")]
245 #[doc(alias = "SQLITE_OMIT_JSON")]
246 EnableJson(bool),
247 #[doc(alias = "SQLITE_OMIT_LOAD_EXTENSION")]
248 EnableLoadExtension(bool),
249 #[doc(alias = "SQLITE_ENABLE_MEMORY_MANAGEMENT")]
250 EnableMemoryManagement(bool),
251 #[doc(alias = "SQLITE_ENABLE_NORMALIZE")]
252 EnableNormalizeSql(bool),
253 #[doc(alias = "SQLITE_ENABLE_PREUPDATE_HOOK")]
254 EnablePreUpdateHook(bool),
255 #[doc(alias = "SQLITE_OMIT_PROGRESS_CALLBACK")]
256 EnableProgressCallback(bool),
257 #[doc(alias = "SQLITE_ENABLE_RTREE")]
258 EnableRtree(bool),
259 #[doc(alias = "SQLITE_ENABLE_STAT4")]
260 EnableStat4(bool),
261 #[doc(alias = "SQLITE_OMIT_DESERIALIZE")]
262 EnableSerialize(bool),
263 #[doc(alias = "SQLITE_ENABLE_SESSION")]
264 EnableSession(bool),
265 #[doc(alias = "SQLITE_ENABLE_SNAPSHOT")]
266 EnableSnapshot(bool),
267 #[doc(alias = "SQLITE_OMIT_SHARED_CACHE")]
268 EnableSharedCache(bool),
269 #[doc(alias = "SQLITE_SOUNDEX")]
270 EnableSoundex(bool),
271 #[doc(alias = "SQLITE_OMIT_TCL_VARIABLE")]
272 EnableTclVariables(bool),
273 #[doc(alias = "SQLITE_OMIT_TEMPDB")]
274 EnableTemporaryDatabase(bool),
275 #[doc(alias = "SQLITE_OMIT_TRACE")]
276 EnableTrace(bool),
277 #[doc(alias = "SQLITE_OMIT_UTF16")]
278 EnableUtf16(bool),
279 #[doc(alias = "SQLITE_OMIT_VIRTUALTABLE")]
280 EnableVirtualTables(bool),
281 #[doc(alias = "SQLITE_OMIT_VIRTUALTABLE")]
282 EnableWriteAheadLog(bool),
283 #[doc(alias = "SQLITE_OMIT_WAL")]
284 LikeOperatorCaseSensitive(bool),
285 #[doc(alias = "SQLITE_LIKE_DOESNT_MATCH_BLOBS")]
286 LikeOperatorMatchesBlob(bool),
287 #[doc(alias = "SQLITE_MAX_ATTACHED")]
288 MaxAttachedDatabases(usize),
289 #[doc(alias = "SQLITE_MAX_COLUMN")]
290 MaxColumns(usize),
291 #[doc(alias = "SQLITE_MAX_EXPR_DEPTH")]
292 MaxExpressionDepth(usize),
293 #[doc(alias = "SQLITE_JSON_MAX_DEPTH")]
294 MaxJsonDepth(usize),
295 #[doc(alias = "SQLITE_MAX_VARIABLE_NUMBER")]
296 MaxVariables(usize),
297 #[doc(alias = "SQLITE_SECURE_DELETE")]
298 SecureDelete(bool),
299 #[doc(alias = "SQLITE_TEMP_STORE")]
300 TemporaryStorage(TemporaryStorage),
301 #[doc(alias = "SQLITE_TRUSTED_SCHEMA")]
302 TrustedSchema(bool),
303}
304
305impl Setting {
306 fn apply(&self, build: &mut cc::Build) {
307 match *self {
308 Setting::Debug(enable) => {
309 self.define(build, "SQLITE_DEBUG", enable);
310 }
311 Setting::DefaultAutomaticIndex(enable) => {
312 self.set(build, "SQLITE_DEFAULT_AUTOMATIC_INDEX", enable);
313 }
314 Setting::DefaultAutomaticVacuum(enable) => {
315 self.set(build, "SQLITE_DEFAULT_AUTOVACUUM", enable);
316 }
317 Setting::DefaultForeignKeys(enable) => {
318 self.set(build, "SQLITE_DEFAULT_FOREIGN_KEYS", enable);
319 }
320 Setting::DefaultMemoryStatus(enable) => {
321 self.set(build, "SQLITE_DEFAULT_MEMSTATUS", enable);
322 }
323 Setting::DoubleQuotedStrings { in_ddl, in_dml } => {
324 let value = match (in_ddl, in_dml) {
325 (true, true) => 3,
326 (true, false) => 2,
327 (false, true) => 1,
328 (false, false) => 0,
329 };
330
331 self.set(build, "SQLITE_DQS", value);
332 }
333 Setting::EnableAlloca(enable) => {
334 self.define(build, "SQLITE_USE_ALLOCA", enable);
335 }
336 Setting::EnableApiArmor(enable) => {
337 self.define(build, "SQLITE_ENABLE_API_ARMOR", enable);
338 }
339 Setting::EnableAuthorization(enable) => {
340 self.define(build, "SQLITE_OMIT_AUTHORIZATION", !enable);
341 }
342 Setting::EnableAutomaticIndex(enable) => {
343 self.define(build, "SQLITE_OMIT_AUTOMATIC_INDEX", !enable);
344 }
345 Setting::EnableAutomaticInitialize(enable) => {
346 self.define(build, "SQLITE_OMIT_AUTOINIT", !enable);
347 }
348 Setting::EnableAutomaticReset(enable) => {
349 self.define(build, "SQLITE_OMIT_AUTORESET", !enable);
350 }
351 Setting::EnableBlobIo(enable) => {
352 self.define(build, "SQLITE_OMIT_INCRBLOB", !enable);
353 }
354 Setting::EnableColumnDeclaredType(enable) => {
355 self.define(build, "SQLITE_OMIT_DECLTYPE", !enable);
356 }
357 Setting::EnableColumnMetadata(enable) => {
358 self.define(build, "SQLITE_ENABLE_COLUMN_METADATA", enable);
359 }
360 Setting::EnableDatabasePagesVirtualTable(enable) => {
361 self.define(build, "SQLITE_ENABLE_DBPAGE_VTAB", enable);
362 }
363 Setting::EnableDatabaseStatisticsVirtualTable(enable) => {
364 self.define(build, "SQLITE_ENABLE_DBSTAT_VTAB", enable);
365 }
366 Setting::EnableDatabaseUri(enable) => {
367 self.set(build, "SQLITE_USE_URI", enable);
368 }
369 Setting::EnableDeprecated(enable) => {
370 self.define(build, "SQLITE_OMIT_DEPRECATED", !enable);
371 }
372 Setting::EnableFts3(enable) => {
373 self.define(build, "SQLITE_ENABLE_FTS3", enable);
374 self.define(build, "SQLITE_ENABLE_FTS3_PARENTHESIS", enable);
375 }
376 Setting::EnableFts5(enable) => {
377 self.define(build, "SQLITE_ENABLE_FTS5", enable);
378 }
379 Setting::EnableGeopoly(enable) => {
380 self.define(build, "SQLITE_ENABLE_GEOPOLY", enable);
381 }
382 Setting::EnableGetTable(enable) => {
383 self.define(build, "SQLITE_OMIT_GET_TABLE", !enable);
384 }
385 Setting::EnableJson(enable) => {
386 self.define(build, "SQLITE_OMIT_JSON", !enable);
387 }
388 Setting::EnableLoadExtension(enable) => {
389 self.define(build, "SQLITE_OMIT_LOAD_EXTENSION", !enable);
390 }
391 Setting::EnableMemoryManagement(enable) => {
392 self.define(build, "SQLITE_ENABLE_MEMORY_MANAGEMENT", enable);
393 }
394 Setting::EnableNormalizeSql(enable) => {
395 self.define(build, "SQLITE_ENABLE_NORMALIZE", enable);
396 }
397 Setting::EnablePreUpdateHook(enable) => {
398 self.define(build, "SQLITE_ENABLE_PREUPDATE_HOOK", enable);
399 }
400 Setting::EnableProgressCallback(enable) => {
401 self.define(build, "SQLITE_OMIT_PROGRESS_CALLBACK", !enable);
402 }
403 Setting::EnableRtree(enable) => {
404 self.define(build, "SQLITE_ENABLE_RTREE", enable);
405 }
406 Setting::EnableSerialize(enable) => {
407 self.define(build, "SQLITE_OMIT_DESERIALIZE", !enable);
408 }
409 Setting::EnableSession(enable) => {
410 self.define(build, "SQLITE_ENABLE_SESSION", enable);
411 }
412 Setting::EnableSharedCache(enable) => {
413 self.define(build, "SQLITE_OMIT_SHARED_CACHE", !enable);
414 }
415 Setting::EnableSnapshot(enable) => {
416 self.define(build, "SQLITE_ENABLE_SNAPSHOT", enable);
417 }
418 Setting::EnableSoundex(enable) => {
419 self.define(build, "SQLITE_SOUNDEX", enable);
420 }
421 Setting::EnableStat4(enable) => {
422 self.define(build, "SQLITE_ENABLE_STAT4", enable);
423 }
424 Setting::EnableTclVariables(enable) => {
425 self.define(build, "SQLITE_OMIT_TCL_VARIABLE", !enable);
426 }
427 Setting::EnableTemporaryDatabase(enable) => {
428 self.define(build, "SQLITE_OMIT_TEMPDB", !enable);
429 }
430 Setting::EnableTrace(enable) => {
431 self.define(build, "SQLITE_OMIT_TRACE", !enable);
432 }
433 Setting::EnableUtf16(enable) => {
434 self.define(build, "SQLITE_OMIT_UTF16", !enable);
435 }
436 Setting::EnableVirtualTables(enable) => {
437 self.define(build, "SQLITE_OMIT_VIRTUALTABLE", !enable);
438 }
439 Setting::EnableWriteAheadLog(enable) => {
440 self.define(build, "SQLITE_OMIT_WAL", !enable);
441 }
442 Setting::LikeOperatorCaseSensitive(enable) => {
443 self.define(build, "SQLITE_CASE_SENSITIVE_LIKE", enable);
444 }
445 Setting::LikeOperatorMatchesBlob(enable) => {
446 self.define(build, "SQLITE_LIKE_DOESNT_MATCH_BLOBS", !enable);
447 }
448 Setting::MaxAttachedDatabases(max) => {
449 self.set(build, "SQLITE_MAX_ATTACHED", max);
450 }
451 Setting::MaxColumns(max) => {
452 self.set(build, "SQLITE_MAX_COLUMN", max);
453 }
454 Setting::MaxExpressionDepth(max) => {
455 self.set(build, "SQLITE_MAX_EXPR_DEPTH", max);
456 }
457 Setting::MaxJsonDepth(max) => {
458 self.set(build, "SQLITE_JSON_MAX_DEPTH", max);
459 }
460 Setting::MaxVariables(max) => {
461 self.set(build, "SQLITE_MAX_VARIABLE_NUMBER", max);
462 }
463 Setting::SecureDelete(enable) => {
464 self.define(build, "SQLITE_SECURE_DELETE", enable);
465 }
466 Setting::Sync(synchronous) => {
467 self.set(build, "SQLITE_DEFAULT_SYNCHRONOUS", synchronous);
468 }
469 Setting::Threading(threading) => {
470 self.set(build, "SQLITE_THREADSAFE", threading);
471 }
472 Setting::TemporaryStorage(mode) => {
473 self.set(build, "SQLITE_TEMP_STORE", mode);
474 }
475 Setting::TrustedSchema(enable) => {
476 self.set(build, "SQLITE_TRUSTED_SCHEMA", enable);
477 }
478 Setting::WalSync(synchronous) => {
479 self.set(build, "SQLITE_DEFAULT_WAL_SYNCHRONOUS", synchronous);
480 }
481 }
482 }
483
484 fn define(&self, build: &mut cc::Build, name: &'static str, enable: bool) {
485 if enable {
486 build.define(name, None);
487 }
488 }
489
490 fn set(&self, build: &mut cc::Build, name: &'static str, value: impl SettingValue) {
491 value.apply(build, name);
492 }
493}
494
495#[derive(PartialEq, Eq, Clone, Copy, Debug)]
496#[repr(usize)]
497pub enum TemporaryStorage {
498 AlwaysFilesystem = 0,
499 DefaultFilesystem = 1,
500 DefaultMemory = 2,
501 AlwaysMemory = 3,
502}
503
504#[derive(PartialEq, Eq, Clone, Copy, Debug)]
505#[repr(usize)]
506pub enum Threading {
507 SingleThread = 0,
508 MultiThread = 1,
509 Serialized = 2,
510}
511
512#[derive(PartialEq, Eq, Clone, Copy, Debug)]
513#[repr(usize)]
514pub enum Synchronous {
515 Off = 0,
516 Normal = 1,
517 Full = 2,
518 Extra = 3,
519}
520
521trait SettingValue {
522 fn apply(&self, build: &mut cc::Build, name: &'static str);
523}
524
525impl SettingValue for bool {
526 fn apply(&self, build: &mut cc::Build, name: &'static str) {
527 let value = if *self { "1" } else { "0" };
528 build.define(name, value);
529 }
530}
531
532impl SettingValue for usize {
533 fn apply(&self, build: &mut cc::Build, name: &'static str) {
534 let value = self.to_string();
535 build.define(name, value.as_str());
536 }
537}
538
539impl SettingValue for TemporaryStorage {
540 fn apply(&self, build: &mut cc::Build, name: &'static str) {
541 (*self as usize).apply(build, name);
542 }
543}
544
545impl SettingValue for Threading {
546 fn apply(&self, build: &mut cc::Build, name: &'static str) {
547 (*self as usize).apply(build, name);
548 }
549}
550
551impl SettingValue for Synchronous {
552 fn apply(&self, build: &mut cc::Build, name: &'static str) {
553 (*self as usize).apply(build, name);
554 }
555}