Further improvements for C23 tests
[autoconf.git] / bin / autoscan.in
blob542865a997d9346752276ea49c62a0d2f4079a14
1 #! @PERL@
2 # -*- perl -*-
3 # @configure_input@
5 # autoscan - Create configure.scan (a preliminary configure.ac) for a package.
6 # Copyright (C) 1994, 1999-2017, 2020-2024 Free Software Foundation,
7 # Inc.
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 3 of the License, or
12 # (at your option) any later version.
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
22 # Written by David MacKenzie <djm@gnu.ai.mit.edu>.
24 eval 'case $# in 0) exec @PERL@ -S "$0";; *) exec @PERL@ -S "$0" "$@";; esac'
25     if 0;
27 use 5.006;
28 use strict;
29 use warnings FATAL => 'all';
31 BEGIN
33   my $pkgdatadir = $ENV{'autom4te_perllibdir'} || '@pkgdatadir@';
34   unshift @INC, $pkgdatadir;
36   # Override SHELL.  On DJGPP SHELL may not be set to a shell
37   # that can handle redirection and quote arguments correctly,
38   # e.g.: COMMAND.COM.  For DJGPP always use the shell that configure
39   # has detected.
40   $ENV{'SHELL'} = '@SHELL@' if ($^O eq 'dos');
43 use File::Basename;
44 use File::Find;
46 use Autom4te::ChannelDefs;
47 use Autom4te::Configure_ac;
48 use Autom4te::FileUtils;
49 use Autom4te::General;
50 use Autom4te::XFile;
52 my (@cfiles, @makefiles, @shfiles, @subdirs, %printed);
54 # The kind of the words we are looking for.
55 my @kinds = qw (function header identifier program
56                 makevar librarie);
58 # For each kind, the default macro.
59 my %generic_macro =
60   (
61    'function'   => 'AC_CHECK_FUNCS',
62    'header'     => 'AC_CHECK_HEADERS',
63    'identifier' => 'AC_CHECK_TYPES',
64    'program'    => 'AC_CHECK_PROGS',
65    'library'    => 'AC_CHECK_LIB'
66   );
68 my %kind_comment =
69   (
70    'function'   => 'Checks for library functions.',
71    'header'     => 'Checks for header files.',
72    'identifier' => 'Checks for typedefs, structures, and compiler characteristics.',
73    'program'    => 'Checks for programs.',
74   );
76 # $USED{KIND}{ITEM} is the list of locations where the ITEM (of KIND) was
77 # used in the user package.
78 # For instance $USED{function}{alloca} is the list of 'file:line' where
79 # 'alloca (...)' appears.
80 my %used = ();
82 # $MACRO{KIND}{ITEM} is the list of macros to use to test ITEM.
83 # Initialized from lib/autoscan/*.  E.g., $MACRO{function}{alloca} contains
84 # the singleton AC_FUNC_ALLOCA.  Some require several checks.
85 my %macro = ();
87 # $NEEDED_MACROS{MACRO} is an array of locations requiring MACRO.
88 # E.g., $NEEDED_MACROS{AC_FUNC_ALLOC} the list of 'file:line' containing
89 # 'alloca (...)'.
90 my %needed_macros =
91   (
92    'AC_PREREQ' => [$me],
93   );
95 my $log;
97 # Autoconf and lib files.
98 my $autom4te = $ENV{'AUTOM4TE'} || '@bindir@/@autom4te-name@';
99 my $autoconf = "$autom4te --language=autoconf";
100 my @prepend_include;
101 my @include = ('@pkgdatadir@');
103 # $help
104 # -----
105 $help = "Usage: $0 [OPTION]... [SRCDIR]
107 Examine source files in the directory tree rooted at SRCDIR, or the
108 current directory if none is given.  Search the source files for
109 common portability problems, check for incompleteness of
110 'configure.ac', and create a file 'configure.scan' which is a
111 preliminary 'configure.ac' for that package.
113   -h, --help          print this help, then exit
114   -V, --version       print version number, then exit
115   -v, --verbose       verbosely report processing
116   -d, --debug         don't remove temporary files
118 Library directories:
119   -B, --prepend-include=DIR  prepend directory DIR to search path
120   -I, --include=DIR          append directory DIR to search path
122 Report bugs to <bug-autoconf\@gnu.org>.
124 The full documentation for Autoconf can be read via 'info autoconf',
125 or on the Web at <https://www.gnu.org/software/autoconf/manual/>.
128 # $version
129 # --------
130 $version = "autoscan (@PACKAGE_NAME@) @VERSION@
131 Copyright (C) @RELEASE_YEAR@ Free Software Foundation, Inc.
132 License GPLv3+/Autoconf: GNU GPL version 3 or later
133 <https://gnu.org/licenses/gpl.html>, <https://gnu.org/licenses/exceptions.html>
134 This is free software: you are free to change and redistribute it.
135 There is NO WARRANTY, to the extent permitted by law.
137 Written by David J. MacKenzie and Akim Demaille.
143 ## ------------------------ ##
144 ## Command line interface.  ##
145 ## ------------------------ ##
147 # parse_args ()
148 # -------------
149 # Process any command line arguments.
150 sub parse_args ()
152   getopt ('I|include=s' => \@include,
153           'B|prepend-include=s' => \@prepend_include);
155   die "$me: too many arguments
156 Try '$me --help' for more information.\n"
157     if @ARGV > 1;
159   my $srcdir = $ARGV[0] || ".";
161   verb "srcdir = $srcdir";
162   chdir $srcdir || error "cannot cd to $srcdir: $!";
166 # init_tables ()
167 # --------------
168 # Put values in the tables of what to do with each token.
169 sub init_tables ()
171   # The data file format supports only one line of macros per function.
172   # If more than that is required for a common portability problem,
173   # a new Autoconf macro should probably be written for that case,
174   # instead of duplicating the code in lots of configure.ac files.
175   my $file = find_file ("autoscan/autoscan.list",
176                         reverse (@prepend_include), @include);
177   my $table = new Autom4te::XFile ($file, "<");
178   my $tables_are_consistent = 1;
180   while ($_ = $table->getline)
181     {
182       # Ignore blank lines and comments.
183       next
184         if /^\s*$/ || /^\s*\#/;
186       # '<kind>: <word> <macro invocation>' or...
187       # '<kind>: <word> warn: <message>'.
188       if (/^(\S+):\s+(\S+)\s+(\S.*)$/)
189         {
190           my ($kind, $word, $macro) = ($1, $2, $3);
191           error "$file:$.: invalid kind: $_"
192             unless grep { $_ eq $kind } @kinds;
193           push @{$macro{$kind}{$word}}, $macro;
194         }
195       else
196         {
197           error "$file:$.: invalid definition: $_";
198         }
199     }
201   if ($debug)
202     {
203       foreach my $kind (@kinds)
204         {
205           foreach my $word (sort keys %{$macro{$kind}})
206             {
207               print "$kind: $word: @{$macro{$kind}{$word}}\n";
208             }
209         }
211     }
215 # used ($KIND, $WORD, [$WHERE])
216 # -----------------------------
217 # $WORD is used as a $KIND.
218 sub used ($$;$)
220   my ($kind, $word, $where) = @_;
221   $where ||= "$File::Find::name:$.";
222   if (
223       # Check for all the libraries.  But '-links' is certainly a
224       # 'find' argument, and '-le', a 'test' argument.
225       ($kind eq 'library' && $word !~ /^(e|inks)$/)
226       # Other than libraries are to be checked only if listed in
227       # the Autoscan library files.
228       || defined $macro{$kind}{$word}
229      )
230     {
231       push (@{$used{$kind}{$word}}, $where);
232     }
237 ## ----------------------- ##
238 ## Scanning source files.  ##
239 ## ----------------------- ##
242 # scan_c_file ($FILE-NAME)
243 # ------------------------
244 sub scan_c_file ($)
246   my ($file_name) = @_;
247   push @cfiles, $File::Find::name;
249   # Nonzero if in a multiline comment.
250   my $in_comment = 0;
252   my $file = new Autom4te::XFile ($file_name, "<");
254   while ($_ = $file->getline)
255     {
256       # Strip out comments.
257       if ($in_comment && s,^.*?\*/,,)
258         {
259           $in_comment = 0;
260         }
261       # The whole line is inside a comment.
262       next if $in_comment;
263       # All on one line.
264       s,/\*.*?\*/,,g;
266       # Starting on this line.
267       if (s,/\*.*$,,)
268         {
269           $in_comment = 1;
270         }
272       # Preprocessor directives.
273       if (s/^\s*\#\s*//)
274         {
275           if (/^include\s*<([^>]*)>/)
276             {
277               used ('header', $1);
278             }
279           if (s/^(el)?if(n?def)?\s+//)
280             {
281               foreach my $word (split (/\W+/))
282                 {
283                   used ('identifier', $word)
284                     unless $word eq 'defined' || $word !~ /^[a-zA-Z_]/;
285                 }
286             }
287           # Ignore other preprocessor directives.
288           next;
289         }
291       # Remove string and character constants.
292       s,\"[^\"]*\",,g;
293       s,\'[^\']*\',,g;
295       # Tokens in the code.
296       # Maybe we should ignore function definitions (in column 0)?
297       while (s/\b([a-zA-Z_]\w*)\s*\(/ /)
298         {
299           used ('function', $1);
300         }
301       while (s/\b([a-zA-Z_]\w*)\b/ /)
302         {
303           used ('identifier', $1);
304         }
305     }
307   $file->close;
311 # scan_makefile($MAKEFILE-NAME)
312 # -----------------------------
313 sub scan_makefile ($)
315   my ($file_name) = @_;
316   push @makefiles, $File::Find::name;
318   my $file = new Autom4te::XFile ($file_name, "<");
320   while ($_ = $file->getline)
321     {
322       # Strip out comments.
323       s/#.*//;
325       # Variable assignments.
326       while (s/\b([a-zA-Z_]\w*)\s*=/ /)
327         {
328           used ('makevar', $1);
329         }
330       # Be sure to catch a whole word.  For instance 'lex$U.$(OBJEXT)'
331       # is a single token.  Otherwise we might believe 'lex' is needed.
332       foreach my $word (split (/\s+/))
333         {
334           # Libraries.
335           if ($word =~ /^-l([a-zA-Z_]\w*)$/)
336             {
337               used ('library', $1);
338             }
339           # Tokens in the code.
340           # We allow some additional characters, e.g., '+', since
341           # autoscan/programs includes 'c++'.
342           if ($word =~ /^[a-zA-Z_][\w+]*$/)
343             {
344               used ('program', $word);
345             }
346         }
347     }
349   $file->close;
353 # scan_sh_file($SHELL-SCRIPT-NAME)
354 # --------------------------------
355 sub scan_sh_file ($)
357   my ($file_name) = @_;
358   push @shfiles, $File::Find::name;
360   my $file = new Autom4te::XFile ($file_name, "<");
362   while ($_ = $file->getline)
363     {
364       # Strip out comments and variable references.
365       s/#.*//;
366       s/\$\{[^\}]*}//g;
367       s/@[^@]*@//g;
369       # Tokens in the code.
370       while (s/\b([a-zA-Z_]\w*)\b/ /)
371         {
372           used ('program', $1);
373         }
374     }
376   $file->close;
380 # scan_file ()
381 # ------------
382 # Called by &find on each file.  $_ contains the current file name with
383 # the current directory of the walk through.
384 sub scan_file ()
386   # Wanted only if there is no corresponding FILE.in.
387   return
388     if -f "$_.in";
390   # Save $_ as Find::File requires it to be preserved.
391   local $_ = $_;
393   # Strip a useless leading './'.
394   $File::Find::name =~ s,^\./,,;
396   if ($_ ne '.' and -d $_ and
397       -f "$_/configure.in"  ||
398       -f "$_/configure.ac"  ||
399       -f "$_/configure.gnu" ||
400       -f "$_/configure")
401     {
402       $File::Find::prune = 1;
403       push @subdirs, $File::Find::name;
404     }
405   if (/\.[chlym](\.in)?$/)
406     {
407       used 'program', 'cc', $File::Find::name;
408       scan_c_file ($_);
409     }
410   elsif (/\.(cc|cpp|cxx|CC|C|hh|hpp|hxx|HH|H|yy|ypp|ll|lpp)(\.in)?$/)
411     {
412       used 'program', 'c++', $File::Find::name;
413       scan_c_file ($_);
414     }
415   elsif ((/^((?:GNUm|M|m)akefile)(\.in)?$/ && ! -f "$1.am")
416          || /^(?:GNUm|M|m)akefile(\.am)?$/)
417     {
418       scan_makefile ($_);
419     }
420   elsif (/\.sh(\.in)?$/)
421     {
422       scan_sh_file ($_);
423     }
427 # scan_files ()
428 # -------------
429 # Read through the files and collect lists of tokens in them
430 # that might create nonportabilities.
431 sub scan_files ()
433   find (\&scan_file, '.');
435   if ($verbose)
436     {
437       print "cfiles: @cfiles\n";
438       print "makefiles: @makefiles\n";
439       print "shfiles: @shfiles\n";
441       foreach my $kind (@kinds)
442         {
443           print "\n$kind:\n";
444           foreach my $word (sort keys %{$used{$kind}})
445             {
446               print "$word: @{$used{$kind}{$word}}\n";
447             }
448         }
449     }
453 ## ----------------------- ##
454 ## Output configure.scan.  ##
455 ## ----------------------- ##
458 # output_kind ($FILE, $KIND)
459 # --------------------------
460 sub output_kind ($$)
462   my ($file, $kind) = @_;
463   # Lists of words to be checked with the generic macro.
464   my @have;
466   print $file "\n# $kind_comment{$kind}\n"
467     if exists $kind_comment{$kind};
468   foreach my $word (sort keys %{$used{$kind}})
469     {
470       # Output the needed macro invocations in configure.scan if not
471       # already printed, and remember these macros are needed.
472       foreach my $macro (@{$macro{$kind}{$word}})
473         {
474           if ($macro =~ /^warn:\s+(.*)/)
475             {
476               my $message = $1;
477               foreach my $location (@{$used{$kind}{$word}})
478                 {
479                   warn "$location: warning: $message\n";
480                 }
481             }
482           elsif (exists $generic_macro{$kind}
483               && $macro eq $generic_macro{$kind})
484             {
485               push (@have, $word);
486               push (@{$needed_macros{"$generic_macro{$kind}([$word])"}},
487                     @{$used{$kind}{$word}});
488             }
489           else
490             {
491               if (! $printed{$macro})
492                 {
493                   print $file "$macro\n";
494                   $printed{$macro} = 1;
495                 }
496               push (@{$needed_macros{$macro}},
497                     @{$used{$kind}{$word}});
498             }
499         }
500     }
501   print $file "$generic_macro{$kind}([" . join(' ', sort(@have)) . "])\n"
502     if @have;
506 # output_libraries ($FILE)
507 # ------------------------
508 sub output_libraries ($)
510   my ($file) = @_;
512   print $file "\n# Checks for libraries.\n";
513   foreach my $word (sort keys %{$used{'library'}})
514     {
515       print $file "# FIXME: Replace 'main' with a function in '-l$word':\n";
516       print $file "AC_CHECK_LIB([$word], [main])\n";
517     }
521 # output ($CONFIGURE_SCAN)
522 # ------------------------
523 # Print a proto configure.ac.
524 sub output ($)
526   my $configure_scan = shift;
527   my %unique_makefiles;
529   my $file = new Autom4te::XFile ($configure_scan, ">");
531   print $file
532     ("#                                               -*- Autoconf -*-\n" .
533      "# Process this file with autoconf to produce a configure script.\n" .
534      "\n" .
535      "AC_PREREQ([@VERSION@])\n" .
536      "AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS])\n");
537   if (defined $cfiles[0])
538     {
539       print $file "AC_CONFIG_SRCDIR([$cfiles[0]])\n";
540       print $file "AC_CONFIG_HEADERS([config.h])\n";
541     }
543   output_kind ($file, 'program');
544   output_kind ($file, 'makevar');
545   output_libraries ($file);
546   output_kind ($file, 'header');
547   output_kind ($file, 'identifier');
548   output_kind ($file, 'function');
550   print $file "\n";
551   if (@makefiles)
552     {
553       # Change DIR/Makefile.in to DIR/Makefile.
554       foreach my $m (@makefiles)
555         {
556           $m =~ s/\.(?:in|am)$//;
557           $unique_makefiles{$m}++;
558         }
559       print $file ("AC_CONFIG_FILES([",
560                    join ("\n                 ",
561                          sort keys %unique_makefiles), "])\n");
562     }
563   if (@subdirs)
564     {
565       print $file ("AC_CONFIG_SUBDIRS([",
566                    join ("\n                   ",
567                          sort @subdirs), "])\n");
568     }
569   print $file "AC_OUTPUT\n";
571   $file->close;
576 ## --------------------------------------- ##
577 ## Checking the accuracy of configure.ac.  ##
578 ## --------------------------------------- ##
581 # &check_configure_ac ($CONFIGURE_AC)
582 # -----------------------------------
583 # Use autoconf to check if all the suggested macros are included
584 # in CONFIGURE_AC.
585 sub check_configure_ac ($)
587   my ($configure_ac) = @_;
589   # Find what needed macros are invoked in CONFIGURE_AC.
590   # I'd be very happy if someone could explain to me why sort (uniq ...)
591   # doesn't work properly: I need 'uniq (sort ...)'.  --akim
592   my $trace_option =
593     join (' --trace=', '',
594           uniq (sort (map { s/\(.*//; $_ } keys %needed_macros)));
596   # Suppress all warnings from the subsidiary autoconf invocation.
597   local $ENV{WARNINGS} = 'none';
599   verb "running: WARNINGS=none $autoconf $trace_option $configure_ac";
600   my $traces =
601     new Autom4te::XFile "$autoconf $trace_option $configure_ac |";
603   while ($_ = $traces->getline)
604     {
605       chomp;
606       my ($file, $line, $macro, @args) = split (/:/, $_);
607       if ($macro =~ /^AC_CHECK_(HEADER|FUNC|TYPE|MEMBER)S$/)
608         {
609           # To be rigorous, we should distinguish between space and comma
610           # separated macros.  But there is no point.
611           foreach my $word (split (/\s|,/, $args[0]))
612             {
613               # AC_CHECK_MEMBERS wants 'struct' or 'union'.
614               if ($macro eq "AC_CHECK_MEMBERS"
615                   && $word =~ /^stat.st_/)
616                 {
617                   $word = "struct " . $word;
618                 }
619               delete $needed_macros{"$macro([$word])"};
620             }
621         }
622       else
623         {
624           delete $needed_macros{$macro};
625         }
626     }
628   $traces->close;
630   # Report the missing macros.
631   foreach my $macro (sort keys %needed_macros)
632     {
633       warn ("$configure_ac: warning: missing $macro wanted by: "
634             . (${$needed_macros{$macro}}[0])
635             . "\n");
636       print $log "$me: warning: missing $macro wanted by: \n";
637       foreach my $need (@{$needed_macros{$macro}})
638         {
639           print $log "\t$need\n";
640         }
641     }
645 ## -------------- ##
646 ## Main program.  ##
647 ## -------------- ##
649 parse_args;
650 $log = new Autom4te::XFile ("$me.log", ">");
652 $autoconf .= " --debug" if $debug;
653 $autoconf .= " --verbose" if $verbose;
654 $autoconf .= join (' --include=', '', map { shell_quote ($_) } @include);
655 $autoconf .= join (' --prepend-include=', '', map { shell_quote ($_) } @prepend_include);
657 my $configure_ac = find_configure_ac;
658 init_tables;
659 scan_files;
660 output ('configure.scan');
661 if (-f $configure_ac)
662   {
663     check_configure_ac ($configure_ac);
664   }
665 # This close is really needed.  For some reason, probably best named
666 # a bug, it seems that the dtor of $LOG is not called automatically
667 # at END.  It results in a truncated file.
668 $log->close;
669 exit 0;
671 ### Setup "GNU" style for perl-mode and cperl-mode.
672 ## Local Variables:
673 ## perl-indent-level: 2
674 ## perl-continued-statement-offset: 2
675 ## perl-continued-brace-offset: 0
676 ## perl-brace-offset: 0
677 ## perl-brace-imaginary-offset: 0
678 ## perl-label-offset: -2
679 ## cperl-indent-level: 2
680 ## cperl-brace-offset: 0
681 ## cperl-continued-brace-offset: 0
682 ## cperl-label-offset: -2
683 ## cperl-extra-newline-before-brace: t
684 ## cperl-merge-trailing-else: nil
685 ## cperl-continued-statement-offset: 2
686 ## End: