Fossil SCM

fossil-scm / autosetup / autosetup
Blame History Raw 2537 lines
1
#!/bin/sh
2
# Copyright (c) 2006-2011 WorkWare Systems http://www.workware.net.au/
3
# All rights reserved
4
# vim:se syntax=tcl:
5
# \
6
dir=`dirname "$0"`; exec "`$dir/autosetup-find-tclsh`" "$0" "$@"
7
8
# Note that the version has a trailing + on unreleased versions
9
set autosetup(version) 0.7.2
10
11
# Can be set to 1 to debug early-init problems
12
set autosetup(debug) [expr {"--debug" in $argv}]
13
14
##################################################################
15
#
16
# Main flow of control, option handling
17
#
18
proc main {argv} {
19
global autosetup define
20
21
# There are 3 potential directories involved:
22
# 1. The directory containing autosetup (this script)
23
# 2. The directory containing auto.def
24
# 3. The current directory
25
26
# From this we need to determine:
27
# a. The path to this script (and related support files)
28
# b. The path to auto.def
29
# c. The build directory, where output files are created
30
31
# This is also complicated by the fact that autosetup may
32
# have been run via the configure wrapper ([getenv WRAPPER] is set)
33
34
# Here are the rules.
35
# a. This script is $::argv0
36
# => dir, prog, exe, libdir
37
# b. auto.def is in the directory containing the configure wrapper,
38
# otherwise it is in the current directory.
39
# => srcdir, autodef
40
# c. The build directory is the current directory
41
# => builddir, [pwd]
42
43
# 'misc' is needed before we can do anything, so set a temporary libdir
44
# in case this is the development version
45
set autosetup(libdir) [file dirname $::argv0]/lib
46
use misc
47
48
# (a)
49
set autosetup(dir) [realdir [file dirname [realpath $::argv0]]]
50
set autosetup(prog) [file join $autosetup(dir) [file tail $::argv0]]
51
set autosetup(exe) [getenv WRAPPER $autosetup(prog)]
52
if {$autosetup(installed)} {
53
set autosetup(libdir) $autosetup(dir)
54
} else {
55
set autosetup(libdir) [file join $autosetup(dir) lib]
56
}
57
autosetup_add_dep $autosetup(prog)
58
59
# (b)
60
if {[getenv WRAPPER ""] eq ""} {
61
# Invoked directly
62
set autosetup(srcdir) [pwd]
63
} else {
64
# Invoked via the configure wrapper
65
set autosetup(srcdir) [file-normalize [file dirname $autosetup(exe)]]
66
}
67
set autosetup(autodef) [relative-path $autosetup(srcdir)/auto.def]
68
69
# (c)
70
set autosetup(builddir) [pwd]
71
72
set autosetup(argv) $argv
73
set autosetup(cmdline) {}
74
# options is a list of known options
75
set autosetup(options) {}
76
# optset is a dictionary of option values set by the user based on getopt
77
set autosetup(optset) {}
78
# optdefault is a dictionary of default values
79
set autosetup(optdefault) {}
80
# options-defaults is a dictionary of overrides for default values for options
81
set autosetup(options-defaults) {}
82
set autosetup(optionhelp) {}
83
set autosetup(showhelp) 0
84
85
use util
86
87
# Parse options
88
use getopt
89
90
# At the is point we don't know what is a valid option
91
# We simply parse anything that looks like an option
92
set autosetup(getopt) [getopt argv]
93
94
#"=Core Options:"
95
options-add {
96
help:=all => "display help and options. Optional: module name, such as --help=system"
97
licence license => "display the autosetup license"
98
version => "display the version of autosetup"
99
ref:=text manual:=text
100
reference:=text => "display the autosetup command reference. 'text', 'wiki', 'asciidoc' or 'markdown'"
101
debug => "display debugging output as autosetup runs"
102
install:=. => "install autosetup to the current or given directory"
103
}
104
if {$autosetup(installed)} {
105
# hidden options so we can produce a nice error
106
options-add {
107
sysinstall:path
108
}
109
} else {
110
options-add {
111
sysinstall:path => "install standalone autosetup to the given directory (e.g.: /usr/local)"
112
}
113
}
114
options-add {
115
force init:=help => "create initial auto.def, etc. Use --init=help for known types"
116
# Undocumented options
117
option-checking=1
118
nopager
119
quiet
120
timing
121
conf:
122
}
123
124
if {[opt-bool version]} {
125
puts $autosetup(version)
126
exit 0
127
}
128
129
# autosetup --conf=alternate-auto.def
130
if {[opt-str conf o]} {
131
set autosetup(autodef) $o
132
}
133
134
# Debugging output (set this early)
135
incr autosetup(debug) [opt-bool debug]
136
incr autosetup(force) [opt-bool force]
137
incr autosetup(msg-quiet) [opt-bool quiet]
138
incr autosetup(msg-timing) [opt-bool timing]
139
140
# If the local module exists, source it now to allow for
141
# project-local customisations
142
if {[file exists $autosetup(libdir)/local.tcl]} {
143
use local
144
}
145
146
# Now any auto-load modules
147
autosetup_load_auto_modules
148
149
if {[opt-str help o]} {
150
incr autosetup(showhelp)
151
use help
152
autosetup_help $o
153
}
154
155
if {[opt-bool licence license]} {
156
use help
157
autosetup_show_license
158
exit 0
159
}
160
161
if {[opt-str {manual ref reference} o]} {
162
use help
163
autosetup_reference $o
164
}
165
166
# Allow combining --install and --init
167
set earlyexit 0
168
if {[opt-str install o]} {
169
use install
170
autosetup_install $o
171
incr earlyexit
172
}
173
174
if {[opt-str init o]} {
175
use init
176
autosetup_init $o
177
incr earlyexit
178
}
179
180
if {$earlyexit} {
181
exit 0
182
}
183
if {[opt-str sysinstall o]} {
184
use install
185
autosetup_install $o 1
186
exit 0
187
}
188
189
if {![file exists $autosetup(autodef)]} {
190
# Check for invalid option first
191
options {}
192
user-error "No auto.def found in \"$autosetup(srcdir)\" (use [file tail $::autosetup(exe)] --init to create one)"
193
}
194
195
# Parse extra arguments into autosetup(cmdline)
196
foreach arg $argv {
197
if {[regexp {([^=]*)=(.*)} $arg -> n v]} {
198
dict set autosetup(cmdline) $n $v
199
define $n $v
200
} else {
201
user-error "Unexpected parameter: $arg"
202
}
203
}
204
205
autosetup_add_dep $autosetup(autodef)
206
207
# Add $argv to CONFIGURE_OPTS
208
define-append-argv CONFIGURE_OPTS {*}$autosetup(argv)
209
# Set up AUTOREMAKE to reconfigure with the same args
210
define-append-argv AUTOREMAKE {*}$autosetup(exe) {*}$autosetup(argv)
211
212
# Log how we were invoked
213
configlog "Invoked as: [getenv WRAPPER $::argv0] [quote-argv $autosetup(argv)]"
214
configlog "Tclsh: [info nameofexecutable]"
215
216
# Load auto.def as module "auto.def"
217
autosetup_load_module auto.def source $autosetup(autodef)
218
219
# Could warn here if options {} was not specified
220
221
show-notices
222
223
if {$autosetup(debug)} {
224
msg-result "Writing all defines to config.log"
225
configlog "================ defines ======================"
226
foreach n [lsort [array names define]] {
227
configlog "define $n $define($n)"
228
}
229
}
230
231
exit 0
232
}
233
234
# @section Option Handling
235
236
# @opt-bool ?-nodefault? option ...
237
#
238
# Check each of the named, boolean options and if any have been explicitly enabled
239
# or disabled by the user, return 1 or 0 accordingly.
240
#
241
# If the option was specified more than once, the last value wins.
242
# e.g. With '--enable-foo --disable-foo', '[opt-bool foo]' will return 0
243
#
244
# If no value was specified by the user, returns the default value for the
245
# first option. If '-nodefault' is given, this behaviour changes and
246
# -1 is returned instead.
247
#
248
proc opt-bool {args} {
249
set nodefault 0
250
if {[lindex $args 0] eq "-nodefault"} {
251
set nodefault 1
252
set args [lrange $args 1 end]
253
}
254
option-check-names {*}$args
255
256
foreach opt $args {
257
if {[dict exists $::autosetup(optset) $opt]} {
258
return [dict get $::autosetup(optset) $opt]
259
}
260
}
261
262
if {$nodefault} {
263
return -1
264
}
265
# Default value is the default for the first option
266
return [dict get $::autosetup(optdefault) [lindex $args 0]]
267
}
268
269
# @opt-val optionlist ?default=""?
270
#
271
# Returns a list containing all the values given for the non-boolean options in '$optionlist'.
272
# There will be one entry in the list for each option given by the user, including if the
273
# same option was used multiple times.
274
#
275
# If no options were set, '$default' is returned (exactly, not as a list).
276
#
277
# Note: For most use cases, 'opt-str' should be preferred.
278
#
279
proc opt-val {names {default ""}} {
280
option-check-names {*}$names
281
282
foreach opt $names {
283
if {[dict exists $::autosetup(optset) $opt]} {
284
lappend result {*}[dict get $::autosetup(optset) $opt]
285
}
286
}
287
if {[info exists result]} {
288
return $result
289
}
290
return $default
291
}
292
293
# @opt-str optionlist varname ?default?
294
#
295
# Sets '$varname' in the callers scope to the value for one of the given options.
296
#
297
# For the list of options given in '$optionlist', if any value is set for any option,
298
# the option value is taken to be the *last* value of the last option (in the order given).
299
#
300
# If no option was given, and a default was specified with 'options-defaults',
301
# that value is used.
302
#
303
# If no 'options-defaults' value was given and '$default' was given, it is used.
304
#
305
# If none of the above provided a value, no value is set.
306
#
307
# The return value depends on whether '$default' was specified.
308
# If it was, the option value is returned.
309
# If it was not, 1 is returns if a value was set, or 0 if not.
310
#
311
# Typical usage is as follows:
312
#
313
## if {[opt-str {myopt altname} o]} {
314
## do something with $o
315
## }
316
#
317
# Or:
318
## define myname [opt-str {myopt altname} o "/usr/local"]
319
#
320
proc opt-str {names varname args} {
321
global autosetup
322
323
option-check-names {*}$names
324
upvar $varname value
325
326
if {[llength $args]} {
327
# A default was given, so always return the string value of the option
328
set default [lindex $args 0]
329
set retopt 1
330
} else {
331
# No default, so return 0 or 1 to indicate if a value was found
332
set retopt 0
333
}
334
335
foreach opt $names {
336
if {[dict exists $::autosetup(optset) $opt]} {
337
set result [lindex [dict get $::autosetup(optset) $opt] end]
338
}
339
}
340
341
if {![info exists result]} {
342
# No user-specified value. Has options-defaults been set?
343
foreach opt $names {
344
if {[dict exists $::autosetup(optdefault) $opt]} {
345
set result [dict get $autosetup(optdefault) $opt]
346
}
347
}
348
}
349
350
if {[info exists result]} {
351
set value $result
352
if {$retopt} {
353
return $value
354
}
355
return 1
356
}
357
358
if {$retopt} {
359
set value $default
360
return $value
361
}
362
363
return 0
364
}
365
366
proc option-check-names {args} {
367
foreach o $args {
368
if {$o ni $::autosetup(options)} {
369
autosetup-error "Request for undeclared option --$o"
370
}
371
}
372
}
373
374
# Parse the option definition in $opts and update
375
# ::autosetup(setoptions) and ::autosetup(optionhelp) appropriately
376
#
377
proc options-add {opts} {
378
global autosetup
379
380
# First weed out comment lines
381
set realopts {}
382
foreach line [split $opts \n] {
383
if {![string match "#*" [string trimleft $line]]} {
384
append realopts $line \n
385
}
386
}
387
set opts $realopts
388
389
for {set i 0} {$i < [llength $opts]} {incr i} {
390
set opt [lindex $opts $i]
391
if {[string match =* $opt]} {
392
# This is a special heading
393
lappend autosetup(optionhelp) [list $opt $autosetup(module)]
394
continue
395
}
396
unset -nocomplain defaultvalue equal value
397
398
#puts "i=$i, opt=$opt"
399
regexp {^([^:=]*)(:)?(=)?(.*)$} $opt -> name colon equal value
400
if {$name in $autosetup(options)} {
401
autosetup-error "Option $name already specified"
402
}
403
404
#puts "$opt => $name $colon $equal $value"
405
406
# Find the corresponding value in the user options
407
# and set the default if necessary
408
if {[string match "-*" $opt]} {
409
# This is a documentation-only option, like "-C <dir>"
410
set opthelp $opt
411
} elseif {$colon eq ""} {
412
# Boolean option
413
lappend autosetup(options) $name
414
415
# Check for override
416
if {[dict exists $autosetup(options-defaults) $name]} {
417
# A default was specified with options-defaults, so use it
418
set value [dict get $autosetup(options-defaults) $name]
419
}
420
421
if {$value eq "1"} {
422
set opthelp "--disable-$name"
423
} else {
424
set opthelp "--$name"
425
}
426
427
# Set the default
428
if {$value eq ""} {
429
set value 0
430
}
431
set defaultvalue $value
432
dict set autosetup(optdefault) $name $defaultvalue
433
434
if {[dict exists $autosetup(getopt) $name]} {
435
# The option was specified by the user. Look at the last value.
436
lassign [lindex [dict get $autosetup(getopt) $name] end] type setvalue
437
if {$type eq "str"} {
438
# Can we convert the value to a boolean?
439
if {$setvalue in {1 enabled yes}} {
440
set setvalue 1
441
} elseif {$setvalue in {0 disabled no}} {
442
set setvalue 0
443
} else {
444
user-error "Boolean option $name given as --$name=$setvalue"
445
}
446
}
447
dict set autosetup(optset) $name $setvalue
448
#puts "Found boolean option --$name=$setvalue"
449
}
450
} else {
451
# String option.
452
lappend autosetup(options) $name
453
454
if {$equal ne "="} {
455
# Was the option given as "name:value=default"?
456
# If so, set $value to the display name and $defaultvalue to the default
457
# (This is the preferred way to set a default value for a string option)
458
if {[regexp {^([^=]+)=(.*)$} $value -> value defaultvalue]} {
459
dict set autosetup(optdefault) $name $defaultvalue
460
}
461
}
462
463
# Maybe override the default value
464
if {[dict exists $autosetup(options-defaults) $name]} {
465
# A default was specified with options-defaults, so use it
466
set defaultvalue [dict get $autosetup(options-defaults) $name]
467
dict set autosetup(optdefault) $name $defaultvalue
468
} elseif {![info exists defaultvalue]} {
469
# No default value was given by value=default or options-defaults
470
# so use the value as the default when the plain option with no
471
# value is given (.e.g. just --opt instead of --opt=value)
472
set defaultvalue $value
473
}
474
475
if {$equal eq "="} {
476
# String option with optional value
477
set opthelp "--$name?=$value?"
478
} else {
479
# String option with required value
480
set opthelp "--$name=$value"
481
}
482
483
# Get the values specified by the user
484
if {[dict exists $autosetup(getopt) $name]} {
485
set listvalue {}
486
487
foreach pair [dict get $autosetup(getopt) $name] {
488
lassign $pair type setvalue
489
if {$type eq "bool" && $setvalue} {
490
if {$equal ne "="} {
491
user-error "Option --$name requires a value"
492
}
493
# If given as a boolean, use the default value
494
set setvalue $defaultvalue
495
}
496
lappend listvalue $setvalue
497
}
498
499
#puts "Found string option --$name=$listvalue"
500
dict set autosetup(optset) $name $listvalue
501
}
502
}
503
504
# Now create the help for this option if appropriate
505
if {[lindex $opts $i+1] eq "=>"} {
506
set desc [lindex $opts $i+2]
507
if {[info exists defaultvalue]} {
508
set desc [string map [list @default@ $defaultvalue] $desc]
509
}
510
# A multi-line description
511
lappend autosetup(optionhelp) [list $opthelp $autosetup(module) $desc]
512
incr i 2
513
}
514
}
515
}
516
517
# @module-options optionlist
518
#
519
# Deprecated. Simply use 'options' from within a module.
520
proc module-options {opts} {
521
options $opts
522
}
523
524
proc max {a b} {
525
expr {$a > $b ? $a : $b}
526
}
527
528
proc options-wrap-desc {text length firstprefix nextprefix initial} {
529
set len $initial
530
set space $firstprefix
531
foreach word [split $text] {
532
set word [string trim $word]
533
if {$word == ""} {
534
continue
535
}
536
if {$len && [string length $space$word] + $len >= $length} {
537
puts ""
538
set len 0
539
set space $nextprefix
540
}
541
incr len [string length $space$word]
542
puts -nonewline $space$word
543
set space " "
544
}
545
if {$len} {
546
puts ""
547
}
548
}
549
550
# Display options (from $autosetup(optionhelp)) for modules that match
551
# glob pattern $what
552
proc options-show {what} {
553
set local 0
554
# Determine the max option width
555
set max 0
556
foreach help $::autosetup(optionhelp) {
557
lassign $help opt module desc
558
if {![string match $what $module]} {
559
continue
560
}
561
if {[string match =* $opt] || [string match \n* $desc]} {
562
continue
563
}
564
set max [max $max [string length $opt]]
565
}
566
set indent [string repeat " " [expr {$max+4}]]
567
set cols [getenv COLUMNS 80]
568
catch {
569
lassign [exec stty size] _ sttycols
570
if {[string is integer -strict $sttycols]} {
571
set cols $sttycols
572
}
573
}
574
incr cols -1
575
# Now output
576
foreach help $::autosetup(optionhelp) {
577
lassign $help opt module desc
578
if {![string match $what $module]} {
579
continue
580
}
581
if {$local == 0 && $module eq "auto.def"} {
582
puts "Local Options:"
583
incr local
584
}
585
if {[string match =* $opt]} {
586
# Output a special heading line"
587
puts [string range $opt 1 end]
588
continue
589
}
590
puts -nonewline " [format %-${max}s $opt]"
591
if {[string match \n* $desc]} {
592
# Output a pre-formatted help description as-is
593
puts $desc
594
} else {
595
options-wrap-desc [string trim $desc] $cols " " $indent [expr {$max+2}]
596
}
597
}
598
}
599
600
# @options optionspec
601
#
602
# Specifies configuration-time options which may be selected by the user
603
# and checked with 'opt-str' and 'opt-bool'. '$optionspec' contains a series
604
# of options specifications separated by newlines, as follows:
605
#
606
# A boolean option is of the form:
607
#
608
## name[=0|1] => "Description of this boolean option"
609
#
610
# The default is 'name=0', meaning that the option is disabled by default.
611
# If 'name=1' is used to make the option enabled by default, the description should reflect
612
# that with text like "Disable support for ...".
613
#
614
# An argument option (one which takes a parameter) is of one of the following forms:
615
#
616
## name:value => "Description of this option"
617
## name:value=default => "Description of this option with a default value"
618
## name:=value => "Description of this option with an optional value"
619
#
620
# If the 'name:value' form is used, the value must be provided with the option (as '--name=myvalue').
621
# If the 'name:value=default' form is used, the option has the given default value even if not
622
# specified by the user.
623
# If the 'name:=value' form is used, the value is optional and the given value is used
624
# if it is not provided.
625
#
626
# The description may contain '@default@', in which case it will be replaced with the default
627
# value for the option (taking into account defaults specified with 'options-defaults'.
628
#
629
# Undocumented options are also supported by omitting the '=> description'.
630
# These options are not displayed with '--help' and can be useful for internal options or as aliases.
631
#
632
# For example, '--disable-lfs' is an alias for '--disable=largefile':
633
#
634
## lfs=1 largefile=1 => "Disable large file support"
635
#
636
proc options {optlist} {
637
global autosetup
638
639
options-add $optlist
640
641
if {$autosetup(showhelp)} {
642
# If --help, stop now to show help
643
return -code break
644
}
645
646
if {$autosetup(module) eq "auto.def"} {
647
# Check for invalid options
648
if {[opt-bool option-checking]} {
649
foreach o [dict keys $::autosetup(getopt)] {
650
if {$o ni $::autosetup(options)} {
651
user-error "Unknown option --$o"
652
}
653
}
654
}
655
}
656
}
657
658
# @options-defaults dictionary
659
#
660
# Specifies a dictionary of options and a new default value for each of those options.
661
# Use before any 'use' statements in 'auto.def' to change the defaults for
662
# subsequently included modules.
663
proc options-defaults {dict} {
664
foreach {n v} $dict {
665
dict set ::autosetup(options-defaults) $n $v
666
}
667
}
668
669
proc config_guess {} {
670
if {[file-isexec $::autosetup(dir)/autosetup-config.guess]} {
671
if {[catch {exec-with-stderr sh $::autosetup(dir)/autosetup-config.guess} alias]} {
672
user-error $alias
673
}
674
return $alias
675
} else {
676
configlog "No autosetup-config.guess, so using uname"
677
string tolower [exec uname -p]-unknown-[exec uname -s][exec uname -r]
678
}
679
}
680
681
proc config_sub {alias} {
682
if {[file-isexec $::autosetup(dir)/autosetup-config.sub]} {
683
if {[catch {exec-with-stderr sh $::autosetup(dir)/autosetup-config.sub $alias} alias]} {
684
user-error $alias
685
}
686
}
687
return $alias
688
}
689
690
# @section Variable Definitions (defines)
691
692
# @define name ?value=1?
693
#
694
# Defines the named variable to the given value.
695
# These (name, value) pairs represent the results of the configuration check
696
# and are available to be subsequently checked, modified and substituted.
697
#
698
proc define {name {value 1}} {
699
set ::define($name) $value
700
#dputs "$name <= $value"
701
}
702
703
# @define-push {name ...} script
704
#
705
# Save the values of the given defines, evaluation the script, then restore.
706
# For example, to avoid updating AS_FLAGS and AS_CXXFLAGS:
707
## define-push {AS_CFLAGS AS_CXXFLAGS} {
708
## cc-check-flags -Wno-error
709
## }
710
proc define-push {names script} {
711
array set unset {}
712
foreach name $names {
713
if {[is-defined $name]} {
714
set save($name) [get-define $name]
715
} else {
716
set unset($name) 1
717
}
718
}
719
uplevel 1 $script
720
array set ::define [array get save]
721
foreach name [array names unset] {
722
unset -nocomplain ::define($name)
723
}
724
}
725
726
# @undefine name
727
#
728
# Undefine the named variable.
729
#
730
proc undefine {name} {
731
unset -nocomplain ::define($name)
732
#dputs "$name <= <undef>"
733
}
734
735
# @define-append name value ...
736
#
737
# Appends the given value(s) to the given "defined" variable.
738
# If the variable is not defined or empty, it is set to '$value'.
739
# Otherwise the value is appended, separated by a space.
740
# Any extra values are similarly appended.
741
#
742
# Note that define-append is not designed to add values containing spaces.
743
# If values may contain spaces, consider define-append-argv instead.
744
#
745
proc define-append {name args} {
746
if {[get-define $name ""] ne ""} {
747
foreach arg $args {
748
if {$arg eq ""} {
749
continue
750
}
751
append ::define($name) " " $arg
752
}
753
} else {
754
set ::define($name) [join $args]
755
}
756
#dputs "$name += [join $args] => $::define($name)"
757
}
758
759
# @define-append-argv name value ...
760
#
761
# Similar to define-append except designed to construct shell command
762
# lines, including correct handling of parameters with spaces.
763
#
764
# Each non-empty value is quoted if necessary and then appended to the given variable
765
# if it does not already exist.
766
#
767
proc define-append-argv {name args} {
768
set seen {}
769
set new {}
770
foreach val [list {*}[get-define $name ""] {*}$args] {
771
if {$val ne {} && ![dict exists $seen $val]} {
772
lappend new [quote-if-needed $val]
773
dict set seen $val 1
774
}
775
}
776
set ::define($name) [join $new " "]
777
#dputs "$name += [join $args] => $::define($name)"
778
}
779
780
# @get-define name ?default=0?
781
#
782
# Returns the current value of the "defined" variable, or '$default'
783
# if not set.
784
#
785
proc get-define {name {default 0}} {
786
if {[info exists ::define($name)]} {
787
#dputs "$name => $::define($name)"
788
return $::define($name)
789
}
790
#dputs "$name => $default"
791
return $default
792
}
793
794
# @is-defined name
795
#
796
# Returns 1 if the given variable is defined.
797
#
798
proc is-defined {name} {
799
info exists ::define($name)
800
}
801
802
# @is-define-set name
803
#
804
# Returns 1 if the given variable is defined and is set
805
# to a value other than "" or 0
806
#
807
proc is-define-set {name} {
808
if {[get-define $name] in {0 ""}} {
809
return 0
810
}
811
return 1
812
}
813
814
# @all-defines
815
#
816
# Returns a dictionary (name, value list) of all defined variables.
817
#
818
# This is suitable for use with 'dict', 'array set' or 'foreach'
819
# and allows for arbitrary processing of the defined variables.
820
#
821
proc all-defines {} {
822
array get ::define
823
}
824
825
# @section Environment/Helpers
826
827
# @get-env name default
828
#
829
# If '$name' was specified on the command line, return it.
830
# Otherwise if '$name' was set in the environment, return it.
831
# Otherwise return '$default'.
832
#
833
proc get-env {name default} {
834
if {[dict exists $::autosetup(cmdline) $name]} {
835
return [dict get $::autosetup(cmdline) $name]
836
}
837
getenv $name $default
838
}
839
840
# @env-is-set name
841
#
842
# Returns 1 if '$name' was specified on the command line or in the environment.
843
# Note that an empty environment variable is not considered to be set.
844
#
845
proc env-is-set {name} {
846
if {[dict exists $::autosetup(cmdline) $name]} {
847
return 1
848
}
849
if {[getenv $name ""] ne ""} {
850
return 1
851
}
852
return 0
853
}
854
855
# @readfile filename ?default=""?
856
#
857
# Return the contents of the file, without the trailing newline.
858
# If the file doesn't exist or can't be read, returns '$default'.
859
#
860
proc readfile {filename {default_value ""}} {
861
set result $default_value
862
catch {
863
set f [open $filename]
864
set result [read -nonewline $f]
865
close $f
866
}
867
return $result
868
}
869
870
# @writefile filename value
871
#
872
# Creates the given file containing '$value'.
873
# Does not add an extra newline.
874
#
875
proc writefile {filename value} {
876
set f [open $filename w]
877
puts -nonewline $f $value
878
close $f
879
}
880
881
proc quote-if-needed {str} {
882
if {[string match {*[\" ]*} $str]} {
883
return \"[string map [list \" \\" \\ \\\\] $str]\"
884
}
885
return $str
886
}
887
888
proc quote-argv {argv} {
889
set args {}
890
foreach arg $argv {
891
lappend args [quote-if-needed $arg]
892
}
893
join $args
894
}
895
896
# @list-non-empty list
897
#
898
# Returns a copy of the given list with empty elements removed
899
proc list-non-empty {list} {
900
set result {}
901
foreach p $list {
902
if {$p ne ""} {
903
lappend result $p
904
}
905
}
906
return $result
907
}
908
909
# @section Paths, Searching
910
911
# @find-executable-path name
912
#
913
# Searches the path for an executable with the given name.
914
# Note that the name may include some parameters, e.g. 'cc -mbig-endian',
915
# in which case the parameters are ignored.
916
# Returns the full path to the executable if found, or "" if not found.
917
#
918
proc find-executable-path {name} {
919
# Ignore any parameters
920
set name [lindex $name 0]
921
# The empty string is never a valid executable
922
if {$name ne ""} {
923
foreach p [split-path] {
924
dputs "Looking for $name in $p"
925
set exec [file join $p $name]
926
if {[file-isexec $exec]} {
927
dputs "Found $name -> $exec"
928
return $exec
929
}
930
}
931
}
932
return {}
933
}
934
935
# @find-executable name
936
#
937
# Searches the path for an executable with the given name.
938
# Note that the name may include some parameters, e.g. 'cc -mbig-endian',
939
# in which case the parameters are ignored.
940
# Returns 1 if found, or 0 if not.
941
#
942
proc find-executable {name} {
943
if {[find-executable-path $name] eq {}} {
944
return 0
945
}
946
return 1
947
}
948
949
# @find-an-executable ?-required? name ...
950
#
951
# Given a list of possible executable names,
952
# searches for one of these on the path.
953
#
954
# Returns the name found, or "" if none found.
955
# If the first parameter is '-required', an error is generated
956
# if no executable is found.
957
#
958
proc find-an-executable {args} {
959
set required 0
960
if {[lindex $args 0] eq "-required"} {
961
set args [lrange $args 1 end]
962
incr required
963
}
964
foreach name $args {
965
if {[find-executable $name]} {
966
return $name
967
}
968
}
969
if {$required} {
970
if {[llength $args] == 1} {
971
user-error "failed to find: [join $args]"
972
} else {
973
user-error "failed to find one of: [join $args]"
974
}
975
}
976
return ""
977
}
978
979
# @section Logging, Messages and Errors
980
981
# @configlog msg
982
#
983
# Writes the given message to the configuration log, 'config.log'.
984
#
985
proc configlog {msg} {
986
if {![info exists ::autosetup(logfh)]} {
987
set ::autosetup(logfh) [open config.log w]
988
}
989
puts $::autosetup(logfh) $msg
990
}
991
992
# @msg-checking msg
993
#
994
# Writes the message with no newline to stdout.
995
#
996
proc msg-checking {msg} {
997
if {$::autosetup(msg-quiet) == 0} {
998
maybe-show-timestamp
999
puts -nonewline $msg
1000
set ::autosetup(msg-checking) 1
1001
}
1002
}
1003
1004
# @msg-result msg
1005
#
1006
# Writes the message to stdout.
1007
#
1008
proc msg-result {msg} {
1009
if {$::autosetup(msg-quiet) == 0} {
1010
maybe-show-timestamp
1011
puts $msg
1012
set ::autosetup(msg-checking) 0
1013
show-notices
1014
}
1015
}
1016
1017
# @msg-quiet command ...
1018
#
1019
# 'msg-quiet' evaluates it's arguments as a command with output
1020
# from 'msg-checking' and 'msg-result' suppressed.
1021
#
1022
# This is useful if a check needs to run a subcheck which isn't
1023
# of interest to the user.
1024
proc msg-quiet {args} {
1025
incr ::autosetup(msg-quiet)
1026
set rc [uplevel 1 $args]
1027
incr ::autosetup(msg-quiet) -1
1028
return $rc
1029
}
1030
1031
# Will be overridden by 'use misc'
1032
proc error-stacktrace {msg} {
1033
return $msg
1034
}
1035
1036
proc error-location {msg} {
1037
return $msg
1038
}
1039
1040
##################################################################
1041
#
1042
# Debugging output
1043
#
1044
proc dputs {msg} {
1045
if {$::autosetup(debug)} {
1046
puts $msg
1047
}
1048
}
1049
1050
##################################################################
1051
#
1052
# User and system warnings and errors
1053
#
1054
# Usage errors such as wrong command line options
1055
1056
# @user-error msg
1057
#
1058
# Indicate incorrect usage to the user, including if required components
1059
# or features are not found.
1060
# 'autosetup' exits with a non-zero return code.
1061
#
1062
proc user-error {msg} {
1063
show-notices
1064
puts stderr "Error: $msg"
1065
puts stderr "Try: '[file tail $::autosetup(exe)] --help' for options"
1066
exit 1
1067
}
1068
1069
# @user-notice msg
1070
#
1071
# Output the given message to stderr.
1072
#
1073
proc user-notice {msg} {
1074
lappend ::autosetup(notices) $msg
1075
}
1076
1077
# Incorrect usage in the auto.def file. Identify the location.
1078
proc autosetup-error {msg} {
1079
autosetup-full-error [error-location $msg]
1080
}
1081
1082
# Like autosetup-error, except $msg is the full error message.
1083
proc autosetup-full-error {msg} {
1084
show-notices
1085
puts stderr $msg
1086
exit 1
1087
}
1088
1089
proc show-notices {} {
1090
if {$::autosetup(msg-checking)} {
1091
puts ""
1092
set ::autosetup(msg-checking) 0
1093
}
1094
flush stdout
1095
if {[info exists ::autosetup(notices)]} {
1096
puts stderr [join $::autosetup(notices) \n]
1097
unset ::autosetup(notices)
1098
}
1099
}
1100
1101
proc maybe-show-timestamp {} {
1102
if {$::autosetup(msg-timing) && $::autosetup(msg-checking) == 0} {
1103
puts -nonewline [format {[%6.2f] } [expr {([clock millis] - $::autosetup(start)) % 10000 / 1000.0}]]
1104
}
1105
}
1106
1107
# @autosetup-require-version required
1108
#
1109
# Checks the current version of 'autosetup' against '$required'.
1110
# A fatal error is generated if the current version is less than that required.
1111
#
1112
proc autosetup-require-version {required} {
1113
if {[compare-versions $::autosetup(version) $required] < 0} {
1114
user-error "autosetup version $required is required, but this is $::autosetup(version)"
1115
}
1116
}
1117
1118
proc autosetup_version {} {
1119
return "autosetup v$::autosetup(version)"
1120
}
1121
1122
##################################################################
1123
#
1124
# Directory/path handling
1125
#
1126
1127
proc realdir {dir} {
1128
set oldpwd [pwd]
1129
cd $dir
1130
set pwd [pwd]
1131
cd $oldpwd
1132
return $pwd
1133
}
1134
1135
# Follow symlinks until we get to something which is not a symlink
1136
proc realpath {path} {
1137
while {1} {
1138
if {[catch {
1139
set path [file readlink $path]
1140
}]} {
1141
# Not a link
1142
break
1143
}
1144
}
1145
return $path
1146
}
1147
1148
# Convert absolute path, $path into a path relative
1149
# to the given directory (or the current dir, if not given).
1150
#
1151
proc relative-path {path {pwd {}}} {
1152
set diff 0
1153
set same 0
1154
set newf {}
1155
set prefix {}
1156
set path [file-normalize $path]
1157
if {$pwd eq ""} {
1158
set pwd [pwd]
1159
} else {
1160
set pwd [file-normalize $pwd]
1161
}
1162
1163
if {$path eq $pwd} {
1164
return .
1165
}
1166
1167
# Try to make the filename relative to the current dir
1168
foreach p [split $pwd /] f [split $path /] {
1169
if {$p ne $f} {
1170
incr diff
1171
} elseif {!$diff} {
1172
incr same
1173
}
1174
if {$diff} {
1175
if {$p ne ""} {
1176
# Add .. for sibling or parent dir
1177
lappend prefix ..
1178
}
1179
if {$f ne ""} {
1180
lappend newf $f
1181
}
1182
}
1183
}
1184
if {$same == 1 || [llength $prefix] > 3} {
1185
return $path
1186
}
1187
1188
file join [join $prefix /] [join $newf /]
1189
}
1190
1191
# Add filename as a dependency to rerun autosetup
1192
# The name will be normalised (converted to a full path)
1193
#
1194
proc autosetup_add_dep {filename} {
1195
lappend ::autosetup(deps) [file-normalize $filename]
1196
}
1197
1198
# @section Modules Support
1199
1200
##################################################################
1201
#
1202
# Library module support
1203
#
1204
1205
# @use module ...
1206
#
1207
# Load the given library modules.
1208
# e.g. 'use cc cc-shared'
1209
#
1210
# Note that module 'X' is implemented in either 'autosetup/X.tcl'
1211
# or 'autosetup/X/init.tcl'
1212
#
1213
# The latter form is useful for a complex module which requires additional
1214
# support file. In this form, '$::usedir' is set to the module directory
1215
# when it is loaded.
1216
#
1217
proc use {args} {
1218
global autosetup libmodule modsource
1219
1220
set dirs [list $autosetup(libdir)]
1221
if {[info exists autosetup(srcdir)]} {
1222
lappend dirs $autosetup(srcdir)/autosetup
1223
}
1224
foreach m $args {
1225
if {[info exists libmodule($m)]} {
1226
continue
1227
}
1228
set libmodule($m) 1
1229
1230
if {[info exists modsource(${m}.tcl)]} {
1231
autosetup_load_module $m eval $modsource(${m}.tcl)
1232
} else {
1233
set locs [list ${m}.tcl ${m}/init.tcl]
1234
set found 0
1235
foreach dir $dirs {
1236
foreach loc $locs {
1237
set source $dir/$loc
1238
if {[file exists $source]} {
1239
incr found
1240
break
1241
}
1242
}
1243
if {$found} {
1244
break
1245
}
1246
}
1247
if {$found} {
1248
# For the convenience of the "use" source, point to the directory
1249
# it is being loaded from
1250
set ::usedir [file dirname $source]
1251
autosetup_load_module $m source $source
1252
autosetup_add_dep $source
1253
} else {
1254
autosetup-error "use: No such module: $m"
1255
}
1256
}
1257
}
1258
}
1259
1260
proc autosetup_load_auto_modules {} {
1261
global autosetup modsource
1262
# First load any embedded auto modules
1263
foreach mod [array names modsource *.auto] {
1264
autosetup_load_module $mod eval $modsource($mod)
1265
}
1266
# Now any external auto modules
1267
foreach file [glob -nocomplain $autosetup(libdir)/*.auto $autosetup(libdir)/*/*.auto] {
1268
autosetup_load_module [file tail $file] source $file
1269
}
1270
}
1271
1272
# Load module source in the global scope by executing the given command
1273
proc autosetup_load_module {module args} {
1274
global autosetup
1275
set prev $autosetup(module)
1276
set autosetup(module) $module
1277
1278
if {[catch [list uplevel #0 $args] msg opts] ni {0 2 3}} {
1279
autosetup-full-error [error-dump $msg $opts $::autosetup(debug)]
1280
}
1281
set autosetup(module) $prev
1282
}
1283
1284
# Initial settings
1285
set autosetup(exe) $::argv0
1286
set autosetup(istcl) 1
1287
set autosetup(start) [clock millis]
1288
set autosetup(installed) 0
1289
set autosetup(sysinstall) 0
1290
set autosetup(msg-checking) 0
1291
set autosetup(msg-quiet) 0
1292
set autosetup(inittypes) {}
1293
set autosetup(module) autosetup
1294
1295
# Embedded modules are inserted below here
1296
set autosetup(installed) 1
1297
set autosetup(sysinstall) 0
1298
# ----- @module asciidoc-formatting.tcl -----
1299
1300
set modsource(asciidoc-formatting.tcl) {
1301
# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/
1302
# All rights reserved
1303
1304
# Module which provides text formatting
1305
# asciidoc format
1306
1307
use formatting
1308
1309
proc para {text} {
1310
regsub -all "\[ \t\n\]+" [string trim $text] " "
1311
}
1312
proc title {text} {
1313
underline [para $text] =
1314
nl
1315
}
1316
proc p {text} {
1317
puts [para $text]
1318
nl
1319
}
1320
proc code {text} {
1321
foreach line [parse_code_block $text] {
1322
puts " $line"
1323
}
1324
nl
1325
}
1326
proc codelines {lines} {
1327
foreach line $lines {
1328
puts " $line"
1329
}
1330
nl
1331
}
1332
proc nl {} {
1333
puts ""
1334
}
1335
proc underline {text char} {
1336
regexp "^(\[ \t\]*)(.*)" $text -> indent words
1337
puts $text
1338
puts $indent[string repeat $char [string length $words]]
1339
}
1340
proc section {text} {
1341
underline "[para $text]" -
1342
nl
1343
}
1344
proc subsection {text} {
1345
underline "$text" ~
1346
nl
1347
}
1348
proc bullet {text} {
1349
puts "* [para $text]"
1350
}
1351
proc indent {text} {
1352
puts " :: "
1353
puts [para $text]
1354
}
1355
proc defn {first args} {
1356
set sep ""
1357
if {$first ne ""} {
1358
puts "${first}::"
1359
} else {
1360
puts " :: "
1361
}
1362
set defn [string trim [join $args \n]]
1363
regsub -all "\n\n" $defn "\n ::\n" defn
1364
puts $defn
1365
}
1366
}
1367
1368
# ----- @module formatting.tcl -----
1369
1370
set modsource(formatting.tcl) {
1371
# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/
1372
# All rights reserved
1373
1374
# Module which provides common text formatting
1375
1376
# This is designed for documentation which looks like:
1377
# code {...}
1378
# or
1379
# code {
1380
# ...
1381
# ...
1382
# }
1383
# In the second case, we need to work out the indenting
1384
# and strip it from all lines but preserve the remaining indenting.
1385
# Note that all lines need to be indented with the same initial
1386
# spaces/tabs.
1387
#
1388
# Returns a list of lines with the indenting removed.
1389
#
1390
proc parse_code_block {text} {
1391
# If the text begins with newline, take the following text,
1392
# otherwise just return the original
1393
if {![regexp "^\n(.*)" $text -> text]} {
1394
return [list [string trim $text]]
1395
}
1396
1397
# And trip spaces off the end
1398
set text [string trimright $text]
1399
1400
set min 100
1401
# Examine each line to determine the minimum indent
1402
foreach line [split $text \n] {
1403
if {$line eq ""} {
1404
# Ignore empty lines for the indent calculation
1405
continue
1406
}
1407
regexp "^(\[ \t\]*)" $line -> indent
1408
set len [string length $indent]
1409
if {$len < $min} {
1410
set min $len
1411
}
1412
}
1413
1414
# Now make a list of lines with this indent removed
1415
set lines {}
1416
foreach line [split $text \n] {
1417
lappend lines [string range $line $min end]
1418
}
1419
1420
# Return the result
1421
return $lines
1422
}
1423
}
1424
1425
# ----- @module getopt.tcl -----
1426
1427
set modsource(getopt.tcl) {
1428
# Copyright (c) 2006 WorkWare Systems http://www.workware.net.au/
1429
# All rights reserved
1430
1431
# Simple getopt module
1432
1433
# Parse everything out of the argv list which looks like an option
1434
# Everything which doesn't look like an option, or is after --, is left unchanged
1435
# Understands --enable-xxx as a synonym for --xxx to enable the boolean option xxx.
1436
# Understands --disable-xxx to disable the boolean option xxx.
1437
#
1438
# The returned value is a dictionary keyed by option name
1439
# Each value is a list of {type value} ... where type is "bool" or "str".
1440
# The value for a boolean option is 0 or 1. The value of a string option is the value given.
1441
proc getopt {argvname} {
1442
upvar $argvname argv
1443
set nargv {}
1444
1445
set opts {}
1446
1447
for {set i 0} {$i < [llength $argv]} {incr i} {
1448
set arg [lindex $argv $i]
1449
1450
#dputs arg=$arg
1451
1452
if {$arg eq "--"} {
1453
# End of options
1454
incr i
1455
lappend nargv {*}[lrange $argv $i end]
1456
break
1457
}
1458
1459
if {[regexp {^--([^=][^=]+)=(.*)$} $arg -> name value]} {
1460
# --name=value
1461
dict lappend opts $name [list str $value]
1462
} elseif {[regexp {^--(enable-|disable-)?([^=]*)$} $arg -> prefix name]} {
1463
if {$prefix in {enable- ""}} {
1464
set value 1
1465
} else {
1466
set value 0
1467
}
1468
dict lappend opts $name [list bool $value]
1469
} else {
1470
lappend nargv $arg
1471
}
1472
}
1473
1474
#puts "getopt: argv=[join $argv] => [join $nargv]"
1475
#array set getopt $opts
1476
#parray getopt
1477
1478
set argv $nargv
1479
1480
return $opts
1481
}
1482
}
1483
1484
# ----- @module help.tcl -----
1485
1486
set modsource(help.tcl) {
1487
# Copyright (c) 2010 WorkWare Systems http://workware.net.au/
1488
# All rights reserved
1489
1490
# Module which provides usage, help and the command reference
1491
1492
proc autosetup_help {what} {
1493
use_pager
1494
1495
puts "Usage: [file tail $::autosetup(exe)] \[options\] \[settings\]\n"
1496
puts "This is [autosetup_version], a build environment \"autoconfigurator\""
1497
puts "See the documentation online at https://msteveb.github.io/autosetup/\n"
1498
1499
if {$what in {all local}} {
1500
# Need to load auto.def now
1501
if {[file exists $::autosetup(autodef)]} {
1502
# Load auto.def as module "auto.def"
1503
autosetup_load_module auto.def source $::autosetup(autodef)
1504
}
1505
if {$what eq "all"} {
1506
set what *
1507
} else {
1508
set what auto.def
1509
}
1510
} else {
1511
use $what
1512
puts "Options for module $what:"
1513
}
1514
options-show $what
1515
exit 0
1516
}
1517
1518
proc autosetup_show_license {} {
1519
global modsource autosetup
1520
use_pager
1521
1522
if {[info exists modsource(LICENSE)]} {
1523
puts $modsource(LICENSE)
1524
return
1525
}
1526
foreach dir [list $autosetup(libdir) $autosetup(srcdir)] {
1527
set path [file join $dir LICENSE]
1528
if {[file exists $path]} {
1529
puts [readfile $path]
1530
return
1531
}
1532
}
1533
puts "LICENSE not found"
1534
}
1535
1536
# If not already paged and stdout is a tty, pipe the output through the pager
1537
# This is done by reinvoking autosetup with --nopager added
1538
proc use_pager {} {
1539
if {![opt-bool nopager] && [getenv PAGER ""] ne "" && [isatty? stdin] && [isatty? stdout]} {
1540
if {[catch {
1541
exec [info nameofexecutable] $::argv0 --nopager {*}$::argv |& {*}[getenv PAGER] >@stdout <@stdin 2>@stderr
1542
} msg opts] == 1} {
1543
if {[dict get $opts -errorcode] eq "NONE"} {
1544
# an internal/exec error
1545
puts stderr $msg
1546
exit 1
1547
}
1548
}
1549
exit 0
1550
}
1551
}
1552
1553
# Outputs the autosetup references in one of several formats
1554
proc autosetup_reference {{type text}} {
1555
1556
use_pager
1557
1558
switch -glob -- $type {
1559
wiki {use wiki-formatting}
1560
ascii* {use asciidoc-formatting}
1561
md - markdown {use markdown-formatting}
1562
default {use text-formatting}
1563
}
1564
1565
title "[autosetup_version] -- Command Reference"
1566
1567
section {Introduction}
1568
1569
p {
1570
See https://msteveb.github.io/autosetup/ for the online documentation for 'autosetup'.
1571
This documentation can also be accessed locally with `autosetup --ref`.
1572
}
1573
1574
p {
1575
'autosetup' provides a number of built-in commands which
1576
are documented below. These may be used from 'auto.def' to test
1577
for features, define variables, create files from templates and
1578
other similar actions.
1579
}
1580
1581
automf_command_reference
1582
1583
exit 0
1584
}
1585
1586
proc autosetup_output_block {type lines} {
1587
if {[llength $lines]} {
1588
switch $type {
1589
section {
1590
section $lines
1591
}
1592
subsection {
1593
subsection $lines
1594
}
1595
code {
1596
codelines $lines
1597
}
1598
p {
1599
p [join $lines]
1600
}
1601
list {
1602
foreach line $lines {
1603
bullet $line
1604
}
1605
nl
1606
}
1607
}
1608
}
1609
}
1610
1611
# Generate a command reference from inline documentation
1612
proc automf_command_reference {} {
1613
lappend files $::autosetup(prog)
1614
lappend files {*}[lsort [glob -nocomplain $::autosetup(libdir)/*.tcl]]
1615
1616
# We want to process all non-module files before module files
1617
# and then modules in alphabetical order.
1618
# So examine all files and extract docs into doc($modulename) and doc(_core_)
1619
#
1620
# Each entry is a list of {type data} where $type is one of: section, subsection, code, list, p
1621
# and $data is a string for section, subsection or a list of text lines for other types.
1622
1623
# XXX: Should commands be in alphabetical order too? Currently they are in file order.
1624
1625
set doc(_core_) {}
1626
lappend doc(_core_) [list section "Core Commands"]
1627
1628
foreach file $files {
1629
set modulename [file rootname [file tail $file]]
1630
set current _core_
1631
set f [open $file]
1632
while {![eof $f]} {
1633
set line [gets $f]
1634
1635
if {[regexp {^#.*@section (.*)$} $line -> section]} {
1636
lappend doc($current) [list section $section]
1637
continue
1638
}
1639
1640
# Find embedded module names
1641
if {[regexp {^#.*@module ([^ ]*)} $line -> modulename]} {
1642
continue
1643
}
1644
1645
# Find lines starting with "# @*" and continuing through the remaining comment lines
1646
if {![regexp {^# @(.*)} $line -> cmd]} {
1647
continue
1648
}
1649
1650
# Synopsis or command?
1651
if {$cmd eq "synopsis:"} {
1652
set current $modulename
1653
lappend doc($current) [list section "Module: $modulename"]
1654
} else {
1655
lappend doc($current) [list subsection $cmd]
1656
}
1657
1658
set lines {}
1659
set type p
1660
1661
# Now the description
1662
while {![eof $f]} {
1663
set line [gets $f]
1664
1665
if {![regexp {^#(#)? ?(.*)} $line -> hash cmd]} {
1666
break
1667
}
1668
if {$hash eq "#"} {
1669
set t code
1670
} elseif {[regexp {^- (.*)} $cmd -> cmd]} {
1671
set t list
1672
} else {
1673
set t p
1674
}
1675
1676
#puts "hash=$hash, oldhash=$oldhash, lines=[llength $lines], cmd=$cmd"
1677
1678
if {$t ne $type || $cmd eq ""} {
1679
# Finish the current block
1680
lappend doc($current) [list $type $lines]
1681
set lines {}
1682
set type $t
1683
}
1684
if {$cmd ne ""} {
1685
lappend lines $cmd
1686
}
1687
}
1688
1689
lappend doc($current) [list $type $lines]
1690
}
1691
close $f
1692
}
1693
1694
# Now format and output the results
1695
1696
# _core_ will sort first
1697
foreach module [lsort [array names doc]] {
1698
foreach item $doc($module) {
1699
autosetup_output_block {*}$item
1700
}
1701
}
1702
}
1703
}
1704
1705
# ----- @module init.tcl -----
1706
1707
set modsource(init.tcl) {
1708
# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/
1709
# All rights reserved
1710
1711
# Module to help create auto.def and configure
1712
1713
proc autosetup_init {type} {
1714
set help 0
1715
if {$type in {? help}} {
1716
incr help
1717
} elseif {![dict exists $::autosetup(inittypes) $type]} {
1718
puts "Unknown type, --init=$type"
1719
incr help
1720
}
1721
if {$help} {
1722
puts "Use one of the following types (e.g. --init=make)\n"
1723
foreach type [lsort [dict keys $::autosetup(inittypes)]] {
1724
lassign [dict get $::autosetup(inittypes) $type] desc
1725
# XXX: Use the options-show code to wrap the description
1726
puts [format "%-10s %s" $type $desc]
1727
}
1728
return
1729
}
1730
lassign [dict get $::autosetup(inittypes) $type] desc script
1731
1732
puts "Initialising $type: $desc\n"
1733
1734
# All initialisations happens in the top level srcdir
1735
cd $::autosetup(srcdir)
1736
1737
uplevel #0 $script
1738
}
1739
1740
proc autosetup_add_init_type {type desc script} {
1741
dict set ::autosetup(inittypes) $type [list $desc $script]
1742
}
1743
1744
# This is for in creating build-system init scripts
1745
#
1746
# If the file doesn't exist, create it containing $contents
1747
# If the file does exist, only overwrite if --force is specified.
1748
#
1749
proc autosetup_check_create {filename contents} {
1750
if {[file exists $filename]} {
1751
if {!$::autosetup(force)} {
1752
puts "I see $filename already exists."
1753
return
1754
} else {
1755
puts "I will overwrite the existing $filename because you used --force."
1756
}
1757
} else {
1758
puts "I don't see $filename, so I will create it."
1759
}
1760
writefile $filename $contents
1761
}
1762
}
1763
1764
# ----- @module install.tcl -----
1765
1766
set modsource(install.tcl) {
1767
# Copyright (c) 2006-2010 WorkWare Systems http://www.workware.net.au/
1768
# All rights reserved
1769
1770
# Module which can install autosetup
1771
1772
# autosetup(installed)=1 means that autosetup is not running from source
1773
# autosetup(sysinstall)=1 means that autosetup is running from a sysinstall version
1774
# shared=1 means that we are trying to do a sysinstall. This is only possible from the development source.
1775
1776
proc autosetup_install {dir {shared 0}} {
1777
global autosetup
1778
if {$shared} {
1779
if {$autosetup(installed) || $autosetup(sysinstall)} {
1780
user-error "Can only --sysinstall from development sources"
1781
}
1782
} elseif {$autosetup(installed) && !$autosetup(sysinstall)} {
1783
user-error "Can't --install from project install"
1784
}
1785
1786
if {$autosetup(sysinstall)} {
1787
# This is the sysinstall version, so install just uses references
1788
cd $dir
1789
1790
puts "[autosetup_version] creating configure to use system-installed autosetup"
1791
autosetup_create_configure 1
1792
puts "Creating autosetup/README.autosetup"
1793
file mkdir autosetup
1794
autosetup_install_readme autosetup/README.autosetup 1
1795
return
1796
}
1797
1798
if {[catch {
1799
if {$shared} {
1800
set target $dir/bin/autosetup
1801
set installedas $target
1802
} else {
1803
if {$dir eq "."} {
1804
set installedas autosetup
1805
} else {
1806
set installedas $dir/autosetup
1807
}
1808
cd $dir
1809
file mkdir autosetup
1810
set target autosetup/autosetup
1811
}
1812
set targetdir [file dirname $target]
1813
file mkdir $targetdir
1814
1815
set f [open $target w]
1816
1817
set publicmodules {}
1818
1819
# First the main script, but only up until "CUT HERE"
1820
set in [open $autosetup(dir)/autosetup]
1821
while {[gets $in buf] >= 0} {
1822
if {$buf ne "##-- CUT HERE --##"} {
1823
puts $f $buf
1824
continue
1825
}
1826
1827
# Insert the static modules here
1828
# i.e. those which don't contain @synopsis:
1829
# All modules are inserted if $shared is set
1830
puts $f "set autosetup(installed) 1"
1831
puts $f "set autosetup(sysinstall) $shared"
1832
foreach file [lsort [glob $autosetup(libdir)/*.{tcl,auto}]] {
1833
set modname [file tail $file]
1834
set ext [file ext $modname]
1835
set buf [readfile $file]
1836
if {!$shared} {
1837
if {$ext eq ".auto" || [string match "*\n# @synopsis:*" $buf]} {
1838
lappend publicmodules $file
1839
continue
1840
}
1841
}
1842
dputs "install: importing lib/[file tail $file]"
1843
puts $f "# ----- @module $modname -----"
1844
puts $f "\nset modsource($modname) \{"
1845
puts $f $buf
1846
puts $f "\}\n"
1847
}
1848
if {$shared} {
1849
foreach {srcname destname} [list $autosetup(libdir)/README.autosetup-lib README.autosetup \
1850
$autosetup(srcdir)/LICENSE LICENSE] {
1851
dputs "install: importing $srcname as $destname"
1852
puts $f "\nset modsource($destname) \\\n[list [readfile $srcname]\n]\n"
1853
}
1854
}
1855
}
1856
close $in
1857
close $f
1858
catch {exec chmod 755 $target}
1859
1860
set installfiles {autosetup-config.guess autosetup-config.sub autosetup-test-tclsh}
1861
set removefiles {}
1862
1863
if {!$shared} {
1864
autosetup_install_readme $targetdir/README.autosetup 0
1865
1866
# Install public modules
1867
foreach file $publicmodules {
1868
set tail [file tail $file]
1869
autosetup_install_file $file $targetdir/$tail
1870
}
1871
lappend installfiles jimsh0.c autosetup-find-tclsh LICENSE
1872
lappend removefiles config.guess config.sub test-tclsh find-tclsh
1873
} else {
1874
lappend installfiles {sys-find-tclsh autosetup-find-tclsh}
1875
}
1876
1877
# Install support files
1878
foreach fileinfo $installfiles {
1879
if {[llength $fileinfo] == 2} {
1880
lassign $fileinfo source dest
1881
} else {
1882
lassign $fileinfo source
1883
set dest $source
1884
}
1885
autosetup_install_file $autosetup(dir)/$source $targetdir/$dest
1886
}
1887
1888
# Remove obsolete files
1889
foreach file $removefiles {
1890
if {[file exists $targetdir/$file]} {
1891
file delete $targetdir/$file
1892
}
1893
}
1894
} error]} {
1895
user-error "Failed to install autosetup: $error"
1896
}
1897
if {$shared} {
1898
set type "system"
1899
} else {
1900
set type "local"
1901
}
1902
puts "Installed $type [autosetup_version] to $installedas"
1903
1904
if {!$shared} {
1905
# Now create 'configure' if necessary
1906
autosetup_create_configure 0
1907
}
1908
}
1909
1910
proc autosetup_create_configure {shared} {
1911
if {[file exists configure]} {
1912
if {!$::autosetup(force)} {
1913
# Could this be an autosetup configure?
1914
if {![string match "*\nWRAPPER=*" [readfile configure]]} {
1915
puts "I see configure, but not created by autosetup, so I won't overwrite it."
1916
puts "Remove it or use --force to overwrite."
1917
return
1918
}
1919
} else {
1920
puts "I will overwrite the existing configure because you used --force."
1921
}
1922
} else {
1923
puts "I don't see configure, so I will create it."
1924
}
1925
if {$shared} {
1926
writefile configure \
1927
{#!/bin/sh
1928
WRAPPER="$0"; export WRAPPER; "autosetup" "$@"
1929
}
1930
} else {
1931
writefile configure \
1932
{#!/bin/sh
1933
dir="`dirname "$0"`/autosetup"
1934
#@@INITCHECK@@#
1935
WRAPPER="$0"; export WRAPPER; exec "`"$dir/autosetup-find-tclsh"`" "$dir/autosetup" "$@"
1936
}
1937
}
1938
catch {exec chmod 755 configure}
1939
}
1940
1941
# Append the contents of $file to filehandle $f
1942
proc autosetup_install_append {f file} {
1943
dputs "install: include $file"
1944
set in [open $file]
1945
puts $f [read $in]
1946
close $in
1947
}
1948
1949
proc autosetup_install_file {source target} {
1950
dputs "install: $source => $target"
1951
if {![file exists $source]} {
1952
error "Missing installation file '$source'"
1953
}
1954
writefile $target [readfile $source]\n
1955
# If possible, copy the file mode
1956
file stat $source stat
1957
set mode [format %o [expr {$stat(mode) & 0x1ff}]]
1958
catch {exec chmod $mode $target}
1959
}
1960
1961
proc autosetup_install_readme {target sysinstall} {
1962
set readme "README.autosetup created by [autosetup_version]\n\n"
1963
if {$sysinstall} {
1964
append readme \
1965
{This is the autosetup directory for a system install of autosetup.
1966
Loadable modules can be added here.
1967
}
1968
} else {
1969
append readme \
1970
{This is the autosetup directory for a local install of autosetup.
1971
It contains autosetup, support files and loadable modules.
1972
}
1973
}
1974
1975
append readme {
1976
*.tcl files in this directory are optional modules which
1977
can be loaded with the 'use' directive.
1978
1979
*.auto files in this directory are auto-loaded.
1980
1981
For more information, see https://msteveb.github.io/autosetup/
1982
}
1983
dputs "install: autosetup/README.autosetup"
1984
writefile $target $readme
1985
}
1986
}
1987
1988
# ----- @module markdown-formatting.tcl -----
1989
1990
set modsource(markdown-formatting.tcl) {
1991
# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/
1992
# All rights reserved
1993
1994
# Module which provides text formatting
1995
# markdown format (kramdown syntax)
1996
1997
use formatting
1998
1999
proc para {text} {
2000
regsub -all "\[ \t\n\]+" [string trim $text] " " text
2001
regsub -all {([^a-zA-Z])'([^']*)'} $text {\1**`\2`**} text
2002
regsub -all {^'([^']*)'} $text {**`\1`**} text
2003
regsub -all {(http[^ \t\n]*)} $text {[\1](\1)} text
2004
return $text
2005
}
2006
proc title {text} {
2007
underline [para $text] =
2008
nl
2009
}
2010
proc p {text} {
2011
puts [para $text]
2012
nl
2013
}
2014
proc codelines {lines} {
2015
puts "~~~~~~~~~~~~"
2016
foreach line $lines {
2017
puts $line
2018
}
2019
puts "~~~~~~~~~~~~"
2020
nl
2021
}
2022
proc code {text} {
2023
puts "~~~~~~~~~~~~"
2024
foreach line [parse_code_block $text] {
2025
puts $line
2026
}
2027
puts "~~~~~~~~~~~~"
2028
nl
2029
}
2030
proc nl {} {
2031
puts ""
2032
}
2033
proc underline {text char} {
2034
regexp "^(\[ \t\]*)(.*)" $text -> indent words
2035
puts $text
2036
puts $indent[string repeat $char [string length $words]]
2037
}
2038
proc section {text} {
2039
underline "[para $text]" -
2040
nl
2041
}
2042
proc subsection {text} {
2043
puts "### `$text`"
2044
nl
2045
}
2046
proc bullet {text} {
2047
puts "* [para $text]"
2048
}
2049
proc defn {first args} {
2050
puts "^"
2051
set defn [string trim [join $args \n]]
2052
if {$first ne ""} {
2053
puts "**${first}**"
2054
puts -nonewline ": "
2055
regsub -all "\n\n" $defn "\n: " defn
2056
}
2057
puts "$defn"
2058
}
2059
}
2060
2061
# ----- @module misc.tcl -----
2062
2063
set modsource(misc.tcl) {
2064
# Copyright (c) 2007-2010 WorkWare Systems http://www.workware.net.au/
2065
# All rights reserved
2066
2067
# Module containing misc procs useful to modules
2068
# Largely for platform compatibility
2069
2070
set autosetup(istcl) [info exists ::tcl_library]
2071
set autosetup(iswin) [string equal windows $tcl_platform(platform)]
2072
2073
if {$autosetup(iswin)} {
2074
# mingw/windows separates $PATH with semicolons
2075
# and doesn't have an executable bit
2076
proc split-path {} {
2077
split [getenv PATH .] {;}
2078
}
2079
proc file-isexec {exec} {
2080
# Basic test for windows. We ignore .bat
2081
if {[file isfile $exec] || [file isfile $exec.exe]} {
2082
return 1
2083
}
2084
return 0
2085
}
2086
} else {
2087
# unix separates $PATH with colons and has and executable bit
2088
proc split-path {} {
2089
split [getenv PATH .] :
2090
}
2091
proc file-isexec {exec} {
2092
file executable $exec
2093
}
2094
}
2095
2096
# Assume that exec can return stdout and stderr
2097
proc exec-with-stderr {args} {
2098
exec {*}$args 2>@1
2099
}
2100
2101
if {$autosetup(istcl)} {
2102
# Tcl doesn't have the env command
2103
proc getenv {name args} {
2104
if {[info exists ::env($name)]} {
2105
return $::env($name)
2106
}
2107
if {[llength $args]} {
2108
return [lindex $args 0]
2109
}
2110
return -code error "environment variable \"$name\" does not exist"
2111
}
2112
proc isatty? {channel} {
2113
dict exists [fconfigure $channel] -xchar
2114
}
2115
# Jim-compatible stacktrace using info frame
2116
proc stacktrace {} {
2117
set stacktrace {}
2118
# 2 to skip the current frame
2119
for {set i 2} {$i < [info frame]} {incr i} {
2120
set frame [info frame -$i]
2121
if {[dict exists $frame file]} {
2122
# We don't need proc, so use ""
2123
lappend stacktrace "" [dict get $frame file] [dict get $frame line]
2124
}
2125
}
2126
return $stacktrace
2127
}
2128
} else {
2129
if {$autosetup(iswin)} {
2130
# On Windows, backslash convert all environment variables
2131
# (Assume that Tcl does this for us)
2132
proc getenv {name args} {
2133
string map {\\ /} [env $name {*}$args]
2134
}
2135
} else {
2136
# Jim on unix is simple
2137
alias getenv env
2138
}
2139
proc isatty? {channel} {
2140
set tty 0
2141
catch {
2142
# isatty is a recent addition to Jim Tcl
2143
set tty [$channel isatty]
2144
}
2145
return $tty
2146
}
2147
}
2148
2149
# In case 'file normalize' doesn't exist
2150
#
2151
proc file-normalize {path} {
2152
if {[catch {file normalize $path} result]} {
2153
if {$path eq ""} {
2154
return ""
2155
}
2156
set oldpwd [pwd]
2157
if {[file isdir $path]} {
2158
cd $path
2159
set result [pwd]
2160
} else {
2161
cd [file dirname $path]
2162
set result [file join [pwd] [file tail $path]]
2163
}
2164
cd $oldpwd
2165
}
2166
return $result
2167
}
2168
2169
# If everything is working properly, the only errors which occur
2170
# should be generated in user code (e.g. auto.def).
2171
# By default, we only want to show the error location in user code.
2172
# We use [info frame] to achieve this, but it works differently on Tcl and Jim.
2173
#
2174
# This is designed to be called for incorrect usage in auto.def, via autosetup-error
2175
#
2176
proc error-location {msg} {
2177
if {$::autosetup(debug)} {
2178
return -code error $msg
2179
}
2180
# Search back through the stack trace for the first error in a .def file
2181
foreach {p f l} [stacktrace] {
2182
if {[string match *.def $f]} {
2183
return "[relative-path $f]:$l: Error: $msg"
2184
}
2185
#puts "Skipping $f:$l"
2186
}
2187
return $msg
2188
}
2189
2190
# If everything is working properly, the only errors which occur
2191
# should be generated in user code (e.g. auto.def).
2192
# By default, we only want to show the error location in user code.
2193
# We use [info frame] to achieve this, but it works differently on Tcl and Jim.
2194
#
2195
# This is designed to be called for incorrect usage in auto.def, via autosetup-error
2196
#
2197
proc error-stacktrace {msg} {
2198
if {$::autosetup(debug)} {
2199
return -code error $msg
2200
}
2201
# Search back through the stack trace for the first error in a .def file
2202
for {set i 1} {$i < [info level]} {incr i} {
2203
if {$::autosetup(istcl)} {
2204
array set info [info frame -$i]
2205
} else {
2206
lassign [info frame -$i] info(caller) info(file) info(line)
2207
}
2208
if {[string match *.def $info(file)]} {
2209
return "[relative-path $info(file)]:$info(line): Error: $msg"
2210
}
2211
#puts "Skipping $info(file):$info(line)"
2212
}
2213
return $msg
2214
}
2215
2216
# Given the return from [catch {...} msg opts], returns an appropriate
2217
# error message. A nice one for Jim and a less-nice one for Tcl.
2218
# If 'fulltrace' is set, a full stack trace is provided.
2219
# Otherwise a simple message is provided.
2220
#
2221
# This is designed for developer errors, e.g. in module code or auto.def code
2222
#
2223
#
2224
proc error-dump {msg opts fulltrace} {
2225
if {$::autosetup(istcl)} {
2226
if {$fulltrace} {
2227
return "Error: [dict get $opts -errorinfo]"
2228
} else {
2229
return "Error: $msg"
2230
}
2231
} else {
2232
lassign $opts(-errorinfo) p f l
2233
if {$f ne ""} {
2234
set result "$f:$l: Error: "
2235
}
2236
append result "$msg\n"
2237
if {$fulltrace} {
2238
append result [stackdump $opts(-errorinfo)]
2239
}
2240
2241
# Remove the trailing newline
2242
string trim $result
2243
}
2244
}
2245
}
2246
2247
# ----- @module text-formatting.tcl -----
2248
2249
set modsource(text-formatting.tcl) {
2250
# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/
2251
# All rights reserved
2252
2253
# Module which provides text formatting
2254
2255
use formatting
2256
2257
proc wordwrap {text length {firstprefix ""} {nextprefix ""}} {
2258
set len 0
2259
set space $firstprefix
2260
2261
foreach word [split $text] {
2262
set word [string trim $word]
2263
if {$word eq ""} {
2264
continue
2265
}
2266
if {[info exists partial]} {
2267
append partial " " $word
2268
if {[string first $quote $word] < 0} {
2269
# Haven't found end of quoted word
2270
continue
2271
}
2272
# Finished quoted word
2273
set word $partial
2274
unset partial
2275
unset quote
2276
} else {
2277
set quote [string index $word 0]
2278
if {$quote in {' *}} {
2279
if {[string first $quote $word 1] < 0} {
2280
# Haven't found end of quoted word
2281
# Not a whole word.
2282
set first [string index $word 0]
2283
# Start of quoted word
2284
set partial $word
2285
continue
2286
}
2287
}
2288
}
2289
2290
if {$len && [string length $space$word] + $len >= $length} {
2291
puts ""
2292
set len 0
2293
set space $nextprefix
2294
}
2295
incr len [string length $space$word]
2296
2297
# Use man-page conventions for highlighting 'quoted' and *quoted*
2298
# single words.
2299
# Use x^Hx for *bold* and _^Hx for 'underline'.
2300
#
2301
# less and more will both understand this.
2302
# Pipe through 'col -b' to remove them.
2303
if {[regexp {^'(.*)'(.*)} $word -> quoted after]} {
2304
set quoted [string map {~ " "} $quoted]
2305
regsub -all . $quoted "&\b&" quoted
2306
set word $quoted$after
2307
} elseif {[regexp {^[*](.*)[*](.*)} $word -> quoted after]} {
2308
set quoted [string map {~ " "} $quoted]
2309
regsub -all . $quoted "_\b&" quoted
2310
set word $quoted$after
2311
}
2312
puts -nonewline $space$word
2313
set space " "
2314
}
2315
if {[info exists partial]} {
2316
# Missing end of quote
2317
puts -nonewline $space$partial
2318
}
2319
if {$len} {
2320
puts ""
2321
}
2322
}
2323
proc title {text} {
2324
underline [string trim $text] =
2325
nl
2326
}
2327
proc p {text} {
2328
wordwrap $text 80
2329
nl
2330
}
2331
proc codelines {lines} {
2332
foreach line $lines {
2333
puts " $line"
2334
}
2335
nl
2336
}
2337
proc nl {} {
2338
puts ""
2339
}
2340
proc underline {text char} {
2341
regexp "^(\[ \t\]*)(.*)" $text -> indent words
2342
puts $text
2343
puts $indent[string repeat $char [string length $words]]
2344
}
2345
proc section {text} {
2346
underline "[string trim $text]" -
2347
nl
2348
}
2349
proc subsection {text} {
2350
underline "$text" ~
2351
nl
2352
}
2353
proc bullet {text} {
2354
wordwrap $text 76 " * " " "
2355
}
2356
proc indent {text} {
2357
wordwrap $text 76 " " " "
2358
}
2359
proc defn {first args} {
2360
if {$first ne ""} {
2361
underline " $first" ~
2362
}
2363
foreach p $args {
2364
if {$p ne ""} {
2365
indent $p
2366
}
2367
}
2368
}
2369
}
2370
2371
# ----- @module util.tcl -----
2372
2373
set modsource(util.tcl) {
2374
# Copyright (c) 2012 WorkWare Systems http://www.workware.net.au/
2375
# All rights reserved
2376
2377
# Module which contains miscellaneous utility functions
2378
2379
# @section Utilities
2380
2381
# @compare-versions version1 version2
2382
#
2383
# Versions are of the form 'a.b.c' (may be any number of numeric components)
2384
#
2385
# Compares the two versions and returns:
2386
## -1 if v1 < v2
2387
## 0 if v1 == v2
2388
## 1 if v1 > v2
2389
#
2390
# If one version has fewer components than the other, 0 is substituted to the right. e.g.
2391
## 0.2 < 0.3
2392
## 0.2.5 > 0.2
2393
## 1.1 == 1.1.0
2394
#
2395
proc compare-versions {v1 v2} {
2396
foreach c1 [split $v1 .] c2 [split $v2 .] {
2397
if {$c1 eq ""} {
2398
set c1 0
2399
}
2400
if {$c2 eq ""} {
2401
set c2 0
2402
}
2403
if {$c1 < $c2} {
2404
return -1
2405
}
2406
if {$c1 > $c2} {
2407
return 1
2408
}
2409
}
2410
return 0
2411
}
2412
2413
# @suffix suf list
2414
#
2415
# Takes a list and returns a new list with '$suf' appended
2416
# to each element
2417
#
2418
## suffix .c {a b c} => {a.c b.c c.c}
2419
#
2420
proc suffix {suf list} {
2421
set result {}
2422
foreach p $list {
2423
lappend result $p$suf
2424
}
2425
return $result
2426
}
2427
2428
# @prefix pre list
2429
#
2430
# Takes a list and returns a new list with '$pre' prepended
2431
# to each element
2432
#
2433
## prefix jim- {a.c b.c} => {jim-a.c jim-b.c}
2434
#
2435
proc prefix {pre list} {
2436
set result {}
2437
foreach p $list {
2438
lappend result $pre$p
2439
}
2440
return $result
2441
}
2442
2443
# @lpop list
2444
#
2445
# Removes the last entry from the given list and returns it.
2446
proc lpop {listname} {
2447
upvar $listname list
2448
set val [lindex $list end]
2449
set list [lrange $list 0 end-1]
2450
return $val
2451
}
2452
}
2453
2454
# ----- @module wiki-formatting.tcl -----
2455
2456
set modsource(wiki-formatting.tcl) {
2457
# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/
2458
# All rights reserved
2459
2460
# Module which provides text formatting
2461
# wiki.tcl.tk format output
2462
2463
use formatting
2464
2465
proc joinlines {text} {
2466
set lines {}
2467
foreach l [split [string trim $text] \n] {
2468
lappend lines [string trim $l]
2469
}
2470
join $lines
2471
}
2472
proc p {text} {
2473
puts [joinlines $text]
2474
puts ""
2475
}
2476
proc title {text} {
2477
puts "*** [joinlines $text] ***"
2478
puts ""
2479
}
2480
proc codelines {lines} {
2481
puts "======"
2482
foreach line $lines {
2483
puts " $line"
2484
}
2485
puts "======"
2486
}
2487
proc code {text} {
2488
puts "======"
2489
foreach line [parse_code_block $text] {
2490
puts " $line"
2491
}
2492
puts "======"
2493
}
2494
proc nl {} {
2495
}
2496
proc section {text} {
2497
puts "'''$text'''"
2498
puts ""
2499
}
2500
proc subsection {text} {
2501
puts "''$text''"
2502
puts ""
2503
}
2504
proc bullet {text} {
2505
puts " * [joinlines $text]"
2506
}
2507
proc indent {text} {
2508
puts " : [joinlines $text]"
2509
}
2510
proc defn {first args} {
2511
if {$first ne ""} {
2512
indent '''$first'''
2513
}
2514
2515
foreach p $args {
2516
p $p
2517
}
2518
}
2519
}
2520
2521
2522
##################################################################
2523
#
2524
# Entry/Exit
2525
#
2526
if {$autosetup(debug)} {
2527
main $argv
2528
}
2529
if {[catch {main $argv} msg opts] == 1} {
2530
show-notices
2531
autosetup-full-error [error-dump $msg $opts $autosetup(debug)]
2532
if {!$autosetup(debug)} {
2533
puts stderr "Try: '[file tail $autosetup(exe)] --debug' for a full stack trace"
2534
}
2535
exit 1
2536
}
2537

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button