# this simple script is used to build Tcl/Tk statically with
 # zip, vfs support to allow Tcl::Tk integration w/ Perl and all Tcl scripts
 # packed into ZIP
 #
 # argument to this script are one of following:
 #
 #    tcl             will build Tcl from 'tcl8.4.13' directory
 #    tk              will build Tk from 'tk8.4.13' directory
 #    memchan         will build memchan from 'memchan-2.2.1' directory
 #    trf             will build trf from 'trf2.1p2' directory
 #    vfs             will build vfs from 'tclvfs-1.3.1' directory
 #    tix             will build tix from 'tix-8.4.0' directory
 #    +zip            Tcl scripts will be added to archive, Tclaux.pm will be created
 #    perltcl         Perl module 'Tcl' will be built with predefined options
 #
 #  if no args is given, all items will be done in mentioned sequence
 #

 use Config;
 use strict;
 use Cwd;
 use File::Find;
 use File::Path;
 use File::Copy;

 my $cwd=cwd;

 # environment variables should be set properly (e.g. by vcvars32.bat)
 # for example env vars MSVCDIR, MSDEVDIR should point to right directories
 # ($ENV{MSDEVDIR}\bin should contain rc.exe for example)

 my $is_tcl_static = 1;
 my $is_tk_static = 1;
 my $is_tix_static = 0; # turn to 1 if Tix also goes into common Tcl.dll

 #my $what = shift || join '&', qw(tcl tk memchan trf vfs tix table perltcl);
 # add BLT?
 my $what = shift || join '&', qw(tcl tk memchan trf vfs tix +zip perltcl);

 my $tcldir = "$cwd/tcl8.4.13";
 my $tkdir = "$cwd/tk8.4.13";
 my $tixdir = "$cwd/tix-8.4.0";
 my $tabledir = "$cwd/tktable";
 my $memchdir = "$cwd/memchan-2.2.1";
 my $memchver = "2.2.1";
 my $trfdir = "$cwd/trf2.1p2";
 my $trfver = "21";
 my $vfsdir = "$cwd/tclvfs-1.3.1";
 my $vfsver = "1.3";
 my $perltcldir = "$cwd\\Tcl-0.89"; # version  TODO

 my $tclhfile = read_file("$tcldir/generic/tcl.h");
 my ($tcl_major,$tcl_minor,$tcl_patch) = # (8,4,7); # - get versions from src tree (tcl.h)
     $tclhfile =~ m/^\s*#define\s+TCL_MAJOR_VERSION\s+(\d+).*?^#define\s*TCL_MINOR_VERSION\s+(\d+).*?^#define\s+TCL_RELEASE_SERIAL\s+(\d+)/sm or die "can't figure out versions in tcl.h";
 print STDERR "(\$tcl_major,\$tcl_minor,\$tcl_patch)=($tcl_major,$tcl_minor,$tcl_patch)\n";

 my $tclidir = "$cwd/tclinst${tcl_major}${tcl_minor}${tcl_patch}";
 s/\//\\/g for $tclidir, $tcldir;

 # OS config
 my $lib = $Config{_a};
 # ok.

 my $tcllib = "tcl$tcl_major${tcl_minor}tsx$lib";
 my $tklib = "tk$tcl_major${tcl_minor}tsx$lib";
 my $tixlib = "tix840st$lib";

 my $rc = 0;

 # directory 'libs' will contain lib for static linking
 my $libdir = 'libs';
 mkdir $libdir;

 sub copy_lib {
     my $fn = shift;
     $fn =~ /([^\\\/]+)$/ or die;
     copy($fn,"$cwd/$libdir/$1");
 }
 sub copy_scripts {
     my ($dir,$name) = @_;
     my $cwd1 = cwd;
     chdir $dir;
     find({
             no_chdir => 1,
             wanted => sub {
                 -f && /^(.*?)\/([^\/]+)$/ && do {
                     my ($fdir,$fn) = ($1,$2);
                     mkpath("$cwd/$libdir/tcl-scripts/$name/$fdir");
                     print STDERR '.';
                     copy("$fdir/$fn","$cwd/$libdir/tcl-scripts/$name/$fdir/$fn");
                 };
             }
         },'.');
     print STDERR "\n";
     chdir $cwd1;
 }

 if ($what=~/\btcl\b/ && $is_tcl_static) {
     print STDERR "------$&------\n";
     chdir "$tcldir\\win";
     $rc = system("nmake -nologo -f makefile.vc OPTS=static,msvcrt,threads INSTALLDIR=$tclidir");
     if ($rc==0) {
         # only if perl memory allocator should be used
         #$rc = system(qw(nmake -nologo -f makefile.vc shell), "OPTS=static,msvcrt,threads","baselibs = kernel32.lib advapi32.lib user32.lib [[path-to-perl5-lib]]\\perl58.lib");
     }
     print STDERR "rc=$rc\n";
     #system("nmake -nologo -f makefile.vc install OPTS=static,msvcrt,threads INSTALLDIR=$tclidir");
     $rc==0 && copy_lib("$tcldir\\win\\Release\\tcl$tcl_major${tcl_minor}tsx$lib");
     $rc==0 && copy_scripts("$tcldir\\library","tcl$tcl_major.$tcl_minor");
     $rc==0 and $rc = system("nmake -nologo -f makefile.vc install OPTS=static,msvcrt,threads INSTALLDIR=$tclidir");
 }

 if ($rc==0 && $what=~/\btk\b/ && $is_tk_static) {
     print STDERR "------$&------\n";
     chdir "$tkdir\\win";
     $rc = system("nmake -nologo -f makefile.vc core release OPTS=static,msvcrt,threads INSTALLDIR=$tclidir TCLDIR=$tcldir");
     print STDERR "rc=$rc\n";
     $rc==0 && copy_lib("$tkdir\\win\\Release\\tk$tcl_major${tcl_minor}tsx$lib");
     $rc==0 && copy_lib("$tkdir\\win\\Release\\tk_ThreadedStaticX\\wish.res"); # TODO tk.res ???
     $rc==0 && copy_scripts("$tkdir\\library","tk$tcl_major.$tcl_minor");
     $rc==0 and $rc = system("nmake -nologo -f makefile.vc install OPTS=static,msvcrt,threads INSTALLDIR=$tclidir TCLDIR=$tcldir");
 }

 if ($rc==0 && $what=~/\btix\b/ && $is_tix_static) {
     print STDERR "------$&------\n";
     chdir "$tixdir\\win";
     my $tixmakestr = "nmake -nologo -f makefile.vc OPTS=static,msvcrt,threads INSTALL_DIR=$tclidir INSTALLDIR=$tclidir TCL_DIR=$tcldir TK_DIR=$tkdir TCL_MAJOR=$tcl_major TCL_MINOR=$tcl_minor TCL_PATCH=$tcl_patch USESTUBS=0 TCLSH_EXE=$tclidir\\bin\\tclsh$tcl_major${tcl_minor}tsx.exe TK_LIB=$tclidir\\tkstub$tcl_major$tcl_minor$lib TCL_LIB=$tclidir\\tclstub$tcl_major$tcl_minor$lib MKDIR=mkdir TOOLS32=$ENV{MSVCDIR} TOOLS32_rc=$ENV{MSDEVDIR} \"optlibs=msvcrt.lib oldnames.lib\"";
     $rc = system("$tixmakestr static");
     print STDERR "rc=$rc\ncwd=".cwd."\n";
     #$rc==0 and $rc = system("$tixmakestr install");
     #$rc==0 && copy_lib("$tixdir\\win\\Release\\tix84.dll");
     $rc==0 && copy_lib("$tixdir\\win\\tix840st.lib");
     $rc==0 && copy_scripts("$tixdir\\library","tix8.4");
 }

 if ($rc==0 && $what=~/\btable\b/) {
     print STDERR "------$&------\n";
     chdir "$tabledir\\win";
     my $tablemakestr = "nmake -nologo -f makefile.vc OPTS=static,msvcrt,threads DEST_DIR=$tclidir DEST_DIRU=$tclidir TCL_DIR=$tcldir TK_DIR=$tkdir TCL_MAJOR=$tcl_major TCL_MINOR=$tcl_minor TCL_PATCH=$tcl_patch USESTUBS=1 TCLSH_EXE=$tclidir\\bin\\tclsh$tcl_major${tcl_minor}tsx.exe TK_LIB=$tkdir\\win\\Release\\tkstub$tcl_major${tcl_minor}$lib TCL_LIB=$tcldir\\win\\Release\\tclstub$tcl_major${tcl_minor}$lib MKDIR=mkdir TOOLS32=$ENV{MSVCDIR} TOOLS32_rc=$ENV{MSDEVDIR} \"optlibs=msvcrt.lib oldnames.lib\"";
     $rc = system("$tablemakestr static");
     print STDERR "rc=$rc\ncwd=".cwd."\n";
     $rc==0 and $rc = system("$tablemakestr install");
     $rc==0 && copy_lib("$tabledir\\win\\Release\\table84.dll");
     $rc==0 && copy_scripts("$tabledir\\library","table8.1");
 }

 if ($rc==0 && $what=~/\bmemchan\b/) {
     print STDERR "------$&------\n";
     chdir "$memchdir/win";
     mkpath 'Release\Memchan_ThreadedStaticX';
     $rc = system("nmake -nologo -f makefile.vc static OPTS=static,msvcrt,threads INSTALLDIR=$tclidir TCLDIR=$tcldir TKDIR=$tkdir STATIC_BUILD=1");
     mkpath "$tclidir\\lib\\Memchan$memchver";
     $rc==0 and $rc = system("nmake -nologo -f makefile.vc install-libraries OPTS=static,msvcrt,threads INSTALLDIR=$tclidir TCLDIR=$tcldir TKDIR=$tkdir STATIC_BUILD=1");
     print STDERR "rc=$rc\n";
     copy_lib("$memchdir/win\\memchan$lib");
 }

 if ($rc==0 && $what=~/\btrf\b/) {
     print STDERR "------$&------\n";
     chdir "$trfdir/win";
     $rc = system("nmake -nologo -f makefile.vc static OPTS=static,msvcrt,threads TCL_SHORTVERS=$tcl_major${tcl_minor} \"TCL_INCLUDES=-I$tcldir\\generic -I$tcldir\\win\" TCLLIB=$tcldir\\win\\Release\\tclstub$tcl_major${tcl_minor}$lib TCLDIR=$tcldir  ZLIB_STATIC=-DZLIB_STATIC_BUILD TOOLS32=$ENV{MSVCDIR} TOOLS32_rc=$ENV{MSDEVDIR}");
     print STDERR "rc=$rc\n";
     copy_lib("$trfdir/win/trf$trfver$lib");
 }

 if ($rc==0 && $what=~/\bvfs\b/) {
     print STDERR "------$&------\n";
     chdir "$vfsdir/win";
     mkpath 'Release';
     $rc = system("nmake -nologo -f makefile.vc static OPTS=static,msvcrt,threads TCL_SHORTVERS=$tcl_major${tcl_minor} TCLDIR=$tcldir");
     print STDERR "rc=$rc\n";
     chdir $cwd;
     copy_lib("$vfsdir/win/vfs13$lib");
     copy_scripts("$vfsdir/library","vfs1.3");
 }

 if ($rc==0 && $what=~/\+zip\b/) {
     # create ZIP archive containing required tcl files
     # Now -- simplsitic usage of system("zip",...);
     # TODO -- using Tcl's zip

     chdir "$cwd/$libdir/tcl-scripts";
     system("zip -R -m ../tcl-scripts.zip *");
     chdir $cwd;

     # create special aux perl module to bootstrap Tcl/Tk

     open my $ftclpmaux, ">$libdir/Tclaux.pm";
     print $ftclpmaux do{local $_=<<'EOS';s/\{\{MAJ\}\}/$tcl_major/g;s/\{\{MIN\}\}/$tcl_minor/g;$_;};
 # this fake module serves purposes to bootstrap Tcl/Tk compiled into
 # Perl as extension with all Tcl/Tk scripts (including pure-Tcl widgets)
 # being inside ZIP file;
 # a mission of this file is to keep 6 Tcl scripts to facilitate bootstraping.

 $::tcl_library = __FILE__;
 $::tcl_library =~ s/Tclaux\.pm$//;
 $::tcl_library1 = $::tcl_library;
 $::tcl_library = "$::tcl_library/tcl{{MAJ}}.{{MIN}}";

 $Tcl::tcl_init0 = sub {
   my $interp = shift;
   $interp->Eval($Tcl::init_scripts{'tcl{{MAJ}}.{{MIN}}/init.tcl'});
   $interp->Eval("proc tclInit {} {}"); # so this will avoid extra searching of init.tcl
 };
 $Tcl::tcl_init = sub {
   my $interp = shift;

   $interp->Eval($Tcl::init_scripts{'tcl{{MAJ}}.{{MIN}}/package.tcl'});
   $interp->Eval($Tcl::init_scripts{'tcl{{MAJ}}.{{MIN}}/auto.tcl'});

   my $t5=Win32::GetTickCount;
   $interp->Vfs_Init;
   $interp->Eval($Tcl::init_scripts{'vfs1.3/vfslib.tcl'});
   $interp->Eval($Tcl::init_scripts{'vfs1.3/vfsUtils.tcl'});
   $interp->Eval($Tcl::init_scripts{'vfs1.3/zipvfs.tcl'});

   $interp->Trf_Init;
   $interp->Memchan_Init;
   #print STDERR $::tcl_library,'--',map{"{$_}"}$interp->Eval('info procs'),"\n";
   my $t4=Win32::GetTickCount;
   $interp->invoke("vfs::zip::Mount", "$::tcl_library1/tclscripts.zip.pm", "$::tcl_library1");
   
   $int = $interp;
   my $t3=Win32::GetTickCount;
   $interp->Tk_Init;
   my $t2=Win32::GetTickCount;
   print STDERR "*".($t2-$t0),"(","tk=",($t2-$t3),":mount=",($t3-$t4),":vfs=",($t4-$t5),"z)\n";
 };

 # Perl hash array to hold bootstrap Tcl scripts (init/tcl etc)
 %Tcl::init_scripts = (
 EOS

     # TODO - compressing?
     for (qw(init package auto)) {
         print $ftclpmaux "  'tcl$tcl_major.$tcl_minor/$_.tcl' , <<'EOSEOS',\n",
            read_file("$tcldir\\library\\$_.tcl"),
            "\nEOSEOS\n";
     }
     for (qw(vfslib vfsUtils zipvfs)) {
         print $ftclpmaux "  'vfs1.3/$_.tcl' , <<'EOSEOS',\n",
            read_file("$vfsdir\\library\\$_.tcl"),
            "\nEOSEOS\n";
     }
     print $ftclpmaux ");\n1;\n";
 }

 # perl module Tcl itself; (the entire reason of this script!)
 if ($rc==0 && $what=~/\bperltcl\b/) {
     print STDERR "------$&------\n";
     chdir $perltcldir;
     # copy Tclaux.pm, tcl-scripts.zip
     copy("$cwd/$libdir/Tclaux.pm","./Tclaux.pm");
     copy("$cwd/$libdir/tcl-scripts.zip","./tclscripts.zip.pm");
     # call makefile with known parameters:
     #  - all static libs are enlisted within --library="...."
     #  - -DHAVE_XXXINIT within --define="...."
     $rc = system("perl -w Makefile.PL ".
         "--library=\"".
         join(' ',
             "-l$cwd/$libdir/$tcllib",
             "-l$cwd/$libdir/$tklib" x $is_tk_static,
             "-l$cwd/$libdir/$tixlib" x $is_tix_static,
             "-l$cwd/$libdir/vfs13$lib",
             "-l$cwd/$libdir/trf21$lib",
             "-l$cwd/$libdir/memchan$lib",
         )   .
         " -limm32.lib -lcomctl32.lib\" ".
         "--include=-I$tclidir/include --nousestubs ".
         "--define=\"".
         join(' ',
             '-DHAVE_TKINIT' x $is_tk_static,
             '-DHAVE_MEMCHANINIT',
             '-DHAVE_TRFINIT',
             '-DHAVE_VFSINIT',
             '-DHAVE_TIXINIT'x$is_tix_static,
         )."\"");
     #$rc==0 and $rc = system("nmake");
     print STDERR "rc=$rc\n";
     chdir $cwd;
 }

 unless ($rc==0) {
     die "aborted with rc=$rc";
 }

 # few aux subs...
 sub read_file {
     # no binmode?
     my $fn=shift;
     local $/;
     open my $fh, "<$fn" or die "can't open $fn: $!";
     return <$fh>;
 }
 sub read_file0 {
     return do {local (@ARGV,$/) = shift; <>};
 }