autoconf/tests/mktests.pl
2021-01-28 15:19:21 -05:00

416 lines
12 KiB
Perl

#! /usr/bin/perl
# Build some of the Autoconf test files.
# Copyright (C) 2000-2017, 2020-2021 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
use 5.006;
use strict;
use warnings FATAL => 'all';
# ac_exclude_list
# ---------------
# Check all AC_DEFUN'ed macros with AT_CHECK_MACRO, except these.
# Not every macro can be run without arguments, and some are already
# tested elsewhere.
my @ac_exclude_list = (
# Internal macros should not be invoked directly from a configure.ac.
qr/^_?_AC_/,
# Used in many places.
qr/^AC_.*_IFELSE$/,
qr/^AC_LANG/,
qr/^AC_RUN_LOG$/,
qr/^AC_TRY/,
# Need an argument.
qr/^AC_(CANONICALIZE|PREFIX_PROGRAM|PREREQ)$/,
qr/^AC_(SEARCH_LIBS|REPLACE_FUNCS)$/,
qr/^AC_(CACHE_CHECK|COMPUTE)_INT$/,
qr/^AC_ARG_VAR$/,
qr/^AC_REQUIRE_SHELL_FN$/,
# Performed in the semantics tests.
qr/^AC_CHECK_(
ALIGNOF|DECL|FILE|FUNC|HEADER|LIB|MEMBER
|PROG|SIZEOF|(TARGET_)?TOOL|TYPE
)S?$/x,
qr/^AC_PATH_PROGS_FEATURE_CHECK$/,
# Fail when the source does not exist.
qr/^AC_CONFIG/,
# AU defined to nothing.
qr/^AC_(CYGWIN|CYGWIN32|EMXOS2|MING32|EXEEXT|OBJEXT)$/,
# Produce "= val" because $1, the variable used to store the result,
# is empty.
qr/^AC_(F77|FC)_FUNC$/,
qr/^AC_FC_(PP_)?SRCEXT$/,
qr/^AC_PATH_((TARGET_)?TOOL|PROG)S?$/,
# Is a number.
qr/^AC_FD_CC$/,
# Obsolete, but needs to be AC_DEFUNed and cannot safely be called
# without arguments. Tested in tools.at.
qr/^AC_(DIAGNOSE|FATAL|FOREACH|OBSOLETE|WARNING)$/,
# Require a file that is not shipped with Autoconf. But it should.
qr/^AC_FUNC_(GETLOADAVG|FNMATCH_GNU)$/,
qr/^AC_REPLACE_FNMATCH$/,
# Obsolete, checked in semantics.
qr/^AC_FUNC_WAIT3$/,
qr/^AC_FUNC_SETVBUF_REVERSED$/,
qr/^AC_SYS_RESTARTABLE_SYSCALLS$/,
# Not intended to be invoked at the top level.
qr/^AC_INCLUDES_DEFAULT$/,
# AC_INIT includes all the AC_INIT macros.
# There is an infinite m4 recursion if AC_INIT is used twice.
qr/^AC_INIT/,
# Checked in semantics.
qr/^AC_(PROG_CC|C_CONST|C_VOLATILE)$/,
qr/^AC_PATH_XTRA$/,
# Use without an argument is obsolete.
# Checked in semantics.
qr/^AC_PROG_LEX$/,
# Requires a working C++ compiler, which is not a given.
qr/^AC_PROG_CXX_C_O$/,
# Already tested by AT_CHECK_MACRO.
qr/^AC_OUTPUT$/,
# Tested alongside m4_divert_text.
qr/^AC_PRESERVE_HELP_ORDER$/,
# Tested in erlang.at.
qr/^AC_ERLANG_SUBST_(INSTALL_LIB_SUBDIR|ROOT_DIR)$/,
qr/^AC_ERLANG_CHECK_LIB$/,
);
# au_exclude_list
# ---------------
# Check all AU_DEFUN'ed macros with AT_CHECK_AU_MACRO, except these.
my @au_exclude_list = (
# Empty.
qr/^AC_(C_CROSS|PROG_CC_(C[89]9|STDC))$/,
# Use AC_REQUIRE.
qr/^AC_(CYGWIN|MINGW32|EMXOS2)$/,
# Already in configure.ac.
qr/^AC_(INIT|OUTPUT)$/,
# AC_LANG_SAVE needs user interaction to be removed.
# AC_LANG_RESTORE cannot be used alone.
qr/^AC_LANG_(SAVE|RESTORE)$/,
# Need arguments. Tested in tools.at.
qr/^AC_(DIAGNOSE|FATAL|OBSOLETE|WARNING)$/,
qr/^AC_(FOREACH|LINK_FILES|PREREQ)$/,
# Need arguments. Tested in semantics.at.
qr/^AC_HAVE_LIBRARY$/,
qr/^AC_COMPILE_CHECK$/,
qr/^AC_TRY_(COMPILE|CPP|LINK|RUN)$/,
# Not macros, just mapping from old variable name to a new one.
qr/^ac_cv_prog_(gcc|gxx|g77)$/,
);
# test_parameters
# ---------------
# Extra arguments to pass to the test macro for particular macros.
# Keys are macro names, values are records containing one or more
# of the possible optional arguments to AT_CHECK_(AU_)MACRO:
# macro_use, additional_commands, autoconf_flags, test_parameters => '...'.
# Entries in this hash are grouped by common situations, and sorted
# alphabetically within each group.
# Note that you must provide M4 quotation; emit_test will not quote
# the arguments for you. (This is so you can _not_ quote the arguments
# when that's useful.)
my %test_parameters = (
# Uses AC_RUN_IFELSE, cross-compilation test fails.
AC_FC_CHECK_BOUNDS => { test_parameters => '[no-cross]' },
AC_FUNC_CHOWN => { test_parameters => '[no-cross]' },
AC_FUNC_FNMATCH => { test_parameters => '[no-cross]' },
AC_FUNC_FORK => { test_parameters => '[no-cross]' },
AC_FUNC_GETGROUPS => { test_parameters => '[no-cross]' },
AC_FUNC_LSTAT => { test_parameters => '[no-cross]' },
AC_FUNC_MALLOC => { test_parameters => '[no-cross]' },
AC_FUNC_MEMCMP => { test_parameters => '[no-cross]' },
AC_FUNC_MKTIME => { test_parameters => '[no-cross]' },
AC_FUNC_MMAP => { test_parameters => '[no-cross]' },
AC_FUNC_REALLOC => { test_parameters => '[no-cross]' },
AC_FUNC_STAT => { test_parameters => '[no-cross]' },
AC_FUNC_STRCOLL => { test_parameters => '[no-cross]' },
AC_FUNC_STRNLEN => { test_parameters => '[no-cross]' },
AC_FUNC_STRTOD => { test_parameters => '[no-cross]' },
# Different result with a C++ compiler than a C compiler:
# C++ compilers may or may not support these features from C1999 and later.
AC_C_RESTRICT => {
test_parameters => ('[cxx_define_varies:restrict' .
' cxx_cv_varies:cxx_restrict]')
},
AC_C_TYPEOF => {
test_parameters => ('[cxx_define_varies:typeof' .
' cxx_define_varies:HAVE_TYPEOF' .
' cxx_cv_varies:cxx_typeof]')
},
AC_C__GENERIC => {
test_parameters => ('[cxx_define_varies:HAVE_C__GENERIC' .
' cxx_cv_varies:cxx__Generic]')
},
AC_C_VARARRAYS => {
test_parameters => ('[cxx_define_varies:HAVE_C_VARARRAYS' .
' cxx_define_varies:__STDC_NO_VLA__' .
' cxx_cv_varies:cxx_vararrays]')
},
# stdbool.h is supposed to be includeable from C++, per C++2011
# [support.runtime], but the type _Bool was not added to the C++
# standard, so it may or may not be present depending on how much
# the C++ compiler cares about C source compatibility.
AC_CHECK_HEADER_STDBOOL => {
test_parameters => ('[cxx_define_varies:HAVE__BOOL' .
' cxx_cv_varies:type__Bool]')
},
AC_HEADER_STDBOOL => {
test_parameters => ('[cxx_define_varies:HAVE__BOOL' .
' cxx_cv_varies:type__Bool]')
},
# G++ forces -D_GNU_SOURCE which, with some versions of GNU libc,
# changes the declaration of strerror_r. Blech.
AC_FUNC_STRERROR_R => {
test_parameters => ('[cxx_define_varies:STRERROR_R_CHAR_P' .
' cxx_cv_varies:func_strerror_r_char_p]')
},
);
# skip_macro MACRO, EXCLUDE-LIST
# ------------------------------
# Returns truthy if any of the regexes in EXCLUDE-LIST match MACRO.
sub skip_macro
{
my $macro = shift;
for my $pat (@_)
{
return 1 if $macro =~ $pat;
}
return 0;
}
# emit_test FH, TEST-MACRO, MACRO
# --------------------------------
# Emit code to FH to test MACRO using TEST-MACRO.
# TEST-MACRO is expected to be either AT_CHECK_MACRO or AT_CHECK_AU_MACRO;
# see local.at.
sub emit_test
{
my ($fh, $test_macro, $macro) = @_;
my $params = $test_parameters{$macro} || {};
my $macro_use = ${$params}{macro_use} || '';
my $additional_commands = ${$params}{additional_commands} || '';
my $autoconf_flags = ${$params}{autoconf_flags} || '';
my $test_parameters = ${$params}{test_parameters} || '';
$autoconf_flags = '[]'
if $autoconf_flags eq '' && $test_parameters ne '';
$additional_commands = '[]'
if $additional_commands eq '' && $autoconf_flags ne '';
$macro_use = '[]'
if $macro_use eq '' && $additional_commands ne '';
print $fh "$test_macro([$macro]";
print $fh ", $autoconf_flags" if $autoconf_flags ne '';
print $fh ", $additional_commands" if $additional_commands ne '';
print $fh ", $autoconf_flags" if $autoconf_flags ne '';
print $fh ", $test_parameters" if $test_parameters ne '';
print $fh ")\n";
}
# scan_m4_files
# -------------
# Scan all of the Autoconf source files and produce lists of macros
# to be tested
sub scan_m4_files
{
my @macros_to_test;
my %required_macros;
for my $file (@_)
{
open my $fh, "<", $file
or die "$file: $!\n";
my (@ac_macros, @au_macros);
while (<$fh>)
{
chomp;
s/\bdnl\b.*$//;
if (/\bAC_REQUIRE\(\[*([a-zA-Z0-9_]+)/)
{
$required_macros{$1} = 1;
}
elsif (/^AC_DEFUN(?:_ONCE)?\(\[*([a-zA-Z0-9_]+)/)
{
push @ac_macros, $1
unless skip_macro $1, @ac_exclude_list;
}
elsif (/^AU_DEFUN?\(\[*([a-zA-Z0-9_]+)/)
{
push @au_macros, $1
unless skip_macro $1, @au_exclude_list;
}
}
push @macros_to_test, [ $file, \@ac_macros, \@au_macros ];
}
# Filter out macros that are AC_REQUIREd by some other macro;
# it's not necessary to test them directly.
my @pruned_macros_to_test;
for my $elt (@macros_to_test)
{
my ($file, $ac_macros, $au_macros) = @$elt;
my (@pruned_ac_macros, @pruned_au_macros);
for my $macro (@$ac_macros)
{
push @pruned_ac_macros, $macro
unless defined $required_macros{$macro};
}
for my $macro (@$au_macros)
{
push @pruned_au_macros, $macro
unless defined $required_macros{$macro};
}
push @pruned_macros_to_test, [
$file, \@pruned_ac_macros, \@pruned_au_macros
];
}
return @pruned_macros_to_test;
}
# make_read_only FILE
# -------------------
# Make FILE read-only on disk. Also clears the execute and special bits.
sub make_read_only
{
my $f = shift;
my $mode = (stat $f)[2];
die "stat($f): $!\n" unless defined $mode;
# clear all the bits in $mode except r--r--r--
$mode &= 00444;
chmod $mode, $f or die "making $f read-only: $!\n";
}
# create_test_files OUTDIR, M4-FILES...
# -----------------
# Main loop: for each file listed in M4-FILES, generate an .at file
# named "ac${file}.at" that does cursory tests on each of the macros
# defined in $file.
sub create_test_files
{
my $outdir = shift;
if (@_ == 0)
{
print STDERR "usage: $0 outdir m4-files...\n";
exit 1;
}
for my $elt (scan_m4_files @_)
{
my ($file, $ac_macros, $au_macros) = @$elt;
my $base = $file;
$base =~ s|^.*/([^/.]+)(?:.[^/]*)?$|$1|;
my $tmpout = "${outdir}/ac${base}.tat";
my $out = "${outdir}/ac${base}.at";
open my $fh, ">", $tmpout
or die "$tmpout: $!\n";
print $fh <<"EOF";
# -*- autotest -*-
# Generated by $0 from $file.
# Do not edit this file by hand.
EOF
if (@$ac_macros || @$au_macros)
{
print $fh "AT_BANNER([Testing autoconf/$base macros.])\n";
if (@$ac_macros)
{
print $fh "\n# Modern macros.\n";
emit_test ($fh, 'AT_CHECK_MACRO', $_)
for sort @$ac_macros;
}
if (@$au_macros)
{
print $fh "\n# Obsolete macros.\n";
emit_test ($fh, 'AT_CHECK_AU_MACRO', $_)
for sort @$au_macros;
}
}
else
{
print $fh "# Nothing to test.\n";
}
close $fh or die "writing to $tmpout: $!\n";
make_read_only $tmpout;
rename $tmpout, $out or die "updating $out: $!\n";
}
}
create_test_files @ARGV;
exit 0;
### Setup "GNU" style for perl-mode and cperl-mode.
## Local Variables:
## perl-indent-level: 2
## perl-continued-statement-offset: 2
## perl-continued-brace-offset: 0
## perl-brace-offset: 0
## perl-brace-imaginary-offset: 0
## perl-label-offset: -2
## cperl-indent-level: 2
## cperl-brace-offset: 0
## cperl-continued-brace-offset: 0
## cperl-label-offset: -2
## cperl-extra-newline-before-brace: t
## cperl-merge-trailing-else: nil
## cperl-continued-statement-offset: 2
## End: