MarpaX-ESLIF

 view release on metacpan or  search on metacpan

etc/compile_marpaESLIF.pl  view on Meta::CPAN

# directory that is first purged.
#
# Eventual extra includes are generated in inc/include directory.
#
# Object files are in objs directory
#
# Library files are in libs directory
#

my $have_cppguess;
BEGIN {
    use File::Spec;                     # Formally it is not necessary I believe to do it here
    # Make sure we have our 'inc' directory prepended the perl search path
    my $inc_dir = File::Spec->catdir(File::Spec->curdir, 'inc');
    unshift(@INC, $inc_dir);
    #
    # ExtUtils::CppGuess does not install everywhere.
    # This is why we provide it explicitely, we are ok if it fails at run-time
    # by enclosing its usage in try/catch
    #
    $have_cppguess = eval 'use ExtUtils::CppGuess 0.26; 1;';
}

use Archive::Tar;
use Carp qw/croak/;
use Config;
use Config::AutoConf;
use Cwd qw/abs_path/;
use ExtUtils::CBuilder 0.280224; # 0.280224 is to make sure we have the support of $ENV{CXX};
use File::chdir;
use File::Basename;
use File::Copy;
use File::Find;
use File::Path qw/make_path remove_tree/;
use File::Spec;
use File::Which;
use File::Temp;
use IO::Handle;
use Perl::OSType qw/is_os_type/;
use POSIX qw/EXIT_SUCCESS/;
use Try::Tiny;

our $EXTRA_INCLUDE_DIR = File::Spec->catdir('inc', 'include');
our $CONFIG_H = File::Spec->catfile($EXTRA_INCLUDE_DIR, 'marpaESLIFPerl_autoconf.h'); # The file that we generate
our $EXTRACT_DIR = 'extract';
our $TARBALLS_DIR = File::Spec->catdir('etc', 'tarballs');
our $OBJS_DIR = 'objs';
our $IS_WINDOWS = is_os_type('Windows');

$| = 1;

#
# Our distribution have both C and CPP files, and we want to make sure that modifying
# CFLAGS will not affect cpp files. Since we require a version of ExtUtils::CBuilder
# that support the environment variables, explicitely setting the environment variables
# from default ExtUtils::Cbuilder will ensure cc and cpp settings will become independant
# if we are doing to modify any of them.
# We do that for linker settings as well for coherency although we will NEVER touch them.
# OTHERLDFLAGS will be specific to this makefile.
#
# Take care: with ExtUtils::CBuilder, $ENV{CFLAGS} and $ENV{LDFLAGS} are appended to default perl compile flags, not the others
#
#
my %cbuilder_config = ExtUtils::CBuilder->new()->get_config;
$ENV{CC} = $cbuilder_config{cc} // 'cc';
$ENV{CFLAGS} //= '';
$ENV{CFLAGS} .= ' -DNDEBUG -DNTRACE';
$ENV{CXX} = $cbuilder_config{cxx} // $ENV{CC};
$ENV{CXXFLAGS} = $cbuilder_config{cxxflags} // $cbuilder_config{ccflags} // '';
$ENV{LD} = $cbuilder_config{ld} // $ENV{CC};
$ENV{LDFLAGS} //= '';
my @OTHERLDFLAGS = ();
my $optimize = '';

print "==========================================\n";
print "Original compilers and linker settings as per ExtUtils::CBuilder\n";
print "\n";
print "CC           (overwrite) $ENV{CC}\n";
print "CFLAGS       (    fixed) " . ($cbuilder_config{ccflags} // '') . "\n";
print "CFLAGS       (   append) $ENV{CFLAGS}\n";
print "CXX          (overwrite) $ENV{CXX}\n";
print "CXXFLAGS     (overwrite) $ENV{CXXFLAGS}\n";
print "LD           (overwrite) $ENV{LD}\n";
print "LDFLAGS      (    fixed) " . ($cbuilder_config{ldflags} // '') . "\n";
print "LDFLAGS      (   append) $ENV{LDFLAGS}\n";
print "==========================================\n";
print "\n";

my $ac = Config::AutoConf->new();
# goto jdd;
$ac->check_cc;
#
# Guess CXX configuration
#
# Sun C compiler is a special case, we know that guess_compiler will always get it wrong
#
my $sunc = 0;
my $is_gnu = check_compiler_is_gnu($ac);
my $is_clang = check_compiler_is_clang($ac);
$ac->msg_checking(sprintf "if this is Sun C compiler");
if ($ac->link_if_else("#ifdef __SUNPRO_C\n#else\n#error \"this is not Sun C compiler\"\n#endif\nint main() { return 0; }")) {
    $ac->msg_result('yes');
    my $cc = which($ENV{CC}) || '';
    if (! $cc) {
        #
        # Should never happen since we checked that the compiler works
        #
        $ac->msg_notice("Warning! Sun C compiler working but which() on its location returned false !?");
    } else {
        #
        # $cc should be a full path
        #
        $cc = abs_path($cc);
        my $ccdir = dirname($cc) || File::Spec->curdir();
        #
        # We always give precedence to CC that should be at the same location of the C compiler
        #
        my $cxx = File::Spec->catfile($ccdir, 'CC');
        if (! which($cxx)) {
            #
            # No CC at the same location?
            #
            $ac->msg_notice("Warning! Sun C compiler detected but no CC found at the same location - trying with default search path");
            $cxx = 'CC';
        } else {
            #
            # Could it be that this CC is also the one that is, eventually, in the path?
            #
            my $cxxfromPATH = which('CC') || '';
            if ($cxxfromPATH) {
                $cxxfromPATH = abs_path($cxxfromPATH);
                my $cxxfromWhich = abs_path($cxx);
                if ($cxxfromWhich eq $cxxfromPATH) {
                    $ac->msg_notice("Sun C compiler detected and its CC counterpart is already in the search path");
                    $cxx = 'CC';
                }
            }
        }
        if (which($cxx)) {
            $ac->msg_notice("Forcing CXX to $cxx");
            $ENV{CXX} = $cxx;
            #
            # We got "CC" executable - no need of eventual -x c++ that perl may have add
            #
            if ($ENV{CXXFLAGS} =~ s/\-x\s+c\+\+\s*//) {

etc/compile_marpaESLIF.pl  view on Meta::CPAN

		if (try_compile("#include <stdlib.h>\nint main() {\n  exit(0);\n}\n", { extra_compiler_flags => $flag })) {
		    $ac->msg_result('yes');
		    $optimize = $flag;
		    last;
		} else {
		    $ac->msg_result('no');
		}
	    }
	} else {
	    #
	    # Some versions of gcc may not yell with bad options unless -Werror is set.
	    # Check that flag and set it temporarly.
	    #
	    my $tmpflag = '-Werror';
	    $ac->msg_checking("if flag $tmpflag works:");
	    if (try_compile("#include <stdlib.h>\nint main() {\n  exit(0);\n}\n", { extra_compiler_flags => $tmpflag })) {
		$ac->msg_result('yes');
		$has_Werror = 1;
	    } else {
		$ac->msg_result('no');
		$tmpflag = '';
	    }
	    #
	    # We test AIX case first because it overlaps with general O3
	    #
            my @flag_candidates = ();
            if ($sunc) {
                push(@flag_candidates, "-xO3"); # CC
            } elsif ($is_gnu || $is_clang) {
                push(@flag_candidates, "-O3"); # gcc, clang
            } else {
                push(@flag_candidates, "-O3 -qstrict"); # xlc maybe ?
                push(@flag_candidates, "-O3"); # Others
            }
	    foreach my $flag (@flag_candidates) {
		$ac->msg_checking("if flag $flag works:");
		if (try_compile("#include <stdlib.h>\nint main() {\n  exit(0);\n}\n", { extra_compiler_flags => "$tmpflag $flag" })) {
		    $ac->msg_result('yes');
		    $optimize = $flag;
		    last;
		} else {
		    $ac->msg_result('no');
		}
	    }
	}
    }
}

my $OTHERLDFLAGS = join(' ', @OTHERLDFLAGS);
print "\n";
print "==========================================\n";
print "Tweaked compilers and linker settings\n";
print "\n";
print "CC           (overwrite) $ENV{CC}\n";
print "CFLAGS       (    fixed) " . ($cbuilder_config{ccflags} // '') . "\n";
print "CFLAGS       (   append) $ENV{CFLAGS}\n";
print "CXX          (overwrite) $ENV{CXX}\n";
print "CXXFLAGS     (overwrite) $ENV{CXXFLAGS}\n";
print "LD           (overwrite) $ENV{LD}\n";
print "LDFLAGS      (    fixed) " . ($cbuilder_config{ldflags} // '') . "\n";
print "LDFLAGS      (   append) $ENV{LDFLAGS}\n";
print "OTHERLDFLAGS             $OTHERLDFLAGS\n";
print "==========================================\n";
print "\n";

my %HAVE_HEADERS = ();
$ac->check_all_headers(
    qw{
        ctype.h
            dlfcn.h
            errno.h
            fcntl.h
            features.h
            float.h
            inttypes.h
            io.h
            langinfo.h
            limits.h
            locale.h
            math.h
            memory.h
            poll.h
            process.h
            pwd.h
            regex.h
            signal.h
            stdarg.h
            stddef.h
            stdint.h
            stdio.h
            stdlib.h
            string.h
            strings.h
            sys/inttypes.h
            sys/stat.h
            sys/stdint.h
            sys/time.h
            sys/types.h
            time.h
            unistd.h
            utime.h
            wchar.h
            wctype.h
            windows.h
    }, { action_on_header_true => sub { my $header = shift; $HAVE_HEADERS{$header} = 1 } } );
#
# Private extra checks that Config::AutoConf::INI cannot do
#
check_math($ac);
check_ebcdic($ac);
check_inline($ac);
check_forceinline($ac);
check_va_copy($ac);
check_vsnprintf($ac);
check_fileno($ac);
check_localtime_r($ac);
check_write($ac);
check_log2($ac);
check_log2f($ac);
my $char_bit = check_CHAR_BIT($ac);
check_strtold($ac);

etc/compile_marpaESLIF.pl  view on Meta::CPAN

#
# Write config file
#
print "Generating $CONFIG_H\n";
$ac->write_config_h($CONFIG_H);
$ac->msg_notice("Append -I$EXTRA_INCLUDE_DIR to compile flags");
$ENV{CFLAGS} .= " -I$EXTRA_INCLUDE_DIR";
$ENV{CXXFLAGS} .= " -I$EXTRA_INCLUDE_DIR";
#
# Generate extra headers eventually
#
if (! $HAVE_HEADERS{"stdint.h"}) {
    configure_file($ac, File::Spec->catfile('etc', 'stdint.h.in'), File::Spec->catfile($EXTRA_INCLUDE_DIR, 'stdint.h'));
}
if (! $HAVE_HEADERS{"inttypes.h"}) {
    configure_file($ac, File::Spec->catfile('etc', 'inttypes.h.in'), File::Spec->catfile($EXTRA_INCLUDE_DIR, 'inttypes.h'));
}

#
# General flags that we always set
#
foreach my $flag (qw/_REENTRANT _THREAD_SAFE/) {
    $ac->msg_notice("Append $flag to compile flags");
    $ENV{CFLAGS} .= " -D$flag";
    $ENV{CXXFLAGS} .= " -D$flag";
}
#
# Specific flags for cl
#
if ($ENV{CC} =~ /\bcl\b/) {
    foreach my $flag (qw/WIN32_LEAN_AND_MEAN CRT_SECURE_NO_WARNINGS _CRT_NONSTDC_NO_DEPRECATE/) {
        $ac->msg_notice("Append $flag to compile flags");
        $ENV{CFLAGS} .= " -D$flag";
        $ENV{CXXFLAGS} .= " -D$flag";
    }
}
#
# Extract and process tarballs in an order that we know in advance
#
sub get_object_file {
    my ($source) = @_;

    return File::Spec->catfile($OBJS_DIR, sprintf("%s.o", basename($source)));
}

process_genericLogger($ac);
process_tconv($ac);
process_genericStack($ac);
process_genericHash($ac);
process_genericSparseArray($ac);
process_marpaWrapper($ac);
process_marpaESLIF($ac);
#
# Write LDFLAGS and OTHERLDFLAGS to OTHERLDFLAGS.txt
#
my $otherldflags = 'OTHERLDFLAGS.txt';
open(my $otherldflags_fd, '>', $otherldflags) || die "Cannot open $otherldflags, $!";
foreach (@OTHERLDFLAGS) {
    print $otherldflags_fd "$_\n";
}
print $otherldflags_fd "$ENV{LDFLAGS}\n";
close($otherldflags_fd) || warn "Cannot close $otherldflags, $!";

#
# Write CFLAGS to CFLAGS.txt
#
my $cflags = 'CFLAGS.txt';
open(my $cflags_fd, '>', $cflags) || die "Cannot open $cflags, $!";
print $cflags_fd "$ENV{CFLAGS}\n";
close($cflags_fd) || warn "Cannot close $cflags, $!";

exit(EXIT_SUCCESS);

sub guess_compiler {
  my ($ac) = @_;

  my $guesser = ExtUtils::CppGuess->new(cc => $ENV{CC});
  #
  # We work quite like Module::Build in the sense that we are appending
  # flags.
  #
  my %module_build_options = $guesser->module_build_options;
  my $cxx_guess            = $module_build_options{config}->{cc} // '';
  my $extra_cxxflags_guess = $module_build_options{extra_compiler_flags} // '';
  my $extra_ldflags_guess  = $module_build_options{extra_linker_flags} // '';
  $ac->msg_notice("ExtUtils::CppGuess says \$cxx_guess=$cxx_guess, \$extra_cxxflags_guess=$extra_cxxflags_guess, \$extra_ldflags_guess=$extra_ldflags_guess");

  return ($cxx_guess, $extra_cxxflags_guess, $extra_ldflags_guess);
}

sub try_compile {
    no warnings 'once';
    
    my ($csource, $options) = @_;

    $options //= {};
    my $extra_compiler_flags = $options->{extra_compiler_flags};
    my $link = $options->{link} // 0;
    my $run = $options->{run} // 0;
    my $cbuilder_extra_config = $options->{cbuilder_extra_config};
    my $output_ref = $options->{output_ref};
    my $silent = $options->{silent} // $ENV{COMPILE_MARPAESLIF_SILENT} // 1;
    my $quiet = $options->{quiet} // $ENV{COMPILE_MARPAESLIF_QUIET} // 1;
    my $compile_error_is_fatal = $options->{compile_error_is_fatal} // 0;
    my $link_error_is_fatal = $options->{link_error_is_fatal} // 0;
    my $run_error_is_fatal = $options->{run_error_is_fatal} // 0;
    my $cplusplus = $options->{'C++'} // 0;

    my $stderr_and_stdout_txt = "stderr_and_stdout.txt";
    #
    # We do not want to be polluted in any case, redirect stdout and stderr
    # to the same output using method as per perlfunc open
    #
    open(my $oldout, ">&STDOUT") or die "Can't dup STDOUT: $!";
    open(OLDERR, ">&", \*STDERR) or die "Can't dup STDERR: $!";
    if ($silent) {
        open(STDOUT, '>', $stderr_and_stdout_txt) or die "Can't redirect STDOUT: $!";
        open(STDERR, ">&STDOUT") or die "Can't dup STDOUT: $!";
        select STDERR; $| = 1;  # make unbuffered
        select STDOUT; $| = 1;  # make unbuffered
    }

etc/compile_marpaESLIF.pl  view on Meta::CPAN

        if ($exe_file) {
            unlink($exe_file);
        }
    };
    unlink $fh->filename;

    open(STDOUT, ">&", $oldout) or die "Can't dup \$oldout: $!";
    open(STDERR, ">&OLDERR") or die "Can't dup OLDERR: $!";
    unlink $stderr_and_stdout_txt;

    if ($have_compile_error && $compile_error_is_fatal) {
        die "Compilation error and this is fatal";
    }
    if ($have_link_error && $link_error_is_fatal) {
        die "Link error and this is fatal";
    }
    if ($have_run_error && $run_error_is_fatal) {
        die "Run error and this is fatal";
    }

    return $rc;
}

sub try_link {
    my ($csource, $options) = @_;

    $options //= {};

    return try_compile($csource, { %$options, link => 1 });
}

sub try_run {
    my ($csource, $options) = @_;

    $options //= {};

    return try_compile($csource, { %$options, link => 1, run => 1 });
}

sub try_output {
    my ($csource, $output_ref, $options) = @_;

    $options //= {};

    return try_compile($csource, { %$options, link => 1, run => 1, output_ref => $output_ref });
}

sub check_math {
    my ($ac) = @_;

    #
    # log/exp and math lib
    #
    my $lm = $ac->check_lm() // '';
    $ac->msg_checking("for math library:");
    if($lm) {
	$ac->msg_result("$lm");
	$ac->search_libs('log', $lm, { action_on_true => $ac->define_var("HAVE_LOG", 1) });
	$ac->search_libs('exp', $lm, { action_on_true => $ac->define_var("HAVE_EXP", 1) });
        $ac->msg_notice("Append -l$lm to LDFLAGS");
	$ENV{LDFLAGS} .= " -l$lm";
    } else {
	$ac->msg_result("not needed");
	$ac->search_libs('log', { action_on_true => $ac->define_var("HAVE_LOG", 1) });
	$ac->search_libs('exp', { action_on_true => $ac->define_var("HAVE_EXP", 1) });
    }
}

sub check_ebcdic {
    my ($ac) = @_;
    #
    # EBCDIC (We could have used $Config{ebcdic} as well
    # --------------------------------------------------
    $ac->msg_checking("EBCDIC");
    my $prologue = <<PROLOGUE;
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
PROLOGUE
    my $body = <<BODY;
if ('M'==0xd4) {
  exit(0);
} else {
  exit(1);
}
BODY
    my $program = $ac->lang_build_program($prologue, $body);
    if (try_run($program)) {
	$ac->msg_result("yes");
	$ac->define_var("EBCDIC", 1)
    } else {
	$ac->msg_result("no");
    }
}

sub check_inline {
    my ($ac) = @_;

    foreach my $value (qw/inline __inline__ inline__ __inline/) {
	$ac->msg_checking($value);
	my $prologue = <<PROLOGUE;
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif

typedef int foo_t;
static $value foo_t static_foo() {
  return 0;
}
foo_t foo() {
  return 0;
}
PROLOGUE
	my $program = $ac->lang_build_program($prologue);
	if (try_run($program)) {
	    $ac->msg_result("yes");
	    $ac->define_var("C_INLINE", $value);
	    if ($value eq 'inline') {
		$ac->define_var("C_INLINE_IS_INLINE", 1);
	    }
	    last;



( run in 0.714 second using v1.01-cache-2.11-cpan-364913b4093 )