#!/usr/bin/perl -w
#
# Script to generate a Cygwin/Mingw makefile for running unit tests.
#
# Copyright 2002 Geoffrey Hausheer
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#

use strict;

sub usage;
sub fix_dir;
sub get_testname;
sub create_archive($$\@);

# set this variable to point to your windows headers
my ($cygwin_windows_includes) ="/usr/include/w32api";
my ($mingw_windows_includes) ="/mingw/include";
my ($wine_windows_includes) ="./include";
my ($windows_includes)="";

#set the default headers to use
my ($default_headers) =\$cygwin_windows_includes;

# set this variable to your compiler options
my($cc_opts)= "-g -O2 -Wall -mpreferred-stack-boundary=2 -D_REENTRANT";

my($topobjdir);
my(@testdirs);
my(@gooddirs);
my(@ok_list)=();
my($dir);
my($file);
my($afile)="";
my($archive)="";
# parse command-line options
while ($#ARGV >= 0)
{
    my $arg = shift @ARGV;
    if ($arg eq "-h") { usage; }
    if ($arg eq "-T")
    {
        $topobjdir = shift @ARGV;
        usage unless (-d $topobjdir);
        next;
    }
    if ($arg eq "-z") {
      $afile = shift @ARGV;
      $archive = "z";
    }
    if ($arg eq "-g") {
      $afile = shift @ARGV;
      $archive = "g";
    }
    if ($arg eq "-i") {
      $windows_includes = shift @ARGV;
      $default_headers = \$windows_includes;
    }
    if ($arg eq "-s") {
      my($sys) = shift @ARGV;
      if ($sys eq "cygwin") {
        $default_headers = \$cygwin_windows_includes;
      } elsif ($sys eq "mingw") {
        $default_headers = \$mingw_windows_includes;
      } elsif ($sys eq "wine") {
        $default_headers = \$wine_windows_includes;
      } else {
        usage;
      }
    }
}

# check/detect topobjdir
# NOTE: Checking for configure is not ideal, but it seems to work
if (defined($topobjdir))
{
    unless (-f $topobjdir . "/configure")
    {
        printf STDERR "Wrong -T argument, %s/configure does not exist\n", $topobjdir;
        usage;
    }
}
else  # try to detect it automatically
{
    if (-f "./configure") { $topobjdir = "."; }
    elsif (-f "../configure") { $topobjdir = ".."; }
    elsif (-f "../../configure") { $topobjdir = "../.."; }
    elsif (-f "../../../configure") { $topobjdir = "../../.."; }
    else {
      printf STDERR "Couldn't locate 'configure', and so no top-level directory\n";
      usage;
    }
}

chdir $topobjdir;
# Locate all tests
open FIND_FH, "find . -type d -name tests -print |";
while(<FIND_FH>) {
  chomp;
  push @testdirs,$_;
}
close FIND_FH;

#start writing the makefile in the root directory
open MAKE_FH,">Makefile.win";
print MAKE_FH <<EOH ;
#Define WINDOWS_HEADERS to point at the directory where windows.h lives
#Here are some examples
# For Cygwin
#WINDOWS_HEADERS = $cygwin_windows_includes
# For Mingw
#WINDOWS_HEADERS = $mingw_windows_includes
# For Wine
#WINDOWS_HEADERS = $wine_windows_includes

WINDOWS_HEADERS = $$default_headers

CC = gcc
RM = rm -f
TOUCH = touch

INCLUDE_DIRS = -I\$(WINDOWS_HEADERS) -I./include
CC_OPTS = \$(INCLUDE_DIRS) $cc_opts -include \$(WINDOWS_HEADERS)/windows.h

EOH

# iterate over each 'tests' directory
print MAKE_FH "TEST_O_FILES_wtmain = ./programs/winetest/wtmain.o\n";
foreach $dir (@testdirs) {
  my($rootdir);
  my($testname)=get_testname($dir);
  $rootdir=fix_dir($dir);
  unlink("$dir/testlist.c");
  # Locate all '.c' files that arent 'spec'
  my(@filelist)=grep(!/\.spec/,glob "$dir/*.c");
  if(scalar(@filelist)) {
    # Create a global list of all tests
    foreach $file (@filelist) {
      my($newfile);
      ($newfile = $file) =~ s/c$/ok/;
      push(@ok_list,$newfile);
    }
    # create the testslist.c file for each directory
    system("./programs/winetest/make_ctests @filelist > $dir/testlist.c");
    push @filelist,"$dir/testlist.c";
    push(@gooddirs,$dir);
    print MAKE_FH "# $dir\n";
    # List all object files needed for this test
    print MAKE_FH "TEST_O_FILES_$rootdir = \\\n";
    foreach $file (@filelist) {
      $file =~ s/c$/o/;
      if($file ne $filelist[$#filelist]) {
        print MAKE_FH "	$file\\\n";
      } else {
        print MAKE_FH "	$file\n";
      }
    }
    print MAKE_FH "TEST_EXE_$rootdir = $dir/$testname.exe\n";
  }
}
die "No C files found\n" if (!scalar(@gooddirs));
# The prerequisites for the tests are that the .ok fiels get created
print MAKE_FH "\n# .ok result files\n";
print MAKE_FH "TEST_OK_FILES = \\\n";
foreach $file (@ok_list) {
  if($file ne $ok_list[$#ok_list]) {
    print MAKE_FH "	$file\\\n";
  } else {
    print MAKE_FH "	$file\n";
  }
}
print MAKE_FH "\n";
print MAKE_FH "all: \$(TEST_OK_FILES)\n";
print MAKE_FH "\n";

#define how to clean everything up
print MAKE_FH "clean:\n";
print MAKE_FH "	\$(RM) \$(TEST_OK_FILES)\n";
print MAKE_FH "\n";
print MAKE_FH "distclean:\n";
print MAKE_FH "	\$(RM) \$(TEST_OK_FILES)\n";
print MAKE_FH "	\$(RM) \$(TEST_O_FILES_wtmain)\n";
foreach $dir (@gooddirs) {
  my($rootdir)=fix_dir($dir);
  print MAKE_FH "	\$(RM) \$(TEST_EXE_${rootdir}) \$(TEST_O_FILES_${rootdir})\n";
}
print MAKE_FH "\n";

#define how to make the executables
foreach $dir (@gooddirs) {
  my($rootdir)=fix_dir($dir);
  print MAKE_FH "\$(TEST_EXE_${rootdir}): \$(TEST_O_FILES_${rootdir}) \$(TEST_O_FILES_wtmain)\n";
  print MAKE_FH "	\$(CC) \$(CC_OPTS) \$(TEST_O_FILES_${rootdir}) \$(TEST_O_FILES_wtmain) -o \$@\n";
}

# define how to make to .ok files
foreach $file (@ok_list) {
  my($dir,$test) = ($file =~ /^(.*[\\\/]+tests)[\\\/]+(.*)\.ok$/);

  print MAKE_FH "$file: \$(TEST_EXE_". fix_dir($file) . ")\n";
  print MAKE_FH "	\$< $test && \$(TOUCH) \$@\n";
}
# define how to make the .o files

print MAKE_FH "%.o: %.c\n";
print MAKE_FH "	\$(CC) \$(CC_OPTS) -c -o \$@ \$<\n";
close MAKE_FH;
if($archive ne "") {
  create_archive($afile,$archive,@testdirs);
}
exit 0;

sub fix_dir {
  my($dir)=shift @_;
  my($rootdir)=($dir =~ /^[^\\\/]*[\\\/]+(.+)[\\\/]+tests/);
  $rootdir =~ s/[\\\/]+/_/g;
  return($rootdir);
}

sub get_testname {
  my($dir)=shift @_;
  my($testname)=($dir =~ /[\\\/]+([^\\\/]+)[\\\/]+tests/i);
  return $testname;
}

sub create_archive($$\@) {
  my($file,$arch,$dirlist)=@_;
  my($dir);
  my($cmd);
  if($arch eq "z") {
    print "Creating zip archive : $file\n";
    $cmd = "zip -r $file ";
  } else {
    print "Creating tar.gz archive : $file\n";
   $cmd = "tar -cvzf $file ";
  }
  foreach $dir (@$dirlist) {
    my($cfile);
    foreach $cfile (grep(!/\.spec/,glob "$dir/*.c")) {
      $cmd .= "$cfile ";
    }
  }
  $cmd .= " ./programs/winetest/wtmain.c";
  $cmd .= " ./include";
  $cmd .= " ./Makefile.win";
  system "$cmd";
}

sub usage
{
    print STDERR <<EOF;

Usage: $0 [options]

Options:
    -v       verbose mode (can be specified multiple times)
    -T dir   set Wine tree top directory (autodetected if not specified)
    -z file  archive (zip) all needed files for test
    -g file  archive (tar.gz) all needed files for test
    -i dir   specify directory where windows.h lives
    -s sys   specify system to build on (this sets the default header dir)
             Valid values for 'sys' are: cygwin, mingw, and wine
    -h       Show this message
    NOTE: You can specify either -g or -z but not both
EOF
    exit 1;
}
