xref: /freebsd/contrib/sqlite3/autosetup/system.tcl (revision 17f0f75308f287efea825457364e2a4de2e107d4)
1# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/
2# All rights reserved
3
4# @synopsis:
5#
6# This module supports common system interrogation and options
7# such as '--host', '--build', '--prefix', and setting 'srcdir', 'builddir', and 'EXEEXT'.
8#
9# It also support the "feature" naming convention, where searching
10# for a feature such as 'sys/type.h' defines 'HAVE_SYS_TYPES_H'.
11#
12# It defines the following variables, based on '--prefix' unless overridden by the user:
13#
14## datadir
15## sysconfdir
16## sharedstatedir
17## localstatedir
18## infodir
19## mandir
20## includedir
21#
22# If '--prefix' is not supplied, it defaults to '/usr/local' unless 'options-defaults { prefix ... }' is used *before*
23# including the 'system' module.
24
25if {[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]]
28}
29
30options {
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@')}
37
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
55	program-prefix:
56	program-suffix:
57	program-transform-name:
58	x-includes:
59	x-libraries:
60}
61
62# @check-feature name { script }
63#
64# defines feature '$name' to the return value of '$script',
65# which should be 1 if found or 0 if not found.
66#
67# e.g. the following will define 'HAVE_CONST' to 0 or 1.
68#
69## check-feature const {
70##     cctest -code {const int _x = 0;}
71## }
72proc check-feature {name code} {
73	msg-checking "Checking for $name..."
74	set r [uplevel 1 $code]
75	define-feature $name $r
76	if {$r} {
77		msg-result "ok"
78	} else {
79		msg-result "not found"
80	}
81	return $r
82}
83
84# @have-feature name ?default=0?
85#
86# Returns the value of feature '$name' if defined, or '$default' if not.
87#
88# See 'feature-define-name' for how the "feature" name
89# is translated into the "define" name.
90#
91proc have-feature {name {default 0}} {
92	get-define [feature-define-name $name] $default
93}
94
95# @define-feature name ?value=1?
96#
97# Sets the feature 'define' to '$value'.
98#
99# See 'feature-define-name' for how the "feature" name
100# is translated into the "define" name.
101#
102proc define-feature {name {value 1}} {
103	define [feature-define-name $name] $value
104}
105
106# @feature-checked name
107#
108# Returns 1 if feature '$name' has been checked, whether true or not.
109#
110proc feature-checked {name} {
111	is-defined [feature-define-name $name]
112}
113
114# @feature-define-name name ?prefix=HAVE_?
115#
116# Converts a "feature" name to the corresponding "define",
117# e.g. 'sys/stat.h' becomes 'HAVE_SYS_STAT_H'.
118#
119# Converts '*' to 'P' and all non-alphanumeric to underscore.
120#
121proc feature-define-name {name {prefix HAVE_}} {
122	string toupper $prefix[regsub -all {[^a-zA-Z0-9]} [regsub -all {[*]} $name p] _]
123}
124
125# @write-if-changed filename contents ?script?
126#
127# If '$filename' doesn't exist, or it's contents are different to '$contents',
128# the file is written and '$script' is evaluated.
129#
130# Otherwise a "file is unchanged" message is displayed.
131proc write-if-changed {file buf {script {}}} {
132	set old [readfile $file ""]
133	if {$old eq $buf && [file exists $file]} {
134		msg-result "$file is unchanged"
135	} else {
136		writefile $file $buf\n
137		uplevel 1 $script
138	}
139}
140
141
142# @include-file infile mapping
143#
144# The core of make-template, called recursively for each @include
145# directive found within that template so that this proc's result
146# is the fully-expanded template.
147#
148# The mapping parameter is how we expand @varname@ within the template.
149# We do that inline within this step only for @include directives which
150# can have variables in the filename arg.  A separate substitution pass
151# happens when this recursive function returns, expanding the rest of
152# the variables.
153#
154proc include-file {infile mapping} {
155	# A stack of true/false conditions, one for each nested conditional
156	# starting with "true"
157	set condstack {1}
158	set result {}
159	set linenum 0
160	foreach line [split [readfile $infile] \n] {
161		incr linenum
162		if {[regexp {^@(if|else|endif)(\s*)(.*)} $line -> condtype condspace condargs]} {
163			if {$condtype eq "if"} {
164				if {[string length $condspace] == 0} {
165					autosetup-error "$infile:$linenum: Invalid expression: $line"
166				}
167				if {[llength $condargs] == 1} {
168					# ABC => [get-define ABC] ni {0 ""}
169					# !ABC => [get-define ABC] in {0 ""}
170					lassign $condargs condvar
171					if {[regexp {^!(.*)} $condvar -> condvar]} {
172						set op in
173					} else {
174						set op ni
175					}
176					set condexpr "\[[list get-define $condvar]\] $op {0 {}}"
177				} else {
178					# Translate alphanumeric ABC into [get-define ABC] and leave the
179					# rest of the expression untouched
180					regsub -all {([A-Z][[:alnum:]_]*)} $condargs {[get-define \1]} condexpr
181				}
182				if {[catch [list expr $condexpr] condval]} {
183					dputs $condval
184					autosetup-error "$infile:$linenum: Invalid expression: $line"
185				}
186				dputs "@$condtype: $condexpr => $condval"
187			}
188			if {$condtype ne "if"} {
189				if {[llength $condstack] <= 1} {
190					autosetup-error "$infile:$linenum: Error: @$condtype missing @if"
191				} elseif {[string length $condargs] && [string index $condargs 0] ne "#"} {
192					autosetup-error "$infile:$linenum: Error: Extra arguments after @$condtype"
193				}
194			}
195			switch -exact $condtype {
196				if {
197					# push condval
198					lappend condstack $condval
199				}
200				else {
201					# Toggle the last entry
202					set condval [lpop condstack]
203					set condval [expr {!$condval}]
204					lappend condstack $condval
205				}
206				endif {
207					if {[llength $condstack] == 0} {
208						user-notice "$infile:$linenum: Error: @endif missing @if"
209					}
210					lpop condstack
211				}
212			}
213			continue
214		}
215		# Only continue if the stack contains all "true"
216		if {"0" in $condstack} {
217			continue
218		}
219		if {[regexp {^@include\s+(.*)} $line -> filearg]} {
220			set incfile [string map $mapping $filearg]
221			if {[file exists $incfile]} {
222				lappend ::autosetup(deps) [file-normalize $incfile]
223				lappend result {*}[include-file $incfile $mapping]
224			} else {
225				user-error "$infile:$linenum: Include file $incfile is missing"
226			}
227			continue
228		}
229		if {[regexp {^@define\s+(\w+)\s+(.*)} $line -> var val]} {
230			define $var $val
231			continue
232		}
233		lappend result $line
234	}
235	return $result
236}
237
238
239# @make-template template ?outfile?
240#
241# Reads the input file '<srcdir>/$template' and writes the output file '$outfile'
242# (unless unchanged).
243# If '$outfile' is blank/omitted, '$template' should end with '.in' which
244# is removed to create the output file name.
245#
246# Each pattern of the form '@define@' is replaced with the corresponding
247# "define", if it exists, or left unchanged if not.
248#
249# The special value '@srcdir@' is substituted with the relative
250# path to the source directory from the directory where the output
251# file is created, while the special value '@top_srcdir@' is substituted
252# with the relative path to the top level source directory.
253#
254# Conditional sections may be specified as follows:
255## @if NAME eq "value"
256## lines
257## @else
258## lines
259## @endif
260#
261# Where 'NAME' is a defined variable name and '@else' is optional.
262# Note that variables names *must* start with an uppercase letter.
263# If the expression does not match, all lines through '@endif' are ignored.
264#
265# The alternative forms may also be used:
266## @if NAME  (true if the variable is defined, but not empty and not "0")
267## @if !NAME  (opposite of the form above)
268## @if <general-tcl-expression>
269#
270# In the general Tcl expression, any words beginning with an uppercase letter
271# are translated into [get-define NAME]
272#
273# Expressions may be nested
274#
275proc make-template {template {out {}}} {
276	set infile [file join $::autosetup(srcdir) $template]
277
278	if {![file exists $infile]} {
279		user-error "Template $template is missing"
280	}
281
282	# Define this as late as possible
283	define AUTODEPS $::autosetup(deps)
284
285	if {$out eq ""} {
286		if {[file ext $template] ne ".in"} {
287			autosetup-error "make_template $template has no target file and can't guess"
288		}
289		set out [file rootname $template]
290	}
291
292	set outdir [file dirname $out]
293
294	# Make sure the directory exists
295	file mkdir $outdir
296
297	# Set up srcdir and top_srcdir to be relative to the target dir
298	define srcdir [relative-path [file join $::autosetup(srcdir) $outdir] $outdir]
299	define top_srcdir [relative-path $::autosetup(srcdir) $outdir]
300
301	# Build map from global defines to their values so they can be
302	# substituted into @include file names.
303	proc build-define-mapping {} {
304		set mapping {}
305		foreach {n v} [array get ::define] {
306			lappend mapping @$n@ $v
307		}
308		return $mapping
309	}
310	set mapping [build-define-mapping]
311
312	set result [include-file $infile $mapping]
313
314	# Rebuild the define mapping in case we ran across @define
315	# directives in the template or a file it @included, then
316	# apply that mapping to the expanded template.
317	set mapping [build-define-mapping]
318	write-if-changed $out [string map $mapping [join $result \n]] {
319		msg-result "Created [relative-path $out] from [relative-path $template]"
320	}
321}
322
323proc system-init {} {
324	global autosetup
325
326	# build/host tuples and cross-compilation prefix
327	opt-str build build ""
328	define build_alias $build
329	if {$build eq ""} {
330		define build [config_guess]
331	} else {
332		define build [config_sub $build]
333	}
334
335	opt-str host host ""
336	define host_alias $host
337	if {$host eq ""} {
338		define host [get-define build]
339		set cross ""
340	} else {
341		define host [config_sub $host]
342		set cross $host-
343	}
344	define cross [get-env CROSS $cross]
345
346	# build/host _cpu, _vendor and _os
347	foreach type {build host} {
348		set v [get-define $type]
349		if {![regexp {^([^-]+)-([^-]+)-(.*)$} $v -> cpu vendor os]} {
350			user-error "Invalid canonical $type: $v"
351		}
352		define ${type}_cpu $cpu
353		define ${type}_vendor $vendor
354		define ${type}_os $os
355	}
356
357	opt-str prefix prefix /usr/local
358
359	# These are for compatibility with autoconf
360	define target [get-define host]
361	define prefix $prefix
362	define builddir $autosetup(builddir)
363	define srcdir $autosetup(srcdir)
364	define top_srcdir $autosetup(srcdir)
365	define abs_top_srcdir [file-normalize $autosetup(srcdir)]
366	define abs_top_builddir [file-normalize $autosetup(builddir)]
367
368	# autoconf supports all of these
369	define exec_prefix [opt-str exec-prefix exec_prefix $prefix]
370	foreach {name defpath} {
371		bindir /bin
372		sbindir /sbin
373		libexecdir /libexec
374		libdir /lib
375	} {
376		define $name [opt-str $name o $exec_prefix$defpath]
377	}
378	foreach {name defpath} {
379		datadir /share
380		sharedstatedir /com
381		infodir /share/info
382		mandir /share/man
383		includedir /include
384	} {
385		define $name [opt-str $name o $prefix$defpath]
386	}
387	if {$prefix ne {/usr}} {
388		opt-str sysconfdir sysconfdir $prefix/etc
389	} else {
390		opt-str sysconfdir sysconfdir /etc
391	}
392	define sysconfdir $sysconfdir
393
394	define localstatedir [opt-str localstatedir o /var]
395	define runstatedir [opt-str runstatedir o /run]
396
397	define SHELL [get-env SHELL [find-an-executable sh bash ksh]]
398
399	# These could be used to generate Makefiles following some automake conventions
400	define AM_SILENT_RULES [opt-bool silent-rules]
401	define AM_MAINTAINER_MODE [opt-bool maintainer-mode]
402	define AM_DEPENDENCY_TRACKING [opt-bool dependency-tracking]
403
404	# Windows vs. non-Windows
405	switch -glob -- [get-define host] {
406		*-*-ming* - *-*-cygwin - *-*-msys {
407			define-feature windows
408			define EXEEXT .exe
409		}
410		default {
411			define EXEEXT ""
412		}
413	}
414
415	# Display
416	msg-result "Host System...[get-define host]"
417	msg-result "Build System...[get-define build]"
418}
419
420system-init
421