summaryrefslogtreecommitdiffstats
path: root/test/testrunner_data.tcl
blob: f8f12d91a73956ed4b34dab15d3119af10572c8a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
namespace eval trd {
  variable tcltest
  variable extra
  variable all_configs
  variable build


  # Tcl tests to run for various builds.
  #
  set tcltest(linux.Fast-One)             veryquick
  set tcltest(linux.Debug-One)            veryquick
  set tcltest(linux.Debug-Two)            veryquick
  set tcltest(linux.Have-Not)             veryquick
  set tcltest(linux.Secure-Delete)        veryquick
  set tcltest(linux.Unlock-Notify)        veryquick
  set tcltest(linux.User-Auth)            veryquick
  set tcltest(linux.Update-Delete-Limit)  veryquick
  set tcltest(linux.Extra-Robustness)     veryquick
  set tcltest(linux.Device-Two)           veryquick
  set tcltest(linux.No-lookaside)         veryquick
  set tcltest(linux.Devkit)               veryquick
  set tcltest(linux.Apple)                veryquick
  set tcltest(linux.Android)              veryquick
  set tcltest(linux.Sanitize)             veryquick
  set tcltest(linux.Device-One)           all
  set tcltest(linux.Default)              all_plus_autovacuum_crash
  set tcltest(linux.Valgrind)             valgrind

  set tcltest(osx.Locking-Style)          veryquick
  set tcltest(osx.Have-Not)               veryquick
  set tcltest(osx.Apple)                  all_less_no_mutex_try

  set tcltest(win.Stdcall)                veryquick
  set tcltest(win.Have-Not)               veryquick
  set tcltest(win.Windows-Memdebug)       veryquick
  set tcltest(win.Windows-Win32Heap)      veryquick
  set tcltest(win.Windows-Sanitize)       veryquick
  set tcltest(win.Default)                full

  # Extra [make xyz] tests that should be run for various builds.
  #
  set extra(linux.Check-Symbols)          checksymbols
  set extra(linux.Fast-One)               {fuzztest sourcetest}
  set extra(linux.Debug-One)              {fuzztest sourcetest mptest}
  set extra(linux.Debug-Two)              {fuzztest sourcetest}
  set extra(linux.Have-Not)               {fuzztest sourcetest}
  set extra(linux.Secure-Delete)          {fuzztest sourcetest}
  set extra(linux.Unlock-Notify)          {fuzztest sourcetest}
  set extra(linux.Update-Delete-Limit)    {fuzztest sourcetest}
  set extra(linux.Extra-Robustness)       {fuzztest sourcetest}
  set extra(linux.Device-Two)             {fuzztest sourcetest threadtest}
  set extra(linux.No-lookaside)           {fuzztest sourcetest}
  set extra(linux.Devkit)                 {fuzztest sourcetest}
  set extra(linux.Android)                {fuzztest sourcetest}
  set extra(linux.Apple)                  {fuzztest sourcetest}
  set extra(linux.Sanitize)               {fuzztest sourcetest}
  set extra(linux.Default)                {fuzztest sourcetest threadtest}

  set extra(osx.Apple)                    {fuzztest threadtest}
  set extra(osx.Have-Not)                 {fuzztest sourcetest}
  set extra(osx.Locking-Style)            {mptest fuzztest sourcetest}

  set extra(win.Default)                  mptest
  set extra(win.Stdcall)                  {fuzztest sourcetest}
  set extra(win.Windows-Memdebug)         {fuzztest sourcetest}
  set extra(win.Windows-Win32Heap)        {fuzztest sourcetest}
  set extra(win.Windows-Sanitize)         fuzztest
  set extra(win.Have-Not)                 {fuzztest sourcetest}

  # The following mirrors the set of test suites invoked by "all.test".
  #
  set all_configs {
    full no_optimization memsubsys1 memsubsys2 singlethread 
    multithread onefile utf16 exclusive persistent_journal 
    persistent_journal_error no_journal no_journal_error
    autovacuum_ioerr no_mutex_try fullmutex journaltest 
    inmemory_journal pcache0 pcache10 pcache50 pcache90 
    pcache100 prepare mmap
  }

  #-----------------------------------------------------------------------
  # Start of build() definitions.
  #
  set build(Default) {
    -O2
    --disable-amalgamation --disable-shared
    --enable-session
    -DSQLITE_ENABLE_RBU
  }

  # These two are used by [testrunner.tcl mdevtest] (All-O0) and 
  # [testrunner.tcl sdevtest] (All-Sanitize).
  #
  set build(All-Debug) {
    --enable-debug --enable-all
  }
  set build(All-O0) {
    -O0 --enable-all
  }
  set build(All-Sanitize) { 
    -DSQLITE_OMIT_LOOKASIDE=1
    --enable-all -fsanitize=address,undefined -fno-sanitize-recover=undefined
  }

  set build(Sanitize) {
    CC=clang -fsanitize=address,undefined -fno-sanitize-recover=undefined
    -DSQLITE_ENABLE_STAT4
    -DSQLITE_OMIT_LOOKASIDE=1
    -DCONFIG_SLOWDOWN_FACTOR=5.0
    -DSQLITE_ENABLE_RBU
    --enable-debug
    --enable-all
  }
  set build(Stdcall) {
    -DUSE_STDCALL=1
    -O2
  }

  # The "Have-Not" configuration sets all possible -UHAVE_feature options
  # in order to verify that the code works even on platforms that lack
  # these support services.
  set build(Have-Not) {
    -DHAVE_FDATASYNC=0
    -DHAVE_GMTIME_R=0
    -DHAVE_ISNAN=0
    -DHAVE_LOCALTIME_R=0
    -DHAVE_LOCALTIME_S=0
    -DHAVE_MALLOC_USABLE_SIZE=0
    -DHAVE_STRCHRNUL=0
    -DHAVE_USLEEP=0
    -DHAVE_UTIME=0
  }
  set build(Unlock-Notify) {
    -O2
    -DSQLITE_ENABLE_UNLOCK_NOTIFY
    -DSQLITE_THREADSAFE
    -DSQLITE_TCL_DEFAULT_FULLMUTEX=1
  }
  set build(User-Auth) {
    -O2
    -DSQLITE_USER_AUTHENTICATION=1
  }
  set build(Secure-Delete) {
    -O2
    -DSQLITE_SECURE_DELETE=1
    -DSQLITE_SOUNDEX=1
  }
  set build(Update-Delete-Limit) {
    -O2
    -DSQLITE_DEFAULT_FILE_FORMAT=4
    -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1
    -DSQLITE_ENABLE_STMT_SCANSTATUS
    -DSQLITE_LIKE_DOESNT_MATCH_BLOBS
    -DSQLITE_ENABLE_CURSOR_HINTS
  }
  set build(Check-Symbols) {
    -DSQLITE_MEMDEBUG=1
    -DSQLITE_ENABLE_FTS3_PARENTHESIS=1
    -DSQLITE_ENABLE_FTS3=1
    -DSQLITE_ENABLE_RTREE=1
    -DSQLITE_ENABLE_MEMSYS5=1
    -DSQLITE_ENABLE_MEMSYS3=1
    -DSQLITE_ENABLE_COLUMN_METADATA=1
    -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1
    -DSQLITE_SECURE_DELETE=1
    -DSQLITE_SOUNDEX=1
    -DSQLITE_ENABLE_ATOMIC_WRITE=1
    -DSQLITE_ENABLE_MEMORY_MANAGEMENT=1
    -DSQLITE_ENABLE_OVERSIZE_CELL_CHECK=1
    -DSQLITE_ENABLE_STAT4
    -DSQLITE_ENABLE_STMT_SCANSTATUS
    --enable-fts5 --enable-session
  }
  set build(Debug-One) {
    --disable-shared
    -O2 -funsigned-char
    -DSQLITE_DEBUG=1
    -DSQLITE_MEMDEBUG=1
    -DSQLITE_MUTEX_NOOP=1
    -DSQLITE_TCL_DEFAULT_FULLMUTEX=1
    -DSQLITE_ENABLE_FTS3=1
    -DSQLITE_ENABLE_RTREE=1
    -DSQLITE_ENABLE_MEMSYS5=1
    -DSQLITE_ENABLE_COLUMN_METADATA=1
    -DSQLITE_ENABLE_STAT4
    -DSQLITE_ENABLE_HIDDEN_COLUMNS
    -DSQLITE_MAX_ATTACHED=125
    -DSQLITE_MUTATION_TEST
    --enable-fts5
  }
  set build(Debug-Two) {
    -DSQLITE_DEFAULT_MEMSTATUS=0
    -DSQLITE_MAX_EXPR_DEPTH=0
    --enable-debug
  }
  set build(Fast-One) {
    -O6
    -DSQLITE_ENABLE_FTS4=1
    -DSQLITE_ENABLE_RTREE=1
    -DSQLITE_ENABLE_STAT4
    -DSQLITE_ENABLE_RBU
    -DSQLITE_MAX_ATTACHED=125
    -DSQLITE_MAX_MMAP_SIZE=12884901888
    -DSQLITE_ENABLE_SORTER_MMAP=1
    -DLONGDOUBLE_TYPE=double
    --enable-session
  }
  set build(Device-One) {
    -O2
    -DSQLITE_DEBUG=1
    -DSQLITE_DEFAULT_AUTOVACUUM=1
    -DSQLITE_DEFAULT_CACHE_SIZE=64
    -DSQLITE_DEFAULT_PAGE_SIZE=1024
    -DSQLITE_DEFAULT_TEMP_CACHE_SIZE=32
    -DSQLITE_DISABLE_LFS=1
    -DSQLITE_ENABLE_ATOMIC_WRITE=1
    -DSQLITE_ENABLE_IOTRACE=1
    -DSQLITE_ENABLE_MEMORY_MANAGEMENT=1
    -DSQLITE_MAX_PAGE_SIZE=4096
    -DSQLITE_OMIT_LOAD_EXTENSION=1
    -DSQLITE_OMIT_PROGRESS_CALLBACK=1
    -DSQLITE_OMIT_VIRTUALTABLE=1
    -DSQLITE_ENABLE_HIDDEN_COLUMNS
    -DSQLITE_TEMP_STORE=3
  }
  set build(Device-Two) {
    -DSQLITE_4_BYTE_ALIGNED_MALLOC=1
    -DSQLITE_DEFAULT_AUTOVACUUM=1
    -DSQLITE_DEFAULT_CACHE_SIZE=1000
    -DSQLITE_DEFAULT_LOCKING_MODE=0
    -DSQLITE_DEFAULT_PAGE_SIZE=1024
    -DSQLITE_DEFAULT_TEMP_CACHE_SIZE=1000
    -DSQLITE_DISABLE_LFS=1
    -DSQLITE_ENABLE_FTS3=1
    -DSQLITE_ENABLE_MEMORY_MANAGEMENT=1
    -DSQLITE_ENABLE_RTREE=1
    -DSQLITE_MAX_COMPOUND_SELECT=50
    -DSQLITE_MAX_PAGE_SIZE=32768
    -DSQLITE_OMIT_TRACE=1
    -DSQLITE_TEMP_STORE=3
    -DSQLITE_THREADSAFE=2
    --enable-fts5 --enable-session
  }
  set build(Locking-Style) {
    -O2
    -DSQLITE_ENABLE_LOCKING_STYLE=1
  }
  set build(Android) {
    -Os
    -DHAVE_USLEEP=1
    -DSQLITE_HAVE_ISNAN
    -DSQLITE_POWERSAFE_OVERWRITE=1
    -DSQLITE_DEFAULT_FILE_FORMAT=4
    -DSQLITE_DEFAULT_AUTOVACUUM=1
    -DSQLITE_ENABLE_MEMORY_MANAGEMENT=1
    -DSQLITE_ENABLE_FTS3
    -DSQLITE_ENABLE_FTS3_BACKWARDS
    -DSQLITE_ENABLE_FTS4
    -DSQLITE_SECURE_DELETE
    -DSQLITE_ENABLE_BATCH_ATOMIC_WRITE
    -DBIONIC_IOCTL_NO_SIGNEDNESS_OVERLOAD
    -DSQLITE_ALLOW_ROWID_IN_VIEW
    -DSQLITE_ENABLE_BYTECODE_VTAB
    -Wno-unused-parameter
    -Werror
    -DUSE_PREAD64
    -Dfdatasync=fdatasync
    -DHAVE_MALLOC_H=1
    -DHAVE_MALLOC_USABLE_SIZE
    -DSQLITE_ENABLE_DBSTAT_VTAB
  }
  # Compile-options used by Android but omitted from these
  # tests:
  #   -DNDEBUG=1
  #   -DSQLITE_DEFAULT_LEGACY_ALTER_TABLE
  #   -DSQLITE_DEFAULT_JOURNAL_SIZE_LIMIT=1048576
  #   -DSQLITE_DEFAULT_FILE_PERMISSIONS=0600
  #   -DSQLITE_OMIT_BUILTIN_TEST
  #   -DSQLITE_OMIT_LOAD_EXTENSION
  #   -DSQLITE_OMIT_COMPILEOPTION_DIAGS
  #
  set build(Apple) {
    -Os
    -DHAVE_GMTIME_R=1
    -DHAVE_ISNAN=1
    -DHAVE_LOCALTIME_R=1
    -DHAVE_PREAD=1
    -DHAVE_PWRITE=1
    -DHAVE_UTIME=1
    -DSQLITE_DEFAULT_CACHE_SIZE=1000
    -DSQLITE_DEFAULT_CKPTFULLFSYNC=1
    -DSQLITE_DEFAULT_MEMSTATUS=1
    -DSQLITE_DEFAULT_PAGE_SIZE=1024
    -DSQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS=1
    -DSQLITE_ENABLE_API_ARMOR=1
    -DSQLITE_ENABLE_AUTO_PROFILE=1
    -DSQLITE_ENABLE_FLOCKTIMEOUT=1
    -DSQLITE_ENABLE_FTS3=1
    -DSQLITE_ENABLE_FTS3_PARENTHESIS=1
    -DSQLITE_ENABLE_FTS3_TOKENIZER=1
    -DSQLITE_ENABLE_PERSIST_WAL=1
    -DSQLITE_ENABLE_PURGEABLE_PCACHE=1
    -DSQLITE_ENABLE_RTREE=1
    -DSQLITE_ENABLE_SETLK_TIMEOUT=2
    -DSQLITE_ENABLE_SNAPSHOT=1
    -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1
    -DSQLITE_MAX_LENGTH=2147483645
    -DSQLITE_MAX_VARIABLE_NUMBER=500000
    -DSQLITE_NO_SYNC=1
    -DSQLITE_OMIT_AUTORESET=1
    -DSQLITE_OMIT_LOAD_EXTENSION=1
    -DSQLITE_PREFER_PROXY_LOCKING=1
    -DSQLITE_SERIES_CONSTRAINT_VERIFY=1
    -DSQLITE_THREADSAFE=2
    -DSQLITE_USE_URI=1
    -DSQLITE_WRITE_WALFRAME_PREBUFFERED=1
    -DUSE_GUARDED_FD=1
    -DUSE_PREAD=1
    --enable-fts5
  }
  set build(Extra-Robustness) {
    -DSQLITE_ENABLE_OVERSIZE_CELL_CHECK=1
    -DSQLITE_MAX_ATTACHED=62
  }
  set build(Devkit) {
    -DSQLITE_DEFAULT_FILE_FORMAT=4
    -DSQLITE_MAX_ATTACHED=30
    -DSQLITE_ENABLE_COLUMN_METADATA
    -DSQLITE_ENABLE_FTS4
    -DSQLITE_ENABLE_FTS5
    -DSQLITE_ENABLE_FTS4_PARENTHESIS
    -DSQLITE_DISABLE_FTS4_DEFERRED
    -DSQLITE_ENABLE_RTREE
    --enable-fts5
  }
  set build(No-lookaside) {
    -DSQLITE_TEST_REALLOC_STRESS=1
    -DSQLITE_OMIT_LOOKASIDE=1
  }
  set build(Valgrind) {
    -DSQLITE_ENABLE_STAT4
    -DSQLITE_ENABLE_FTS4
    -DSQLITE_ENABLE_RTREE
    -DSQLITE_ENABLE_HIDDEN_COLUMNS
    -DLONGDOUBLE_TYPE=double
    -DCONFIG_SLOWDOWN_FACTOR=8.0
  }

  set build(Windows-Memdebug) {
    MEMDEBUG=1
    DEBUG=3
  }
  set build(Windows-Win32Heap) {
    WIN32HEAP=1
    DEBUG=4
  }
  set build(Windows-Sanitize) {
    ASAN=1
  }

}


#-------------------------------------------------------------------------
proc trd_import {} {
  uplevel {
    variable ::trd::tcltest
    variable ::trd::extra
    variable ::trd::all_configs
    variable ::trd::build
  }
}

proc trd_builds {platform} {
  trd_import

  set klist [lsort -uniq [concat \
      [array names tcltest ${platform}.*] \
      [array names extra ${platform}.*]   \
  ]]
  if {[llength $klist]==0} {
    error "no such platform: $platform"
  }

  set ret ""
  foreach k $klist {
    foreach {p c} [split $k "."] {}
    lappend ret $c
  }
  set ret
}

proc trd_configs {platform bld} {
  trd_import

  set clist [list]

  if {[info exists tcltest($platform.$bld)]} {
    set clist $tcltest($platform.$bld)
    if {$clist=="all"} {
      set clist $all_configs
    } elseif {$clist=="all_plus_autovacuum_crash"} {
      set clist [concat $all_configs autovacuum_crash]
    } elseif {$clist=="all_less_no_mutex_try"} {
      set idx [lsearch $all_configs no_mutex_try]
      set clist [lreplace $all_configs $idx $idx]
    }
  }

  set clist
}

proc trd_extras {platform bld} {
  trd_import
  if {[info exists extra($platform.$bld)]==0} { return [list] }
  return $extra($platform.$bld)
}

# Usage: 
#
#     trd_fuzztest_data
#
# This returns data used by testrunner.tcl to run commands equivalent 
# to [make fuzztest]. The returned value is a list, which should be
# interpreted as a sequence of pairs. The first element of each pair
# is an interpreter name. The second element is a list of files.
# testrunner.tcl automatically creates one job to build each interpreter,
# and one to run each of the files with it once it has been built.
#
# In practice, the returned value looks like this:
#
# {
#   {fuzzcheck {$testdir/fuzzdata1.db $testdir/fuzzdata2.db ...}}
#   {{sessionfuzz run} $testdir/sessionfuzz-data1.db}
# }
#
# where $testdir is replaced by the full-path to the test-directory (the
# directory containing this file). "fuzzcheck" and "sessionfuzz" have .exe
# extensions on windows.
#
proc trd_fuzztest_data {} {
  set EXE ""
  set lFuzzDb    [glob [file join $::testdir fuzzdata*.db]] 
  set lSessionDb [glob [file join $::testdir sessionfuzz-data*.db]]

  if {$::tcl_platform(platform)=="windows"} {
    return [list fuzzcheck.exe $lFuzzDb]
  }

  return [list fuzzcheck $lFuzzDb {sessionfuzz run} $lSessionDb]
}


proc trd_all_configs {} {
  trd_import
  set all_configs
}

proc trimscript {text} {
  set text [string map {"\n    " "\n"} [string trim $text]]
}

proc make_sh_script {srcdir opts cflags makeOpts configOpts} {

  set tcldir [::tcl::pkgconfig get libdir,install]
  set myopts ""
  if {[info exists ::env(OPTS)]} {
    append myopts "# From environment variable:\n"
    append myopts "OPTS=$::env(OPTS)\n\n"
  }
  foreach o [lsort $opts] { 
    append myopts "OPTS=\"\$OPTS $o\"\n"
  }

  return [trimscript [subst -nocommands {
    set -e
    if [ "\$#" -ne 1 ] ; then
      echo "Usage: \$0 <target>"
      exit -1
    fi
    
    SRCDIR="$srcdir"
    TCLDIR="$tcldir"
    
    if [ ! -f Makefile ] ; then
      \$SRCDIR/configure --with-tcl=\$TCL $configOpts 
    fi
    
    $myopts
    CFLAGS="$cflags"
    
    make \$1 "CFLAGS=\$CFLAGS" "OPTS=\$OPTS" $makeOpts
  }]]
}

# Generate the text of a *.bat script.
#
proc make_bat_file {srcdir opts cflags makeOpts} {
  set srcdir [file nativename [file normalize $srcdir]]

  return [trimscript [subst -nocommands {
    set TARGET=%1
    set TMP=%CD%
    nmake /f $srcdir\\Makefile.msc TOP="$srcdir" %TARGET% "CCOPTS=$cflags" "OPTS=$opts" $makeOpts
  }]]
}


# Generate the text of a shell script.
#
proc make_script {cfg srcdir bMsvc} {
  set opts       [list]                         ;# OPTS value
  set cflags     [expr {$bMsvc ? "-Zi" : "-g"}] ;# CFLAGS value
  set makeOpts   [list]                         ;# Extra args for [make]
  set configOpts [list]                         ;# Extra args for [configure]

  # Define either SQLITE_OS_WIN or SQLITE_OS_UNIX, as appropriate.
  if {$::tcl_platform(platform)=="windows"} {
    lappend opts -DSQLITE_OS_WIN=1
  } else {
    lappend opts -DSQLITE_OS_UNIX=1
  }

  # Unless the configuration specifies -DHAVE_USLEEP=0, set -DHAVE_USLEEP=1.
  #
  if {[lsearch $cfg "-DHAVE_USLEEP=0"]<0} {
    lappend cfg -DHAVE_USLEEP=1
  }

  # Loop through the parameters of the nominated configuration, updating
  # $opts, $cflags, $makeOpts and $configOpts along the way. Rules are as
  # follows:
  #
  #   1. If the parameter begins with "-D", add it to $opts.
  #
  #   2. If the parameter begins with "--" add it to $configOpts. Unless
  #      this command is preparing a script for MSVC - then add an 
  #      equivalent to $makeOpts or $opts.
  #
  #   3. If the parameter begins with "-" add it to $cflags. If in MSVC
  #      mode and the parameter is an -O<integer> option, instead add
  #      an OPTIMIZATIONS=<integer> switch to $makeOpts.
  #
  #   4. If none of the above apply, add the parameter to $makeOpts
  #
  foreach param $cfg {

    if {[string range $param 0 1]=="-D"} {
      lappend opts $param
      continue
    }

    if {[string range $param 0 1]=="--"} {
      if {$bMsvc==0} {
        lappend configOpts $param
      } else {

        switch -- $param {
          --disable-amalgamation {
            lappend makeOpts USE_AMALGAMATION=0
          }
          --disable-shared {
            lappend makeOpts USE_CRT_DLL=0 DYNAMIC_SHELL=0
          }
          --enable-fts5 {
            lappend opts -DSQLITE_ENABLE_FTS5
          } 
          --enable-shared {
            lappend makeOpts USE_CRT_DLL=1 DYNAMIC_SHELL=1
          }
          --enable-session {
            lappend opts -DSQLITE_ENABLE_PREUPDATE_HOOK
            lappend opts -DSQLITE_ENABLE_SESSION
          }
          --enable-all {
          }
          --enable-debug {
            # lappend makeOpts OPTIMIZATIONS=0
            lappend opts -DSQLITE_DEBUG
          }
          default {
            error "Cannot translate $param for MSVC"
          }
        }
      }

      continue
    }

    if {[string range $param 0 0]=="-"} {

      if {$bMsvc} {
        if {[regexp -- {^-O(\d+)$} $param -> level]} {
          lappend makeOpts OPTIMIZATIONS=$level
          continue
        }
        if {$param eq "-fsanitize=address,undefined"} {
          lappend makeOpts ASAN=1
          continue
        }
      }

      lappend cflags $param
      continue
    }

    lappend makeOpts $param
  }

  if {$bMsvc==0} {
    set zRet [make_sh_script $srcdir $opts $cflags $makeOpts $configOpts]
  } else {
    set zRet [make_bat_file $srcdir $opts $cflags $makeOpts]
  }
}

# Usage:
#
#    trd_buildscript CONFIG SRCDIR MSVC
#
# This command returns the full text of a script (either a shell script or
# an ms-dos bat file) that may be used to build SQLite source code according
# to a nominated configuration.
#
# Parameter CONFIG must be a configuration defined above in the ::trd::build
# array. SRCDIR is the root directory of an SQLite source tree (the parent
# directory of that containing this script). MSVC is a boolean - true to
# use the MSVC compiler, false otherwise.
#
proc trd_buildscript {config srcdir bMsvc} {
  trd_import

  # Ensure that the named configuration exists.
  if {![info exists build($config)]} {
    if {$config!="help"} {
      puts "No such build config: $config"
    }
    puts "Available configurations: [lsort [array names build]]"
    flush stdout
    exit 1
  }

  # Generate and return the script.
  return [make_script $build($config) $srcdir $bMsvc]
}

# Usage:
#
#    trd_test_script_properties PATH
#
# The argument must be a path to a Tcl test script. This function scans the
# first 100 lines of the script for lines that look like:
#
#    TESTRUNNER: <properties>
#
# where <properties> is a list of identifiers, each of which defines a 
# property of the test script. Example properties are "slow" or "superslow".
#
proc trd_test_script_properties {path} {
  # Use this global array as a cache:
  global trd_test_script_properties_cache

  if {![info exists trd_test_script_properties_cache($path)]} {
    set fd [open $path]
    set ret [list]
    for {set line 0} {$line < 100 && ![eof $fd]} {incr line} {
      set text [gets $fd]
      if {[string match -nocase *testrunner:* $text]} {
        regexp -nocase {.*testrunner:(.*)} $text -> properties
        lappend ret {*}$properties
      }
    }
    set trd_test_script_properties_cache($path) $ret
    close $fd
  }

  set trd_test_script_properties_cache($path)
}