1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
|
# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/
# All rights reserved
# @synopsis:
#
# The 'cc' module supports checking various 'features' of the C or C++
# compiler/linker environment. Common commands are cc-check-includes,
# cc-check-types, cc-check-functions, cc-with, make-autoconf-h and make-template.
#
# The following environment variables are used if set:
#
## CC - C compiler
## CXX - C++ compiler
## CCACHE - Set to "none" to disable automatic use of ccache
## CFLAGS - Additional C compiler flags
## CXXFLAGS - Additional C++ compiler flags
## LDFLAGS - Additional compiler flags during linking
## LIBS - Additional libraries to use (for all tests)
## CROSS - Tool prefix for cross compilation
#
# The following variables are defined from the corresponding
# environment variables if set.
#
## CPPFLAGS
## LINKFLAGS
## CC_FOR_BUILD
## LD
use system
module-options {}
# Note that the return code is not meaningful
proc cc-check-something {name code} {
uplevel 1 $code
}
# Checks for the existence of the given function by linking
#
proc cctest_function {function} {
cctest -link 1 -declare "extern void $function\(void);" -code "$function\();"
}
# Checks for the existence of the given type by compiling
proc cctest_type {type} {
cctest -code "$type _x;"
}
# Checks for the existence of the given type/structure member.
# e.g. "struct stat.st_mtime"
proc cctest_member {struct_member} {
lassign [split $struct_member .] struct member
cctest -code "static $struct _s; return sizeof(_s.$member);"
}
# Checks for the existence of the given define by compiling
#
proc cctest_define {name} {
cctest -code "#ifndef $name\n#error not defined\n#endif"
}
# Checks for the existence of the given name either as
# a macro (#define) or an rvalue (such as an enum)
#
proc cctest_decl {name} {
cctest -code "#ifndef $name\n(void)$name;\n#endif"
}
# @cc-check-sizeof type ...
#
# Checks the size of the given types (between 1 and 32, inclusive).
# Defines a variable with the size determined, or "unknown" otherwise.
# e.g. for type 'long long', defines SIZEOF_LONG_LONG.
# Returns the size of the last type.
#
proc cc-check-sizeof {args} {
foreach type $args {
msg-checking "Checking for sizeof $type..."
set size unknown
# Try the most common sizes first
foreach i {4 8 1 2 16 32} {
if {[cctest -code "static int _x\[sizeof($type) == $i ? 1 : -1\] = { 1 };"]} {
set size $i
break
}
}
msg-result $size
set define [feature-define-name $type SIZEOF_]
define $define $size
}
# Return the last result
get-define $define
}
# Checks for each feature in $list by using the given script.
#
# When the script is evaluated, $each is set to the feature
# being checked, and $extra is set to any additional cctest args.
#
# Returns 1 if all features were found, or 0 otherwise.
proc cc-check-some-feature {list script} {
set ret 1
foreach each $list {
if {![check-feature $each $script]} {
set ret 0
}
}
return $ret
}
# @cc-check-includes includes ...
#
# Checks that the given include files can be used
proc cc-check-includes {args} {
cc-check-some-feature $args {
cctest -includes $each
}
}
# @cc-check-types type ...
#
# Checks that the types exist.
proc cc-check-types {args} {
cc-check-some-feature $args {
cctest_type $each
}
}
# @cc-check-defines define ...
#
# Checks that the given preprocessor symbol is defined
proc cc-check-defines {args} {
cc-check-some-feature $args {
cctest_define $each
}
}
# @cc-check-decls name ...
#
# Checks that each given name is either a preprocessor symbol or rvalue
# such as an enum. Note that the define used for a decl is HAVE_DECL_xxx
# rather than HAVE_xxx
proc cc-check-decls {args} {
set ret 1
foreach name $args {
msg-checking "Checking for $name..."
set r [cctest_decl $name]
define-feature "decl $name" $r
if {$r} {
msg-result "ok"
} else {
msg-result "not found"
set ret 0
}
}
return $ret
}
# @cc-check-functions function ...
#
# Checks that the given functions exist (can be linked)
proc cc-check-functions {args} {
cc-check-some-feature $args {
cctest_function $each
}
}
# @cc-check-members type.member ...
#
# Checks that the given type/structure members exist.
# A structure member is of the form "struct stat.st_mtime"
proc cc-check-members {args} {
cc-check-some-feature $args {
cctest_member $each
}
}
# @cc-check-function-in-lib function libs ?otherlibs?
#
# Checks that the given given function can be found in one of the libs.
#
# First checks for no library required, then checks each of the libraries
# in turn.
#
# If the function is found, the feature is defined and lib_$function is defined
# to -l$lib where the function was found, or "" if no library required.
# In addition, -l$lib is added to the LIBS define.
#
# If additional libraries may be needed for linking, they should be specified
# as $extralibs as "-lotherlib1 -lotherlib2".
# These libraries are not automatically added to LIBS.
#
# Returns 1 if found or 0 if not.
#
proc cc-check-function-in-lib {function libs {otherlibs {}}} {
msg-checking "Checking libs for $function..."
set found 0
cc-with [list -libs $otherlibs] {
if {[cctest_function $function]} {
msg-result "none needed"
define lib_$function ""
incr found
} else {
foreach lib $libs {
cc-with [list -libs -l$lib] {
if {[cctest_function $function]} {
msg-result -l$lib
define lib_$function -l$lib
define-append LIBS -l$lib
incr found
break
}
}
}
}
}
if {$found} {
define [feature-define-name $function]
} else {
msg-result "no"
}
return $found
}
# @cc-check-tools tool ...
#
# Checks for existence of the given compiler tools, taking
# into account any cross compilation prefix.
#
# For example, when checking for "ar", first AR is checked on the command
# line and then in the environment. If not found, "${host}-ar" or
# simply "ar" is assumed depending upon whether cross compiling.
# The path is searched for this executable, and if found AR is defined
# to the executable name.
#
# It is an error if the executable is not found.
#
proc cc-check-tools {args} {
foreach tool $args {
set TOOL [string toupper $tool]
set exe [get-env $TOOL [get-define cross]$tool]
if {![find-executable $exe]} {
user-error "Failed to find $exe"
}
define $TOOL $exe
}
}
# @cc-check-progs prog ...
#
# Checks for existence of the given executables on the path.
#
# For example, when checking for "grep", the path is searched for
# the executable, 'grep', and if found GREP is defined as "grep".
#
# It the executable is not found, the variable is defined as false.
# Returns 1 if all programs were found, or 0 otherwise.
#
proc cc-check-progs {args} {
set failed 0
foreach prog $args {
set PROG [string toupper $prog]
msg-checking "Checking for $prog..."
if {![find-executable $prog]} {
msg-result no
define $PROG false
incr failed
} else {
msg-result ok
define $PROG $prog
}
}
expr {!$failed}
}
# Adds the given settings to $::autosetup(ccsettings) and
# returns the old settings.
#
proc cc-add-settings {settings} {
if {[llength $settings] % 2} {
autosetup-error "settings list is missing a value: $settings"
}
set prev [cc-get-settings]
# workaround a bug in some versions of jimsh by forcing
# conversion of $prev to a list
llength $prev
array set new $prev
foreach {name value} $settings {
switch -exact -- $name {
-cflags - -includes {
# These are given as lists
lappend new($name) {*}$value
}
-declare {
lappend new($name) $value
}
-libs {
# Note that new libraries are added before previous libraries
set new($name) [list {*}$value {*}$new($name)]
}
-link - -lang {
set new($name) $value
}
-source - -sourcefile - -code {
# XXX: These probably are only valid directly from cctest
set new($name) $value
}
default {
autosetup-error "unknown cctest setting: $name"
}
}
}
cc-store-settings [array get new]
return $prev
}
proc cc-store-settings {new} {
set ::autosetup(ccsettings) $new
}
proc cc-get-settings {} {
return $::autosetup(ccsettings)
}
# Similar to cc-add-settings, but each given setting
# simply replaces the existing value.
#
# Returns the previous settings
proc cc-update-settings {args} {
set prev [cc-get-settings]
array set new $prev
foreach {name value} $args {
set new($name) $value
}
cc-store-settings $new
return $prev
}
# @cc-with settings ?{ script }?
#
# Sets the given 'cctest' settings and then runs the tests in 'script'.
# Note that settings such as -lang replace the current setting, while
# those such as -includes are appended to the existing setting.
#
# If no script is given, the settings become the default for the remainder
# of the auto.def file.
#
## cc-with {-lang c++} {
## # This will check with the C++ compiler
## cc-check-types bool
## cc-with {-includes signal.h} {
## # This will check with the C++ compiler, signal.h and any existing includes.
## ...
## }
## # back to just the C++ compiler
## }
#
# The -libs setting is special in that newer values are added *before* earlier ones.
#
## cc-with {-libs {-lc -lm}} {
## cc-with {-libs -ldl} {
## cctest -libs -lsocket ...
## # libs will be in this order: -lsocket -ldl -lc -lm
## }
## }
proc cc-with {settings args} {
if {[llength $args] == 0} {
cc-add-settings $settings
} elseif {[llength $args] > 1} {
autosetup-error "usage: cc-with settings ?script?"
} else {
set save [cc-add-settings $settings]
set rc [catch {uplevel 1 [lindex $args 0]} result info]
cc-store-settings $save
if {$rc != 0} {
return $result -code [dict get $info -code]
}
return $result
}
}
# @cctest ?settings?
#
# Low level C compiler checker. Compiles and or links a small C program
# according to the arguments and returns 1 if OK, or 0 if not.
#
# Supported settings are:
#
## -cflags cflags A list of flags to pass to the compiler
## -includes list A list of includes, e.g. {stdlib.h stdio.h}
## -declare code Code to declare before main()
## -link 1 Don't just compile, link too
## -lang c|c++ Use the C (default) or C++ compiler
## -libs liblist List of libraries to link, e.g. {-ldl -lm}
## -code code Code to compile in the body of main()
## -source code Compile a complete program. Ignore -includes, -declare and -code
## -sourcefile file Shorthand for -source [readfile [get-define srcdir]/$file]
#
# Unless -source or -sourcefile is specified, the C program looks like:
#
## #include <firstinclude> /* same for remaining includes in the list */
##
## declare-code /* any code in -declare, verbatim */
##
## int main(void) {
## code /* any code in -code, verbatim */
## return 0;
## }
#
# Any failures are recorded in 'config.log'
#
proc cctest {args} {
set src conftest__.c
set tmp conftest__.o
# Easiest way to merge in the settings
cc-with $args {
array set opts [cc-get-settings]
}
if {[info exists opts(-sourcefile)]} {
set opts(-source) [readfile [get-define srcdir]/$opts(-sourcefile) "#error can't find $opts(-sourcefile)"]
}
if {[info exists opts(-source)]} {
set lines $opts(-source)
} else {
foreach i $opts(-includes) {
if {$opts(-code) ne "" && ![feature-checked $i]} {
# Compiling real code with an unchecked header file
# Quickly (and silently) check for it now
# Remove all -includes from settings before checking
set saveopts [cc-update-settings -includes {}]
msg-quiet cc-check-includes $i
cc-store-settings $saveopts
}
if {$opts(-code) eq "" || [have-feature $i]} {
lappend source "#include <$i>"
}
}
lappend source {*}$opts(-declare)
lappend source "int main(void) {"
lappend source $opts(-code)
lappend source "return 0;"
lappend source "}"
set lines [join $source \n]
}
# Build the command line
set cmdline {}
lappend cmdline {*}[get-define CCACHE]
switch -exact -- $opts(-lang) {
c++ {
lappend cmdline {*}[get-define CXX] {*}[get-define CXXFLAGS]
}
c {
lappend cmdline {*}[get-define CC] {*}[get-define CFLAGS]
}
default {
autosetup-error "cctest called with unknown language: $opts(-lang)"
}
}
if {!$opts(-link)} {
lappend cmdline -c
}
lappend cmdline {*}$opts(-cflags)
switch -glob -- [get-define host] {
*-*-darwin* {
# Don't generate .dSYM directories
lappend cmdline -gstabs
}
}
lappend cmdline $src -o $tmp {*}$opts(-libs)
# At this point we have the complete command line and the
# complete source to be compiled. Get the result from cache if
# we can
if {[info exists ::cc_cache($cmdline,$lines)]} {
msg-checking "(cached) "
set ok $::cc_cache($cmdline,$lines)
if {$::autosetup(debug)} {
configlog "From cache (ok=$ok): [join $cmdline]"
configlog "============"
configlog $lines
configlog "============"
}
return $ok
}
writefile $src $lines\n
set ok 1
if {[catch {exec-with-stderr {*}$cmdline} result errinfo]} {
configlog "Failed: [join $cmdline]"
configlog $result
configlog "============"
configlog "The failed code was:"
configlog $lines
configlog "============"
set ok 0
} elseif {$::autosetup(debug)} {
configlog "Compiled OK: [join $cmdline]"
configlog "============"
configlog $lines
configlog "============"
}
file delete $src
file delete $tmp
# cache it
set ::cc_cache($cmdline,$lines) $ok
return $ok
}
# @make-autoconf-h outfile ?auto-patterns=HAVE_*? ?bare-patterns=SIZEOF_*?
#
# Deprecated - see make-config-header
proc make-autoconf-h {file {autopatterns {HAVE_*}} {barepatterns {SIZEOF_* HAVE_DECL_*}}} {
user-notice "*** make-autoconf-h is deprecated -- use make-config-header instead"
make-config-header $file -auto $autopatterns -bare $barepatterns
}
# @make-config-header outfile ?-auto patternlist? ?-bare patternlist? ?-none patternlist? ?-str patternlist? ...
#
# Examines all defined variables which match the given patterns
# and writes an include file, $file, which defines each of these.
# Variables which match '-auto' are output as follows:
# - defines which have the value "0" are ignored.
# - defines which have integer values are defined as the integer value.
# - any other value is defined as a string, e.g. "value"
# Variables which match '-bare' are defined as-is.
# Variables which match '-str' are defined as a string, e.g. "value"
# Variables which match '-none' are omitted.
#
# Note that order is important. The first pattern which matches is selected
# Default behaviour is:
#
# -bare {SIZEOF_* HAVE_DECL_*} -auto HAVE_* -none *
#
# If the file would be unchanged, it is not written.
proc make-config-header {file args} {
set guard _[string toupper [regsub -all {[^a-zA-Z0-9]} [file tail $file] _]]
file mkdir [file dirname $file]
set lines {}
lappend lines "#ifndef $guard"
lappend lines "#define $guard"
# Add some defaults
lappend args -bare {SIZEOF_* HAVE_DECL_*} -auto HAVE_*
foreach n [lsort [dict keys [all-defines]]] {
set value [get-define $n]
set type [calc-define-output-type $n $args]
switch -exact -- $type {
-bare {
# Just output the value unchanged
}
-none {
continue
}
-str {
set value \"$value\"
}
-auto {
# Automatically determine the type
if {$value eq "0"} {
lappend lines "/* #undef $n */"
continue
}
if {![string is integer -strict $value]} {
set value \"$value\"
}
}
"" {
continue
}
default {
autosetup-error "Unknown type in make-config-header: $type"
}
}
lappend lines "#define $n $value"
}
lappend lines "#endif"
set buf [join $lines \n]
write-if-changed $file $buf {
msg-result "Created $file"
}
}
proc calc-define-output-type {name spec} {
foreach {type patterns} $spec {
foreach pattern $patterns {
if {[string match $pattern $name]} {
return $type
}
}
}
return ""
}
# Initialise some values from the environment or commandline or default settings
foreach i {LDFLAGS LIBS CPPFLAGS LINKFLAGS {CFLAGS "-g -O2"}} {
lassign $i var default
define $var [get-env $var $default]
}
if {[env-is-set CC]} {
# Set by the user, so don't try anything else
set try [list [get-env CC ""]]
} else {
# Try some reasonable options
set try [list [get-define cross]cc [get-define cross]gcc]
}
define CC [find-an-executable {*}$try]
if {[get-define CC] eq ""} {
user-error "Could not find a C compiler. Tried: [join $try ", "]"
}
define CPP [get-env CPP "[get-define CC] -E"]
# XXX: Could avoid looking for a C++ compiler until requested
# Note that if CXX isn't found, we just set it to "false". It might not be needed.
if {[env-is-set CXX]} {
define CXX [find-an-executable -required [get-env CXX ""]]
} else {
define CXX [find-an-executable [get-define cross]c++ [get-define cross]g++ false]
}
# CXXFLAGS default to CFLAGS if not specified
define CXXFLAGS [get-env CXXFLAGS [get-define CFLAGS]]
cc-check-tools ld
# May need a CC_FOR_BUILD, so look for one
define CC_FOR_BUILD [find-an-executable [get-env CC_FOR_BUILD ""] cc gcc false]
if {[get-define CC] eq ""} {
user-error "Could not find a C compiler. Tried: [join $try ", "]"
}
define CCACHE [find-an-executable [get-env CCACHE ccache]]
# Initial cctest settings
cc-store-settings {-cflags {} -includes {} -declare {} -link 0 -lang c -libs {} -code {}}
msg-result "C compiler...[get-define CCACHE] [get-define CC] [get-define CFLAGS]"
if {[get-define CXX] ne "false"} {
msg-result "C++ compiler...[get-define CCACHE] [get-define CXX] [get-define CXXFLAGS]"
}
msg-result "Build C compiler...[get-define CC_FOR_BUILD]"
if {![cc-check-includes stdlib.h]} {
user-error "Compiler does not work. See config.log"
}
|