The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
#!perl -w

# before running this script make sure you have 'tclsh' in your path, 
# and this 'tcl' distribution is required one.
# FreeBSD users may want to modify name of tcl interpreter (this is
# $tclsh variable below) as long as 'tclsh' does not work in their case

use strict;
use Getopt::Long qw(GetOptions);
use ExtUtils::MakeMaker;
use Config;

my $tclsh = 'tclsh';
my $tclconfig;
my $buildspec;
my $usestubs = ($^O eq 'MSWin32' ? 0 : 1);
my $libpath;
my $incpath;
my $defs = "";
my $buildtype = "";
my $wince;
my @extraargs;

my $arch;
my $stub = "tclstub8.4";

# These need updating as more platforms are added to tcl-core/ area
if ($^O eq "aix") {
    $arch = "aix";
} elsif ($^O eq "MSWin32") {
    $stub = "tclstub84";
    $arch = "win32-x86" if ($Config{archname} =~ /-x86-/);
    $arch = "win32-x64" if ($Config{archname} =~ /-x64-/);
} elsif ($^O eq "darwin") {
    $arch = "darwin-universal";
} elsif ($^O eq "solaris") {
    $arch = "$^O-x86" if ($Config{archname} =~ /86/);
    $arch = "$^O-sparc" if ($Config{archname} =~ /sun4/);
} elsif ($^O eq "aix") {
    $arch = "$^O";
} elsif ($^O eq "hpux") {
    $arch = "$^O-ia64" if ($Config{archname} =~ /ia64/i);
    $arch = "$^O-parisc" if ($Config{archname} =~ /pa-risc/i);
} elsif ($^O eq "linux") {
    $arch = "$^O-i686" if ($Config{archname} =~ /i\d86/);
    $arch = "$^O-ia64" if ($Config{archname} =~ /ia64/i);
    $arch = "$^O-x86_64" if ($Config{archname} =~ /x86_64/);
} elsif ($^O eq "cygwin") {
    $tclconfig = '/usr/lib/tclConfig.sh';
} elsif ($^O eq "solaris") {
    $arch = "$^O-x86" if ($Config{archname} =~ /[ix]86/);
    $arch = "$^O-sparc" if ($Config{archname} =~ /sun4/);
}

sub _die ($) {
    # now CPAN smokers report FAIL if Makefile.PL dies, it
    # should exit with status 0
    my $err = shift;
    warn $err;
    exit 0;
}

GetOptions("tclsh=s", \$tclsh,         # Use this tclsh executable as a
                                       # base to find the lib info needed
	   "tclconfig=s", \$tclconfig, # Use the specified Tcl config file
                                       # instead of basing the values on
                                       # the tclsh exe found
	   "buildspec", \$buildspec,   # Used with --tclconfig, use the
                                       # build (instead of install) values
                                       # for determining lib info
	   "usestubs!", \$usestubs,    # we want to use the Tcl stubs
                                       # mechanism by default
	   "library=s", \$libpath,     # Use this specific Tcl library
	   "include=s", \$incpath,     # Use this specific include path
	   "define=s", \$defs,         # Use this specific set of defines
	  )
    || _die <<EOT;

Usage: perl Makefile.PL [--tclsh <path>] [--tclconfig <path>]
                        [--buildspec] [--nousestubs] [<makemaker opts>...]

or for WinCE cross-compilation:

       perl -MCross=[your-cross-name] Makefile.PL PERL_CORE=1
             PERL_SRC=[your-perl-distribution-for-wince-crosscompiling]

or for expert compilation:

       perl --library=-l/path/to/tcl(stub).a --include=-I/path/to/tcl/include
            --define="-DLIB_RUNTIME_DIR=... -DTCL_LIB_FILE=..."

EOT

if (defined $Cross::platform) {
    # All appropriate environment variables shoult be set properly, such
    # as OSVERSION, PLATFORM, WCEROOT, SDKROOT. This is usually done with
    # appropriate 'bat' file. such as WCEMIPS.BAT
    #
    #
    # edit following two paths to reflect your situation
    # when editing please note that there should be tcl84.lib
    # libraries at "$tcldir\\wince\\$Cross::platform-release"
    #
    my $tcldir = 'D:\personal\pocketPC\tcltk\84a2\tcl8.4a2';

    WriteMakefile(
        NAME => "Tcl",
        VERSION_FROM => 'Tcl.pm',
        LIBS => ["-l$tcldir\\wince\\$Cross::platform-release\\tcl84.lib"],
        INC => "-I$tcldir\\generic",
    );
    exit;
}

if ($usestubs) {
    $defs .= " -DUSE_TCL_STUBS";
    $buildtype = "stub";
}

# If using stubs, we will set the LIB_RUNTIME_DIR and TCL_LIB_FILE
# to point to the install location as the default dll to load.

if (defined($libpath) && defined($incpath)) {
    # do nothing - set on command line
} elsif (!defined($tclconfig) && defined($arch) && $usestubs) {
    $incpath = "-Itcl-core/include";
    $libpath = "-Ltcl-core/$arch -l$stub";
    if ($^O eq 'darwin') {
	# OS X also requires the Carbon framework by default
	$libpath .= " -framework Carbon";
    }
} elsif ($tclconfig || $^O eq 'darwin') {
    unless ($tclconfig) {
       open(TCLSH, "$tclsh tclcfg.tcl |") or _die "error starting tclsh: $!\n";
       my $tclcfg = join '', <TCLSH>;
       close(TCLSH);
       my %tclcfg = $tclcfg =~ /^([^=]+)=(.*?)\n/gm;
       $tclconfig = $tclcfg{'tclConfig.sh'};
    }
    _die "Tcl config file '$tclconfig' not found\n" unless (-f $tclconfig);

    # Retrieve all info based on tclConfig.sh
    my $variant    = ($usestubs ? "_STUB" : "");
    $variant       = "_BUILD$variant" if $buildspec;
    my $libspecvar = "TCL${variant}_LIB_SPEC";
    my %tclcfg;
    process_tclconfig($tclconfig, \%tclcfg);
    _die "Tcl requires Tcl v8.4 or greater, found '$tclcfg{TCL_VERSION}'\n"
	unless (defined $tclcfg{'TCL_VERSION'}
		&& $tclcfg{'TCL_VERSION'} >= 8.4);
    $libpath = $tclcfg{$libspecvar};
    $incpath = $tclcfg{'TCL_INCLUDE_SPEC'};
    if ($usestubs) {
	if ($^O eq 'MSWin32') {
	    $defs .= " -DLIB_RUNTIME_DIR=\\\"$tclcfg{'TCL_EXEC_PREFIX'}/bin\\\"";
	    $defs .= " -DTCL_LIB_FILE=\\\"$tclcfg{'TCL_DLL_FILE'}\\\""; 
	} elsif ($^O eq 'darwin' && $tclcfg{'TCL_STUB_LIB_PATH'} =~ /\.framework/ ) {
	    (my $fmk = $tclcfg{'TCL_STUB_LIB_PATH'}) =~ s/(?<=\.framework).*//;
	    $defs .= " -DLIB_RUNTIME_DIR=\\\"$fmk\\\"";
	    $defs .= " -DTCL_LIB_FILE=\\\"$tclcfg{'TCL_LIB_FILE'}\\\"";
	    @extraargs = (dynamic_lib => {OTHERLDFLAGS => "-framework Carbon"});
	} else {
	    $defs .= " -DLIB_RUNTIME_DIR=\\\"$tclcfg{'TCL_EXEC_PREFIX'}/lib\\\"";
	    $defs .= " -DTCL_LIB_FILE=\\\"$tclcfg{'TCL_LIB_FILE'}\\\""; 
	}
    }
} else {
    open(TCLSH, "$tclsh tclcfg.tcl |") or _die "error starting tclsh: $!\n";
    my $tclcfg = join '', <TCLSH>;
    close(TCLSH);
    print $tclcfg;
    my %tclcfg = $tclcfg =~ /^([^=]+)=(.*?)\n/gm;

    # This is to allow propagation of this value to sub-Makefile.PLs
    $ENV{'TCLSH_PROG'} = $tclsh;

    if (0 && -f $tclcfg{'tclConfig.sh'}) {
	# Retrieve all info based on tclConfig.sh
	# Don't do this unless the user passes --tclconfig
	process_tclconfig($tclcfg{'tclConfig.sh'}, \%tclcfg);
	# libpath/incpath vars need to be set here if used ...
    } else {
	my $tclver = $tclcfg{tcl_version};

	# currently version must be 8.4+
	my ($vmaj,$vmin) = ($tclver =~ /^(\d+)\.(\d+)/);
	_die "Tcl requires Tcl v8.4 or greater, found '$vmaj.$vmin'\n"
	    if ($vmaj < 8 || ($vmaj == 8 && $vmin < 4));

	if ($tclcfg{tcl_library} =~ /^(.*)[\\\/]lib[\\\/]/) {
	    $libpath = "-L$1/lib";
	    $incpath = "-I$1/include";
	    $defs .= " -DLIB_RUNTIME_DIR=\\\"$1/lib\\\"" if $usestubs;
	}

	if ($^O eq 'MSWin32') {
	    $tclver=~s/\.//;
	    $defs .= " -DTCL_LIB_FILE=\\\"tcl$tclver.dll\\\"" if $usestubs; 
	}
	elsif ($^O eq 'freebsd') {
	    $tclver=~s/\.//;
	    $tclsh=~/([\d.]+)$/ and $incpath .= " -I/usr/local/include/tcl$1";
	    $defs .= " -DTCL_LIB_FILE=\\\"libtcl$tclver.so\\\"" if $usestubs; 
	}
	elsif ($^O eq 'hpux') {
	    #$tclver = '';
	    $defs .= " -DTCL_LIB_FILE=\\\"libtcl$tclver.sl\\\"" if $usestubs;
	}
	else {
	    #$tclver = '';
	    $defs .= " -DTCL_LIB_FILE=\\\"libtcl$tclver.so\\\"" if $usestubs; 
	}
	$libpath .= " -ltcl$buildtype$tclver";
    }
}

print "LIBS   = $libpath\n";
print "INC    = $incpath\n";
print "DEFINE = $defs\n";

if ($^O eq 'darwin') {
    # darwin has a broken ranlib that requires you to run it anytime
    # you copy an archive file, so ensure ours it up-to-date
    system("ranlib tcl-core/$arch/libtclstub8.4.a");
    system("git update-index --assume-unchanged tcl-core/$arch/libtclstub8.4.a")
	if -d ".git";
    if ($libpath =~ /-framework/) {
	# Frameworks require slightly different compile options
	@extraargs = (dynamic_lib => {OTHERLDFLAGS => $libpath});
	$libpath = "";
    }
}

#print <<"#EOS";
WriteMakefile(
    NAME => "Tcl",
    VERSION_FROM => 'Tcl.pm',
    LICENSE => 'perl',
    MIN_PERL_VERSION => '5.006',
    ABSTRACT_FROM => 'Tcl.pm',
    META_MERGE => {
        resources => {
            repository => 'http://github.com/gisle/tcl.pm',
            MailingList => 'mailto:tcltk@perl.org',
        }
    },
    LIBS => ["$libpath"],
    INC => "$incpath",
    DEFINE => $defs,
    @extraargs,
);

#EOS

sub process_tclconfig {
    # Process a tclConfig.sh file for build info
    my $tclconfig = shift;
    my $hashref   = shift;

    open(TCLSH, $tclconfig)
	or _die "error opening file '$tclconfig': $!\n";
    print "Using config data in $tclconfig\n";
    my $tclcfg = join '', <TCLSH>;
    close(TCLSH);
    %$hashref = $tclcfg =~ /^(\w+)=['"]?(.*?)["']?\n/gm;

    for my $k (keys %$hashref) {
	# Handle sh subs like ${TCL_DBGX}
	$hashref->{$k} =~ s/\$\{(\w+)\}/(exists $hashref->{$1} ? $hashref->{$1} : $&)/eg;
	# Handle any cygdrive-style paths
	$hashref->{$k} =~ s,/cygdrive/(\w)/,$1:/,ig;
    }
}

sub MY::libscan {
    my($self, $path) =@_;
    return '' if $path =~ /\.pl$/i;
    return $path;
}

BEGIN {
    # compatibility with older versions of MakeMaker
    my $developer = -f ".git";
    my %mm_req = (
        LICENCE => 6.31,
        META_MERGE => 6.45,
        META_ADD => 6.45,
        MIN_PERL_VERSION => 6.48,
    );
    undef(*WriteMakefile);
    *WriteMakefile = sub {
        my %arg = @_;
        for (keys %mm_req) {
            unless (eval { ExtUtils::MakeMaker->VERSION($mm_req{$_}) }) {
                warn "$_ $@" if $developer;
                delete $arg{$_};
            }
        }
        ExtUtils::MakeMaker::WriteMakefile(%arg);
    };
}