#!/usr/bin/env perl
# See copyright, etc in below POD section.
######################################################################

use warnings;
use Getopt::Long;
use Cwd;
use IO::File;
use Pod::Usage;
use strict;
use vars qw($Debug);

#======================================================================
# main

our $Opt_Stage = 0;
our $Opt_Jobs = calc_jobs();

autoflush STDOUT 1;
autoflush STDERR 1;
Getopt::Long::config("no_auto_abbrev");
if (! GetOptions(
          "debug"       => sub { $Debug = 1; },
          "<>"          => sub { die "%Error: Unknown parameter: $_[0]\n"; },
          "stage=i"     => \$Opt_Stage,
          "j=i"         => \$Opt_Jobs,
    )) {
    die "%Error: Bad usage, try 'install_test --help'\n";
}

test();
exit(0);

#######################################################################

sub test {
    -r "nodist/install_test" or die "%Error: Run from the top of the verilator kit,";

    cleanenv();
    run("make distclean") if -r "Makefile";

    # Try building from a scratch area
    my $srcdir = getcwd();
    my $blddir  = $srcdir."/test_regress/obj_dir/install_test_bld";
    my $prefix  = $srcdir."/test_regress/obj_dir/install_test_prefix";
    my $testdirp= $srcdir."/test_regress/obj_dir/install_test_testp";
    my $testdirn= $srcdir."/test_regress/obj_dir/install_test_testn";

    if ($Opt_Stage <= 0) {
        run("/bin/rm -rf $blddir");
        run("/bin/mkdir -p $blddir");
        run("cd $blddir && $srcdir/configure --prefix $prefix");
        run("cd $blddir && make -j $Opt_Jobs");
    }

    # Install it under the prefix
    if ($Opt_Stage <= 1) {
        run("/bin/rm -rf $prefix");
        run("/bin/mkdir -p $prefix");
        run("cd $blddir && make install");
        run("test -e $prefix/share/man/man1/verilator.1");
        run("test -e $prefix/share/verilator/examples/make_tracing_c/Makefile");
        run("test -e $prefix/share/verilator/include/verilated.h");
        run("test -e $prefix/bin/verilator");
        run("test -e $prefix/bin/verilator_bin");
        run("test -e $prefix/bin/verilator_bin_dbg");
        run("test -e $prefix/bin/verilator_gantt");
        run("test -e $prefix/bin/verilator_profcfunc");
    }

    # Run a test using just the path
    if ($Opt_Stage <= 2) {
        my $dir = $testdirp;
        run("/bin/rm -rf   $dir");
        run("/bin/mkdir -p $dir");
        my $bin1 = $prefix."/bin";
        my $bin2 = $prefix."/share/bin";
        write_verilog($dir);
        run("cd $dir && PATH=$bin1:$bin2:\$PATH verilator --cc foo.v --exe foo.cpp");
        run("cd $dir/obj_dir && PATH=$bin1:$bin2:\$PATH make -f Vfoo.mk");
        run("cd $dir && PATH=$bin1:$bin2:\$PATH obj_dir/Vfoo");
    }

    # Run a test using exact path to binary
    if ($Opt_Stage <= 3) {
        my $dir = $testdirn;
        run("/bin/rm -rf   $dir");
        run("/bin/mkdir -p $dir");
        write_verilog($dir);
        my $bin1 = $prefix."/bin";
        my $bin2 = $prefix."/share/bin";
        run("cd $dir && $bin1/verilator --cc foo.v --exe foo.cpp");
        run("cd $dir/obj_dir && make -f Vfoo.mk");
        run("cd $dir/obj_dir && ./Vfoo");
    }

    if ($Opt_Stage <= 9) {
        print "*-* All Finished *-*\n";
    }
}

sub write_verilog {
    my $dir = shift;
    IO::File->new(">$dir/foo.v")->print('module t; initial begin $display("HELLO WORLD"); $finish; end endmodule'."\n");
    my $fh = IO::File->new(">$dir/foo.cpp");
    $fh->print('#include "Vfoo.h"'              ,"\n");
    $fh->print('unsigned int main_time = 0;'    ,"\n");
    $fh->print('double sc_time_stamp() {'       ,"\n");
    $fh->print('    return main_time;'          ,"\n");
    $fh->print('}'                              ,"\n");
    $fh->print('int main() {'                   ,"\n");
    $fh->print('    Vfoo *top = new Vfoo;'      ,"\n");
    $fh->print('    while (!Verilated::gotFinish()) {',"\n");
    $fh->print('        top->eval();'           ,"\n");
    $fh->print('        main_time++;'           ,"\n");
    $fh->print('    }'                          ,"\n");
    $fh->print('    top->final();'              ,"\n");
    $fh->print('}'                              ,"\n");
}

sub cleanenv {
    foreach my $var (keys %ENV) {
        if ($var eq "VERILATOR_ROOT"
            || $var eq "VERILATOR_INCLUDE"
            || $var eq "VERILATOR_NO_OPT_BUILD") {
            print "unset $var # Was '$ENV{$var}'\n";
            delete $ENV{$var}
        }
    }
}

#######################################################################

sub calc_jobs {
    my $ok = eval "
        use Unix::Processors;
        return Unix::Processors->new->max_online;
    ";
    $ok && !$@ or return 1;
    print "driver.pl: Found $ok cores, using -j ",$ok+1,"\n" if $Debug;
    return $ok + 1;
}

sub run {
    # Run a system command, check errors
    my $command = shift;
    print "\t$command\n";
    system "$command";
    my $status = $?;
    ($status == 0) or die "%Error: Command Failed $command, $status, stopped";
}

#######################################################################
__END__

=pod

=head1 NAME

install_test - Build and install Verilator several ways

=head1 SYNOPSIS

  install_test

=head1 DESCRIPTION

install_test performs several make-and-install iterations to verify the
kit.  It isn't part of the normal "make test" due to the number of builds
required.

=head1 ARGUMENTS

=over 4

=item --help

Displays this message and program version and exits.

=item -j I<jobs>

Specify make -j flag.  Defaults to number of cores + 1 if Perl's
Unix::Processors is installed, else 1.

=item -stage I<stage>

Runs a specific test stage (see the script).

=back

=head1 DISTRIBUTION

Copyright 2009-2020 by Wilson Snyder.  This package is free software; you
can redistribute it and/or modify it under the terms of either the GNU
Lesser General Public License Version 3 or the Perl Artistic License
Version 2.0.

=head1 AUTHORS

Wilson Snyder <wsnyder@wsnyder.org>

=head1 SEE ALSO

=cut

######################################################################
### Local Variables:
### compile-command: "cd .. ; nodist/install_test "
### End:
