# 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; <>};
}