#!/usr/bin/perl
# linkdll       *** internal script (called by vmake !)
#
# @(#)linkdll		1997-11-18
#
# link object modules to create a shared library
#
# TOOL          directory for tools
# LDFLAGS       optional loader flags
# KEEPFLG       optional keep intermediate files flag
#
# 1994-06-02 J.Bruehe  modify for AIX import lists
#
# 1995-08-01	R. Appelt	Einbau des "-deflib"-Parameters
# 1997-02-04 GG: Map-Files werden nach $SYS/map/$VMAKE_VERSION verschoben
# 1998-09-09 &gar: MAP-Files moved to $DBROOT/Support/Mapfiles -> 2 files:
#                  normal (xxx.map) and one with second extension <VMAKE_EXT>
# 1999-06-02 &gar: rename old linkdll.pl to link62.dll (only for UNIX)
#                  and call it in linkdll.pl if RELVER<72
# 2000-10-12 JoergMensing $TOOL/lib replaced by $TOOL_LIB, $INSTROOT/lib by $INSTLIB
#
#
#    ========== licence begin  GPL
#    Copyright (C) 2001 SAP AG
#
#    This program is free software; you can redistribute it and/or
#    modify it under the terms of the GNU General Public License
#    as published by the Free Software Foundation; either version 2
#    of the License, or (at your option) any later version.
#
#    This program 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 General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#    ========== licence end
#


package linkdll;
use Env;
use File::Copy;
use File::Path;
use ICopy;
use ToolLib;


# Get LDFLAGS, AR
@LIBMATH = ();
do "$TOOLVARS";
if ( $@ ) { print "$@"; exit }
ToolTrace("linkdll called with @ARGV\n");

$USAGE = "usage: linkdll [ld-flags] dll archive objects\n";

$MAP    = "";
$DEF    = "";
$NODLL  = "";
$DLLFLG = "";
$ENT    = "";
@Lib    = ();
@ELIB   = ();
if ($ENV{APOMAKE})
{ @DEFLIB = (); }
else
{ @DEFLIB = ("-nodefaultlib");}
$DEBUG  = "";
$PROF   = "";
$SCRIPT = "";
$RCFILE = "";
$NOTHREAD = 0; # CTS 1110865
$NOSTATICLIB = 0;
@LIBPC;
local @LDCMD=();
@SLIBS=();
@STLIBS=();
@ALLARCHIVES_MEMBERS = ();
@LOCAL_OBJECTS=();
@BuildModules=();
$NOWHATSTRING = 0;
$QUANTIFY=0;
@APPENDFLAGS=();
# with MS Compiler > 12 or lcapps pdb files are default
if (($ENV{_MSC_VER} gt "12")  || $ENV{APOMAKE}  || $ENV{FORCE_PDB_FILES} )
{
   $USE_PDB_FILES = 1;
}



do "$TOOL/bin/link_opts$TOOLEXT";
if ( $@ ) { print "$@"; exit }
@IMPLIST = ();
undef $REALNAME;


while ($_ = $ARGV[0], /^-/) {
    shift;
    last if /^--$/;

    if ( /^-linkoption=(.+)$/ ) { push @OFLAGS, $1; next }

    if ( /^-tooloption=(.+)$/ ) { next }

    if(/^-dll$/)     { next }     # ignored

    if(/^-nodll$/)   { $NODLL = $_; next }

    if(/^-def=(.*)/) { $DEF = "$1.def"; next }

	if(/^-deflib=(.*)/) {
        @DEFLIB   = ("-defaultlib:$1.lib");
        @LIBMPROC =	();
		next; }

    if(/^-elib=(.*)/) { push @ELIB, "$1.lib"; next }

    if(/^-usedef$/)  { next }    # unused

    if(/^-e=(.*)/)   { $ENT = "-entry:$1"; next }

    if(/^-exp=.*/) {
        #additional export, not implemented.
        print "linkdll: warning: Parameter not implemented: $_\n";
		next; }
	if ( /^-realname=(.+)$/ )
	{
		$REALNAME="$1";
		next;
	}

	if ( /^-nostaticlib$/ )
	{
		$NOSTATICLIB=1;
		next;
	}

    if(/^-.*/)     { push @OFLAGS, $_; next }

} #while

if ( @ARGV < 3 ) { print $USAGE; exit 1 }

$DLLFILE = shift;

if (!$DEBUG)
    { $STRIPDEBUG = "y" }

#some systems forbid some extensions for executables
if ($DLLFILE =~ /(.*)\.[fqs]/) { $DLL = $1} else { $DLL = $DLLFILE }
($DLLBASE) = $DLLFILE =~ /(\.*[^\.]*)/ ;  # alle Endungen werden abgeschnitten
# with MS Compiler > 12 or lcapps pdb files are default
if (($ENV{_MSC_VER} gt "12")  || $ENV{APOMAKE}  || $ENV{FORCE_PDB_FILES} )
{
   $USE_PDB_FILES = 1;
}


if ( $NOSTATICLIB )
{
	$ARCFILE = $DLLFILE;
	$ARCFILE =~ s/\.$DLL_TYPE/$ARCH_TYPE/;
}
else
{
	$ARCFILE = shift;
}

if ($ARCFILE =~ /(.*)\.[fqs]/) { $ARC = $1 } else { $ARC = $ARCFILE }
($ARCBASE) = $ARCFILE =~ /(\.*[^\.]*)/ ;  # alle Endungen werden abgeschnitten

$ARC_TYPE = "lib" unless defined($ARC_TYPE);
@OBJECTS = ();
foreach $F (@ARGV) {
    if ($F =~ /(.*)\.dll(.*)/) {
       $SUFF = $2;
       push @OBJECTS, "$1.$ARC_TYPE$SUFF" ;
       next;
    }
	if ( $F =~ /-def:(.*)/ ) { $DEF = $1 ; next; }
    push(@OBJECTS, $F) if $F !~ /.*\.def/;
} #foraech

@LDCMD = ($LDDLL_C);  # no difference between c, c++, c++->c
push @LDCMD, ( $NOTHREAD ? () : @THREAD_LDFLAGS ), @DLDFLAGS, @OFLAGS;



push @BuildModules, ToolLib::CreateBuildNrModule(@KEEPFLG,$DEBUG?("-g"):(),$NOTHREAD?("-nothread"):());



if ( $CURR_VERSION !~ /^fast$/i || $LIBDEBUG ) { $DEBUG = "y" }

if ( $DEBUG && $PROF ) {
        push @LDCMD, "-debug", "-debugtype:$LD_PROFDEBGTYPE", "/PROFILE";
}
else {

   if ( $PROF ) {
      push @LDCMD, "-debug", "-debugtype:$LD_PROFDEBGTYPE", "/PROFILE";
   }
   elsif ( ! $NOLINKDEBUG ) {
      push @LDCMD, "-debug", "/OPT:REF", "/IGNORE:4089";
      push @LDCMD, "-debugtype:cv" unless ($USE_PDB_FILES);
   }
}

# PTS: 1107332
push @LDCMD, "-pdb:none" unless($USE_PDB_FILES);

if ( $NODLL eq "" ) {
   $TMPFILE = "$ARCBASE.tmp";

	$DEF = "$DLLBASE.def" if $DEF eq "";
	if ( ! -f $DEF ) {
		warn "INFORMATION: Obsolete usage of -def=! Adapt description.\n";
		ICopy::icp("$DESC/fast/$DEF", "$WRK/$CURR_VERSION/tmp", 0, 0, 1);
	}
	if ( -f $DEF ) {
	    # PTS: 1107332
		system("$TOOL/pgm/mfconcom", @CONCOMOPTS, $DEF, $TMPFILE, $MACH, $VMAKE_VERSION, 'n', $LD, $OS, $OSSPEC) == 0
			or die "Error while executing \"mfconcom \@CONCOMOPTS $DEF $TMPFILE $MACH $VMAKE_VERSION n $LD $OS $OSSPEC\"\n".
			"message: $!\n";

		unlink $DEF;        # delete before write because it may be read-only

		open(FILE_IN, "<$TMPFILE") or die "Can't open $TMPFILE (input): $!\n";
		open(FILE_OUT, ">$DEF") or die "Can't open $DEF (output): $!\n";
		while(<FILE_IN>) {
			s/INITINSTANCE TERMINSTANCE/$ARC/;
			print FILE_OUT ;
		}
		close FILE_IN; close FILE_OUT;

		$DLLFLG = "-def:$DEF";
	} else { warn "No definition file for $DLLFILE" }
}

if ( $MAP eq "" ) { $MAP = "-map:$ARCBASE.map" }

if ( $SCRIPT ne "" ) {
        print "SCRIPT = $SCRIPT $DLLBASE\n";
        system("$TOOL/bin/$SCRIPT $DLLBASE") == 0
           or die "Error while executing \"$TOOL/bin/$SCRIPT $DLLBASE\"\n".
		   "message: $!\n";
    }
    if ( $RCFILE ne "" ) {

		@ARGV = ( "$DLLBASE", "$RCFILE", @RCINCL );
		do "$TOOL/bin/comprc$TOOLEXT";
		if ( $@ ) { warn "Error while executing comprc:\n", "$@"; exit }
    }

unless ($NOSTATICLIB)
{
#
#  Create the static library
#
@ARFLAGS;
@CONCOMOPTS;
	ToolTrace("$AR called with: @ARFLAGS -out:$ARC -debugtype:cv $DLLFLG @ELIB @OBJECTS @BuildModules\n");
	print ("$AR @ARFLAGS -out:$ARC -debugtype:cv @DEFLIB $DLLFLG @ELIB @OBJECTS @BuildModules\n") if ($ENV{NOQUIET});
	system("$AR @ARFLAGS -out:$ARC -debugtype:cv @DEFLIB $DLLFLG @ELIB @OBJECTS @BuildModules") == 0
	   or die "Error while executing $AR @ARFLAGS -out:$ARC -debugtype:cv -nodefaultlib $DLLFLG @ELIB ...\n".
	   "message: $!\n";
	# if $DLLFLG is set, $AR creates $ARC and $ARCBASE.exp
}

#
#  Link the dynamic library
#

if ( $NODLL eq "" ) {

   # $ARCBASE.exp has been created by $AR
   push @LDCMD, "-out:$DLL";
   push @LDCMD, "$ARCBASE.exp" if ( -f "$ARCBASE.exp" );
   push @LDCMD, @DEFLIB;



if ( $USE_PDB_FILES )
{
	$SYMBOL_DIR =~ tr[/][\\];
	if ( ! -d "$SYMBOL_DIR")
	{
		mkpath ("$SYMBOL_DIR", 0777) if ( ! -d "$SYMBOL_DIR") ||
			die ("can't create $SYMBOL_DIR for symbol files\n");
	}
	push @LDCMD, ( "-PDB:$SYMBOL_DIR\\$DLLBASE.pdb" ) if ( $USE_PDB_FILES ) ;
}

   push @LDCMD, "-dll", $ENT, $MAP, $RBJ, @OBJECTS, @BuildModules, @LIBPC, @Lib, @ELIB, @LIBMPROC, @LIBPOGO, @LIBBUFCHECK, $PROF;

	push @LDCMD, @APPENDFLAGS;

	if ($QUANTIFY)
	{ unshift @LDCMD, ($ENV{'QUANTIFYCALL'} ? $ENV{'QUANTIFYCALL'} : "quantify"); }
	$LINKCMD=join " ", @LDCMD;
	$LINKCMD=~s/\s(\S*)\*(\S*)\s/ "$1\*$2" /g;
	$LINKCMD=~s/\s(\S*)\?(\S*)\s/ "$1\?$2" /g;
	$LINKCMD.=" ".ToolLib::getWhatModule($DLL, $LINKCMD, @KEEPFLG,$DEBUG?("-g"):(),$NOTHREAD?("-nothread"):())
		unless ($ENV{APOMAKE} || $NOWHATSTRING );
	print ("$LINKCMD\n") if ($ENV{NOQUIET});
	ToolTrace("linkdll call: $LINKCMD\n");
	if ($ENV{APOMAKE})
	{
		system ("$LINKCMD") == 0 or die "Error while executing $LINKCMD \nmessage: $!\n";
	}
	else
	{

		open(LINK,"$LINKCMD 2>&1 |");
		while(defined($line=<LINK>))
		{
			# ignore lines with ignored values
			next if ( ($LNK_IGNORE ne "") && ($line=~/($LNK_IGNORE)/i));
			print $line;
		}
		close(LINK);
		unless ( $? == 0 )
		{
			die "Error while executing $LINKCMD \nmessage: $!\n";
		}
	}

	unless ( $? == 0 )
	{
		die "Error while executing $LINKCMD \nmessage: $!\n";
	}
}
else {
   # don't create shared library
   # vmake needs a dummy
	if ( -f $DLL ) { my($now)=time; utime($now, $now, $DLL)}
	else {
		open(FILE_OUT, ">>$DLL") or die "Can't open $DLL (touch): $!\n";
		close FILE_OUT;
	}
}
$DLL_DEP_COMMAND = "dumpbin /dependents";
if (scalar(@DENIES))
{
	my @wrong_dependencies = ();
	my %dependencies;
	my $found_start = 0;
	open(DEP_CHECK, "$DLL_DEP_COMMAND $DLL 2>&1 |") or  die "Can't check dependencies of $DLL";
	while (<DEP_CHECK>)
	{
		if ($found_start)
		{
			if (/ Summary/)
			{
				last;
			}
			elsif (/\s*(\S*\.dll)/)
			{
				my $dep = $1;
				$dep =~ tr/a-z/A-Z/;
				$dependencies{$dep} = 1;
			}
		}
		else
		{
			if (/Image has the following dependencies/)
			{	$found_start = 1;	}
		}
	}

	foreach $deny (@DENIES)
	{
		$deny =~ tr/a-z/A-Z/;
		if (defined $dependencies{$deny} || defined $dependencies{"$deny.DLL"})
		{
			push  @wrong_dependencies, $deny;
		}
	}
	if (scalar(@wrong_dependencies))
	{
		die ("ERROR: Found denied dependencies ". (join ",", @wrong_dependencies )."\n");
	}
}

if ( -f $DLL && $NODLL eq "" ) {

	($VMAKE_EXT) = $CURR_VERSION =~ /^(.)/;
	$VMAKE_EXT = lc($VMAKE_EXT);
    if ( $NO_SYMBOL_FILES )
	{
		unlink "$DLLBASE.dbg", "$DLLBASE.pdb";
		unlink "$DLLBASE.dbg.$VMAKE_EXT", "$DLLBASE.pdb.$VMAKE_EXT";
	}
	else
	{
	  if ( $USE_PDB_FILES )
	  {
			unlink "$SYMBOL_DIR/$DLLBASE.pdb.$VMAKE_EXT";
			copy "$SYMBOL_DIR/$DLLBASE.pdb","$SYMBOL_DIR/$DLLBASE.pdb.$VMAKE_EXT"
	       || warn "Can't copy $SYMBOL_DIR/$DLLBASE.pdb to $SYMBOL_DIR/$DLLBASE.pdb.$VMAKE_EXT: $!\n";
	  }
	  else
	  {
			if ( $STRIPDEBUG ne "" && $PROF eq "" )
			{
	            ToolTrace("separate debug info from $DLL\n");
				  system("$TOOL/pgm/symedit X -qo $DLLBASE.DBG $DLL") == 0
					 or die "Error while executing \"$TOOL/pgm/symedit X -qo $DLLBASE.DBG $DLL\"\n".
					 "message: $!\n";

		   		  if ( ! -d "$SYMBOL_DIR")
			      {
				  	mkpath ("$SYMBOL_DIR", 0777) if ( ! -d "$SYMBOL_DIR") ||
				  		die ("can't create $SYMBOL_DIR for symbol files\n");
				  }
				  copy "$DLLBASE.DBG", "$SYMBOL_DIR/$DLLBASE.dbg.$VMAKE_EXT";
				  unlink "$SYMBOL_DIR/$DLLBASE.dbg";
				  rename "$DLLBASE.DBG", "$SYMBOL_DIR/$DLLBASE.dbg";
			}
			else {
				  unlink "$SYMBOL_DIR/$DLLBASE.dbg.$VMAKE_EXT";
				  unlink "$SYMBOL_DIR/$DLLBASE.dbg";
			}
	  }
	}
}

if ( @KEEPFLG == 0 )
{
	unlink "$DLLBASE.res";
	unlink "$DLLBASE.rbj";
	if ( $NO_SYMBOL_FILES )
	{
		unlink "$DLLBASE.map", "$ARCBASE.map";
		unlink "$DLLBASE.map.$VMAKE_EXT", "$ARCBASE.map.$VMAKE_EXT";
	}
	else
	{
		# unlink "$DEF";
		if ( -f $DLL && -f "$DLLBASE.map" ) {
		  if ( ! -d "$MAP_DIR")
		  {
		  	mkpath ("$MAP_DIR", 0777) || die ("can't create $MAP_DIR for map files\n");
		  }
		  copy "$DLLBASE.map", "$MAP_DIR/$DLLBASE.map.$VMAKE_EXT"
		  || warn "Can't copy $DLLBASE.map to $MAP_DIR/$DLLBASE.map.$VMAKE_EXT: $!\n";
		  unlink "$MAP_DIR/$DLLBASE.map";
		  rename "$DLLBASE.map", "$MAP_DIR/$DLLBASE.map";
		}

		if ( $NODLL eq "" && -f $DLL && -f "$ARCBASE.map" ) {
		  unlink "$ARCBASE.tmp", "$ARCBASE.exp";
		  if ( ! -d "$MAP_DIR")
		  {
		  	mkpath ("$MAP_DIR", 0777) || die ("can't create $MAP_DIR for map files\n");
		  }
		  copy "$ARCBASE.map", "$MAP_DIR/$ARCBASE.map.$VMAKE_EXT"
		  || warn "Can't copy $ARCBASE.map to $MAP_DIR/$ARCBASE.map.$VMAKE_EXT: $!\n";
		  unlink "$MAP_DIR/$ARCBASE.map";
		  rename "$ARCBASE.map", "$MAP_DIR/$ARCBASE.map";
		}
		else {
		unlink "$ARCBASE.tmp", "$ARCBASE.exp", "$ARCBASE.map";
		}
	}
}


if ( $DLLFILE ne $DLL && -f $DLL ) { unlink $DLLFILE; rename $DLL, $DLLFILE }
if ( $ARCFILE ne $ARC && -f $ARC ) { unlink $ARCFILE; rename $ARC, $ARCFILE }

