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