#!/usr/local/bin/perl

$usage = <<"!";
Usage: $0 [-m makefileName] [-b buildDbName] [-f additionalBuildDbName]
  [-os2]
  Creates a sub directory 'build' which is used by the Palladium
    makefile.

  -m makefileName: name of makefile to create links to.
                   defaults to pd_make/makefile
  -b buildDbName: name of build database file
                  defaults to pd_make/build.db
  -f additionalBuildDbName: name of additional build db file which
                            will be read after build.db.  (for your
                            testbuild when you want to ADD more files
                            which are not in build.db)
  -copy: create copies of files from src.  If we are on an os2 file
        system, it does not support hard or soft links.
  -os2: does the copy option (see previous) and also created links
        for the cwd.cmd perl script which the os2 version of the gnu 
        makefile needed.

  Assumes the following directories exist:
    src - src which is to be compiled
    pd_make - directory containing makefile and build.db
  - This utility should be run to create a build area which is used by
    the Palladium makefile.
  - Rerun this utility in a directory which contains the build sub
    directory if the build.db file is updated.
!

$login = $ENV{'LOGNAME'};

$top = `pwd`;
print "pwd is at ";
print $top;
chop($top);

if ($login eq "build") {
$buildFile = "bld/build_db.ref";
$buildFileBak = "bld/build_db.ref.bak";
$root = "$top/bld";
}
else {
$buildFile = "build/build_db.ref";
$buildFileBak = "build/build_db.ref.bak";
$root = "$top/build";
}

$exe = "$root/exe";
$lib = "$root/lib";
$make = "$top/pd_make";
$makefile = "$make/GNUmakefile";
#$cwd = "$make/cwd.cmd";
$buildDb = "$make/build.db";
push(@dbList, $buildDb);

while (@ARGV) {
	$_ = shift(@ARGV);
	if (/-m/) {
		$makefile = shift(@ARGV);
	}
	elsif (/-b/) {
		$buildDb = shift(@ARGV);
	}
	elsif (/-f/) {
		$additionalDb = shift(@ARGV);
		push(@dbList, $additionalDb);
	}
	elsif (/-copy/) {
		$copy = 1;
	}
	elsif (/-os2/) {
		$copy = 1;
        $os2 = 1;
	}
	else {
		die $usage;
	}
}
  
if (! -d "src") {
	print $usage;
	die "\nNo src directory, Stopped at";
}

if (! -d "lib") {
	print $usage;
	die "\nNo lib directory, Stopped at";
}

if (! -d "pd_make") {
	print $usage;
	die "\nNo pd_make directory, Stopped at";
}

if (-e "makefile") {
	system "rm -f GNUmakefile.bak";
	system "mv GNUmakefile GNUmakefile.bak";
}

#if ($os2 && -e "cwd.cmd") {
#       system "rm -f cwd.cmd.bak";
#	system "mv cwd.cmd cwd.cmd.bak";
#}

&linktopmakefile(".");

$weAlreadyHaveBuild = 1;
if (! -d $root) {
	if ($login eq "build") {
	print "Creating bld directory ...\n";
	$weAlreadyHaveBuild = 0;
	system "mkdir bld";
	&linkmakefile("bld");
	}
	else {
	print "Creating build directory ...\n";
	$weAlreadyHaveBuild = 0;
	system "mkdir build";
	&linkmakefile("build");
	}
}
if (! -d $exe) {
	system "mkdir $exe";
	&linkmakefile($exe);
}
if (! -d $lib) {
	system "mkdir $lib";
	&linkmakefile($lib);
}

# parse build.db to find out what our new build hierarchy is
# going to look like
&parseBuildDb;

# if we have an existing build directory, then we want to find
# out if the new build structure is different.  more specific,
# we are interested in finding out if files have been deleted
# or moved in this new build.db.  if they have, then we also want
# to delete the libraries those files are in because they are
# now out of date.
&getPictureOfOldDb;

# create the links from our build directory back to the source in
# the 'src' directory
&createLinks;

# compare our old build hierarchy against our new build hierarchy
# against our new build hierarchy to determine which libraries
# now need to be rebuilt.
&checkLibs;

sub parseBuildDb {
	# parse build.db file looking for files which belong in either
	# the lib or exe sub directories and build assoc array for each.

	foreach $db (@dbList) {
		if (! open(C, $db)) {
			warn "No $db";
			next;
		}
		while (<C>) {
			# ignore lines which begin with #, // and blank lines
			if (/^#/ || /^\s*$/ || /^\/\//) {
				next;
			}
			s/\n//;
		
			# create directory name which file will reside in.  e.g.
			# create: exe/spooler as the directory for file spooler.cpp
			# $dir = lib/pd, $file = common/str.cpp
			($dir, $file) = split(/\s+/, $_);
		
			if ($new{$file}) {
		
				# files can reside in multiple build directories.  e.g.
				# str.cpp is in odbgen and pd so:
				# e.g. $new{common/str.cpp} = lib/pd:exe/odbgen
		
				$string = $new{$file};
				if ($string =~ /$dir/) {
					warn "Duplicate file: $dir $file";
				}
				$new{$file} = "$string:$dir";
			}
			else {
				# simple case: $new{dpacomp.cpp} = pd
				$new{$file} = $dir;
			}
		}
		close (C);
	}
}

sub getPictureOfOldDb {

	if ($weAlreadyHaveBuild) {
		if (open(B, "$buildFile")) {
			while (<B>) {
				($oldFile, $oldDir) = split(/\s+/, $_);
				$old{$oldFile} = $oldDir;
			}
			$weHaveOldDb = 1;
		}
	}
}

sub createLinks {
	open(B, ">$buildFileBak") || die "Could not open $buildFileBak";
	foreach $file ( keys %new ) {
		print B "$file $new{$file}\n";
		&createLinksForFile($file);
	}
	close(B);
	system "mv $buildFileBak $buildFile";
}

sub createLinksForFile {
	local($file) = @_;
		
	# the build area has structure build/exe/spooler or 
	# build/lib/trace.  relative paths back to the top are always
	# going to be ../../..

	# e.g. $fullPath = ../../../src/oid/oid_con.cpp
	$srcPath = "src/$file";
	$fullPath = "../../../src/$file";
	@tokens = split(/\//, $file);
	$fileName = pop(@tokens);
	$origFileName = $fileName;
	$fileSrcDir = pop(@tokens);

	# file may reside in multple directories
	@dirs = split(/:/, $new{$file});
	$count = @dirs;
	$prefix = "a";
	foreach $dir (@dirs) {
		$subDir = "$root/$dir";

		# xxx/build/exe/spooler may not exist, so create it
		if (! -d "$subDir") {
			print "Creating $subDir ...\n";
			system "mkdir $subDir";
			&linkmakefile($subDir);
		}

		# if a file resides in multiple directories, create a
		# file name based on the root file name and the library
		# or exectutable into which it will go:
		# e.g. $file = str-odbgen.cpp and str-pd.cpp
		if ($count > 1) {
			($basename, $suffix) = split(/\./, $origFileName);
			$baseDir = $dir;
			$baseDir =~ s,.*/,,;
			$fileName = "${basename}_${baseDir}.$suffix";
		}

		# create the link back to the file under the src/ directory
		$realFile = $fullPath;
		$realFile =~ s,../../../,,;
		$linkToName = "$subDir/$fileName";
		if (! -l $linkToName ) {
			if (! -e "$realFile") {
				warn "Build database has entry for $file\n";
				warn "<$realFile> does not exist.\n";
			}
			# print "ln -fs $fullPath $linkToName\n";
			if ($copy) {
				system "rm -f $linkToName";
				system "cp $srcPath $linkToName";
			}
			else {
				system "ln -fs $fullPath $linkToName";
			}
		}
	}
}

sub checkLibs {
	if ($weHaveOldDb) {

		# check for things which have been deleted from old or
		# modified since old
		for $key (keys %old) {
			# if file has been deleted in the new build or put in a
			# different module
			if (! $new{$key} || $new{$key} ne $old{$key}) {
				if ($new{$key}) {
					$modified{$key} = $new{$key};
				}
				$newFile = $key;
				$oldFile = $key;
				$oldDirs = $old{$key};
				$newDirs = $new{$key};

				# we have: $old{$file} = $dirs
				# e.g. common/str.cpp=lib/pd:exe/odbgen:exe/pdenq:exe/pdobj_u
				# cycle over all the old directory names
				@dirs = split(/:/, $oldDirs);
				$count = @dirs;	
				for (@dirs) {
					($libOrExe, $libName) = split(/\//, $_);

					# the old library this file was in is out of date
					if ($libOrExe == "lib") {
						$rootLibName = $libName;
						$libName = "lib/lib${libName}ns.a";
						$rmLibs{$libName} = 1;
					}

					# if a file resides in multiple directories, create a
					# file name based on the root file name and the library
					# or exectutable into which it will go:
					# e.g. $file = str-odbgen.cpp and str-pd.cpp
					if ($count > 1) {
                                                # HP ssabat 7/31/95 drop4 port
                                                $oldFile = &basename($oldFile);
						($basename, $suffix) = split(/\./, $oldFile);
                                                # HP ssabat 7/31/95 drop4 port
	                        		$realName = "${rootLibName}/${basename}_${rootLibName}.$suffix";
					}
					else {
                                              # HP ssabat 7/31/95 drop4 port
					      ($dummy, $oldDir) = split(/\//,$oldDirs);
                                               $oldFile = &basename($oldFile);
                                               $realName = "$oldDir/$oldFile";
					}

					# delete the old link(s)
					if ($login eq "build") {
					print "rm -f \"bld/$libOrExe/$realName\"\n";
					system "rm -f \"bld/$libOrExe/$realName\"";
					}
					else {
					print "rm -f \"build/$libOrExe/$realName\"\n";
					system "rm -f \"build/$libOrExe/$realName\"";
					}
				}

				# remove the new libraries.  if someone moved a file
				# from one to another, both libraries are out of date.
				@dirs = split(/:/, $newDirs);
				$count = @dirs;	
				for (@dirs) {
					($libOrExe, $libName) = split(/\//, $_);

					# if this file went into a library, we are going to
					# delete the old library and the new library to
					# force their re-creation
					if ($libOrExe == "lib") {
						$rootLibName = $libName;
						$libName = "lib/lib${libName}ns.a";
						$rmLibs{$libName} = 1;
					}
				}
			}

			# even though we already have created the new links
			# (done earlier in this script), we will recreate the new
			# new links again.  this is for the case where file A
			# was in library B and C.  If we now say file A is in 
			# library B C and D.  We just deleted the links in B and C
			# in the previous step above.
		
			&createLinksForFile($newFile);
		}

		# check for files which have only been added to new.
		for $key (keys %new) {
			# new files will not be in old, and will not be in the list of
			# files which have only been modified.
			if (! $old{$key} && ! $modified{$key}) {
				# must be new file, so we want to delete the
				# existing library
				$newDirs = $new{$key};

				@dirs = split(/:/, $newDirs);
				$count = @dirs;	
				for (@dirs) {
					($libOrExe, $libName) = split(/\//, $_);

					# if this file went into a library, we are going to
					# delete the old library
					if ($libOrExe == "lib") {
						$rootLibName = $libName;
						$libName = "lib/lib${libName}ns.a";
						$rmLibs{$libName} = 1;
					}
				}
			}
		}

		# now we can remove the libraries which are out of date
		for (keys %rmLibs) {
			print "rm -f $_\n";
			system "rm -f $_";
		}
	}
}

sub linkmakefile {
	local ($subDir) = @_;
	if ($copy) {
		system "cp $makefile \"$subDir/makefile\"";
	} else {
		system "ln -fs $makefile \"$subDir/makefile\"";
	}

}
		
sub linktopmakefile {
	local ($subDir) = @_;
	if ($copy) {
		system "cp $makefile \"$subDir/GNUmakefile\"";
	} else {
		system "ln -fs $makefile \"$subDir/GNUmakefile\"";
	}

}
		
