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