jimregexp: rename local regex functions
[jimtcl.git] / autosetup / system.tcl
blobb98b5c083016b1bb29444c313c3678610e9deff0
1 # Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/
2 # All rights reserved
4 # @synopsis:
6 # This module supports common system interrogation and options
7 # such as '--host', '--build', '--prefix', and setting 'srcdir', 'builddir', and 'EXEEXT'.
9 # It also support the "feature" naming convention, where searching
10 # for a feature such as 'sys/type.h' defines 'HAVE_SYS_TYPES_H'.
12 # It defines the following variables, based on '--prefix' unless overridden by the user:
14 ## datadir
15 ## sysconfdir
16 ## sharedstatedir
17 ## localstatedir
18 ## infodir
19 ## mandir
20 ## includedir
22 # If '--prefix' is not supplied, it defaults to '/usr/local' unless 'defaultprefix' is defined *before*
23 # including the 'system' module.
25 if {[is-defined defaultprefix]} {
26 user-notice "Note: defaultprefix is deprecated. Use options-defaults to set default options"
27 options-defaults [list prefix [get-define defaultprefix]]
30 module-options [subst -noc -nob {
31 host:host-alias => {a complete or partial cpu-vendor-opsys for the system where
32 the application will run (defaults to the same value as --build)}
33 build:build-alias => {a complete or partial cpu-vendor-opsys for the system
34 where the application will be built (defaults to the
35 result of running config.guess)}
36 prefix:dir=/usr/local => {the target directory for the build (default: '@default@')}
38 # These (hidden) options are supported for autoconf/automake compatibility
39 exec-prefix:
40 bindir:
41 sbindir:
42 includedir:
43 mandir:
44 infodir:
45 libexecdir:
46 datadir:
47 libdir:
48 sysconfdir:
49 sharedstatedir:
50 localstatedir:
51 runstatedir:
52 maintainer-mode=0
53 dependency-tracking=0
54 silent-rules=0
57 # @check-feature name { script }
59 # defines feature '$name' to the return value of '$script',
60 # which should be 1 if found or 0 if not found.
62 # e.g. the following will define 'HAVE_CONST' to 0 or 1.
64 ## check-feature const {
65 ## cctest -code {const int _x = 0;}
66 ## }
67 proc check-feature {name code} {
68 msg-checking "Checking for $name..."
69 set r [uplevel 1 $code]
70 define-feature $name $r
71 if {$r} {
72 msg-result "ok"
73 } else {
74 msg-result "not found"
76 return $r
79 # @have-feature name ?default=0?
81 # Returns the value of feature '$name' if defined, or '$default' if not.
83 # See 'feature-define-name' for how the "feature" name
84 # is translated into the "define" name.
86 proc have-feature {name {default 0}} {
87 get-define [feature-define-name $name] $default
90 # @define-feature name ?value=1?
92 # Sets the feature 'define' to '$value'.
94 # See 'feature-define-name' for how the "feature" name
95 # is translated into the "define" name.
97 proc define-feature {name {value 1}} {
98 define [feature-define-name $name] $value
101 # @feature-checked name
103 # Returns 1 if feature '$name' has been checked, whether true or not.
105 proc feature-checked {name} {
106 is-defined [feature-define-name $name]
109 # @feature-define-name name ?prefix=HAVE_?
111 # Converts a "feature" name to the corresponding "define",
112 # e.g. 'sys/stat.h' becomes 'HAVE_SYS_STAT_H'.
114 # Converts '*' to 'P' and all non-alphanumeric to underscore.
116 proc feature-define-name {name {prefix HAVE_}} {
117 string toupper $prefix[regsub -all {[^a-zA-Z0-9]} [regsub -all {[*]} $name p] _]
120 # @write-if-changed filename contents ?script?
122 # If '$filename' doesn't exist, or it's contents are different to '$contents',
123 # the file is written and '$script' is evaluated.
125 # Otherwise a "file is unchanged" message is displayed.
126 proc write-if-changed {file buf {script {}}} {
127 set old [readfile $file ""]
128 if {$old eq $buf && [file exists $file]} {
129 msg-result "$file is unchanged"
130 } else {
131 writefile $file $buf\n
132 uplevel 1 $script
137 # @include-file infile mapping
139 # The core of make-template, called recursively for each @include
140 # directive found within that template so that this proc's result
141 # is the fully-expanded template.
143 # The mapping parameter is how we expand @varname@ within the template.
144 # We do that inline within this step only for @include directives which
145 # can have variables in the filename arg. A separate substitution pass
146 # happens when this recursive function returns, expanding the rest of
147 # the variables.
149 proc include-file {infile mapping} {
150 # A stack of true/false conditions, one for each nested conditional
151 # starting with "true"
152 set condstack {1}
153 set result {}
154 set linenum 0
155 foreach line [split [readfile $infile] \n] {
156 incr linenum
157 if {[regexp {^@(if|else|endif)(\s*)(.*)} $line -> condtype condspace condargs]} {
158 if {$condtype eq "if"} {
159 if {[string length $condspace] == 0} {
160 autosetup-error "$infile:$linenum: Invalid expression: $line"
162 if {[llength $condargs] == 1} {
163 # ABC => [get-define ABC] ni {0 ""}
164 # !ABC => [get-define ABC] in {0 ""}
165 lassign $condargs condvar
166 if {[regexp {^!(.*)} $condvar -> condvar]} {
167 set op in
168 } else {
169 set op ni
171 set condexpr "\[[list get-define $condvar]\] $op {0 {}}"
172 } else {
173 # Translate alphanumeric ABC into [get-define ABC] and leave the
174 # rest of the expression untouched
175 regsub -all {([A-Z][[:alnum:]_]*)} $condargs {[get-define \1]} condexpr
177 if {[catch [list expr $condexpr] condval]} {
178 dputs $condval
179 autosetup-error "$infile:$linenum: Invalid expression: $line"
181 dputs "@$condtype: $condexpr => $condval"
183 if {$condtype ne "if"} {
184 if {[llength $condstack] <= 1} {
185 autosetup-error "$infile:$linenum: Error: @$condtype missing @if"
186 } elseif {[string length $condargs] && [string index $condargs 0] ne "#"} {
187 autosetup-error "$infile:$linenum: Error: Extra arguments after @$condtype"
190 switch -exact $condtype {
191 if {
192 # push condval
193 lappend condstack $condval
195 else {
196 # Toggle the last entry
197 set condval [lpop condstack]
198 set condval [expr {!$condval}]
199 lappend condstack $condval
201 endif {
202 if {[llength $condstack] == 0} {
203 user-notice "$infile:$linenum: Error: @endif missing @if"
205 lpop condstack
208 continue
209 } elseif {[regexp {^@include\s+(.*)} $line -> filearg]} {
210 set incfile [string map $mapping $filearg]
211 if {[file exists $incfile]} {
212 lappend ::autosetup(deps) [file-normalize $incfile]
213 lappend result {*}[include-file $incfile $mapping]
214 } else {
215 user-error "$infile:$linenum: Include file $incfile is missing"
217 continue
218 } elseif {[regexp {^@define\s+(\w+)\s+(.*)} $line -> var val]} {
219 define $var $val
220 continue
222 # Only output this line if the stack contains all "true"
223 if {"0" in $condstack} {
224 continue
226 lappend result $line
228 return $result
232 # @make-template template ?outfile?
234 # Reads the input file '<srcdir>/$template' and writes the output file '$outfile'
235 # (unless unchanged).
236 # If '$outfile' is blank/omitted, '$template' should end with '.in' which
237 # is removed to create the output file name.
239 # Each pattern of the form '@define@' is replaced with the corresponding
240 # "define", if it exists, or left unchanged if not.
242 # The special value '@srcdir@' is substituted with the relative
243 # path to the source directory from the directory where the output
244 # file is created, while the special value '@top_srcdir@' is substituted
245 # with the relative path to the top level source directory.
247 # Conditional sections may be specified as follows:
248 ## @if NAME eq "value"
249 ## lines
250 ## @else
251 ## lines
252 ## @endif
254 # Where 'NAME' is a defined variable name and '@else' is optional.
255 # Note that variables names *must* start with an uppercase letter.
256 # If the expression does not match, all lines through '@endif' are ignored.
258 # The alternative forms may also be used:
259 ## @if NAME (true if the variable is defined, but not empty and not "0")
260 ## @if !NAME (opposite of the form above)
261 ## @if <general-tcl-expression>
263 # In the general Tcl expression, any words beginning with an uppercase letter
264 # are translated into [get-define NAME]
266 # Expressions may be nested
268 proc make-template {template {out {}}} {
269 set infile [file join $::autosetup(srcdir) $template]
271 if {![file exists $infile]} {
272 user-error "Template $template is missing"
275 # Define this as late as possible
276 define AUTODEPS $::autosetup(deps)
278 if {$out eq ""} {
279 if {[file ext $template] ne ".in"} {
280 autosetup-error "make_template $template has no target file and can't guess"
282 set out [file rootname $template]
285 set outdir [file dirname $out]
287 # Make sure the directory exists
288 file mkdir $outdir
290 # Set up srcdir and top_srcdir to be relative to the target dir
291 define srcdir [relative-path [file join $::autosetup(srcdir) $outdir] $outdir]
292 define top_srcdir [relative-path $::autosetup(srcdir) $outdir]
294 # Build map from global defines to their values so they can be
295 # substituted into @include file names.
296 proc build-define-mapping {} {
297 set mapping {}
298 foreach {n v} [array get ::define] {
299 lappend mapping @$n@ $v
301 return $mapping
303 set mapping [build-define-mapping]
305 set result [include-file $infile $mapping]
307 # Rebuild the define mapping in case we ran across @define
308 # directives in the template or a file it @included, then
309 # apply that mapping to the expanded template.
310 set mapping [build-define-mapping]
311 write-if-changed $out [string map $mapping [join $result \n]] {
312 msg-result "Created [relative-path $out] from [relative-path $template]"
316 # build/host tuples and cross-compilation prefix
317 opt-str build build ""
318 define build_alias $build
319 if {$build eq ""} {
320 define build [config_guess]
321 } else {
322 define build [config_sub $build]
325 opt-str host host ""
326 define host_alias $host
327 if {$host eq ""} {
328 define host [get-define build]
329 set cross ""
330 } else {
331 define host [config_sub $host]
332 set cross $host-
334 define cross [get-env CROSS $cross]
336 # build/host _cpu, _vendor and _os
337 foreach type {build host} {
338 set v [get-define $type]
339 if {![regexp {^([^-]+)-([^-]+)-(.*)$} $v -> cpu vendor os]} {
340 user-error "Invalid canonical $type: $v"
342 define ${type}_cpu $cpu
343 define ${type}_vendor $vendor
344 define ${type}_os $os
347 opt-str prefix prefix /usr/local
349 # These are for compatibility with autoconf
350 define target [get-define host]
351 define prefix $prefix
352 define builddir $autosetup(builddir)
353 define srcdir $autosetup(srcdir)
354 define top_srcdir $autosetup(srcdir)
355 define abs_top_srcdir [file-normalize $autosetup(srcdir)]
356 define abs_top_builddir [file-normalize $autosetup(builddir)]
358 # autoconf supports all of these
359 define exec_prefix [opt-str exec-prefix exec_prefix $prefix]
360 foreach {name defpath} {
361 bindir /bin
362 sbindir /sbin
363 libexecdir /libexec
364 libdir /lib
366 define $name [opt-str $name o $exec_prefix$defpath]
368 foreach {name defpath} {
369 datadir /share
370 sharedstatedir /com
371 infodir /share/info
372 mandir /share/man
373 includedir /include
375 define $name [opt-str $name o $prefix$defpath]
377 if {$prefix ne {/usr}} {
378 opt-str sysconfdir sysconfdir $prefix/etc
379 } else {
380 opt-str sysconfdir sysconfdir /etc
382 define sysconfdir $sysconfdir
384 define localstatedir [opt-str localstatedir o /var]
385 define runstatedir [opt-str runstatedir o /run]
387 define SHELL [get-env SHELL [find-an-executable sh bash ksh]]
389 # These could be used to generate Makefiles following some automake conventions
390 define AM_SILENT_RULES [opt-bool silent-rules]
391 define AM_MAINTAINER_MODE [opt-bool maintainer-mode]
392 define AM_DEPENDENCY_TRACKING [opt-bool dependency-tracking]
394 # Windows vs. non-Windows
395 switch -glob -- [get-define host] {
396 *-*-ming* - *-*-cygwin - *-*-msys {
397 define-feature windows
398 define EXEEXT .exe
400 default {
401 define EXEEXT ""
405 # Display
406 msg-result "Host System...[get-define host]"
407 msg-result "Build System...[get-define build]"