#!/usr/bin/env perl
#
# Script which finds all symbolic links in a directory subtree
# and generates a Makefile to recreate them whenever they got lost
# (e.g. after a CVS checkout)
#
# by Sven H.M. Buijssen (sven.buijssen@math.uni-dortmund.de)
#
# History:
# 2004/09/26   v1.0.0   - First release
#
# $Id: symlinks2Makefile,v 1.1 2005/05/09 16:15:17 buijssen2 Exp $

# Allow proper programming only
# (a bit like Fortran90's "implicit none")
use strict;
use warnings;

#
# Define some variables
#
# program name
(my $progname=$0) =~ s/^.*\/(.*)/$1/;

# name of Makefile to create
# name of Makefile to create
use constant MAKEFILE => "Makefile.symlinks";

my @symlinks      = ();
my $listing       = "";
my %link;
my $commandLine   = "";

# Find all symbolic links below current working directory
@symlinks = `find . -type l -print`;
#@symlinks = `find ./fbenchmark* -type l`;

# Remove line breaks and other white spaces from @symlinks
@symlinks = split('\s+', join(' ', @symlinks));

# Give a listing of all symbolic links and where they point to
$commandLine = "\\ls -l " . join(' ', sort @symlinks);
$listing = `$commandLine`;

# Remove non-interesting parts of output (rights, size, time stamp etc.)
# and store the symbolic links in hash %link: key = link, entry = link target
foreach my $entry (sort split('\n', $listing)) {
    chomp($entry);
    $entry =~ m/^.+\s(\.\/.+)\s->\s(.+)$/;
    $link{$1} = $2;
}


# Backup old Makefile if existent and has not zero size
if ( -e MAKEFILE && -s MAKEFILE ) {
    print "renaming existing ".MAKEFILE." to ".MAKEFILE.".bak\n";
    rename(MAKEFILE, MAKEFILE . ".bak") or die "Failed to rename ".MAKEFILE." to ".MAKEFILE.".bak.";
}
#
# Start to create the Makefile
#
print "creating ".MAKEFILE."\n";
open(MAKEFILE_HDL, ">" , MAKEFILE) or die "Cannot open file <".MAKEFILE."> for writing: $!\n";

# Create head section
print MAKEFILE_HDL
    "#!/usr/bin/env make\n\n" .
    "########################################################################\n" .
    "# Makefile to restore all symbolic links within the FEAST package      #\n" .
    "# when retrieved from CVS.                                             #\n" .
    "#                                                                      #\n" .
    "# Automatically generated by symlinks2Makefile                         #\n" .
    "# Author: Sven H.M. Buijssen (sven.buijssen\@math.uni-dortmund.de)      #\n" .
    "########################################################################\n\n";

# Have a variable containing all symbolic links to be created
print MAKEFILE_HDL
    "# All symbolic links to be created\n" .
    "LINKS = " .
    join(" \\\n\t", sort @symlinks) . "\n\n" .
    "# Every Makefile should contain this line to avoid trouble on systems\n" .
    "# where the SHELL variable might be inherited from the environment.\n" .
    "# (This is never a problem with GNU make.)\n" .
    "SHELL = /bin/sh\n\n";

# Function to test existence of the directory in which a link
# will be created
print MAKEFILE_HDL
    "# Function to test existence of the directory in which a link\n" .
    "# will be created\n" .
    "TEST_DIR_EXISTENCE = \\\n" .
    "\t\@test -d \$(dir \$\@) || \\\n" .
    "\t    (echo; \\\n" .
    "\t     echo \"Directory <\$(dir \$\@)> does not exist!\"; \\\n" .
    "\t     echo \"Restore of symbolic link <\$\@> failed.\"; \\\n" .
    "\t     echo; \\\n" .
    "\t     echo \"Either this directory was lost, never committed to the CVS repository\"; \\\n" .
    "\t     echo \"or its current CVS version does not contain any file and a checkout\"; \\\n" .
    "\t     echo \"was done using the CVS option -P (rune empty directories).\"; \\\n" .
    "\t     echo; exit 1)\n\n";

# Function to test existence of the target for the link
# will be created
print MAKEFILE_HDL
    "# Function to test existence of the target for the link\n" .
    "# that will be created.\n" .
    "# (For a note on the syntax of the test program see target clean.)\n" .
    "TEST_TARGET_EXISTENCE = \\\n" .
    "\t\@file=\$(dir \$\@)\$(TARGET); \\\n" .
    "\tif test ! -f \$\$file -a ! -L \$\$file -a ! -b \$\$file -a ! -c \$\$file -a ! -d \$\$file; then \\\n" .
    "\t    echo; \\\n" .
    "\t    echo \"File <\$\$file> does not exist!\"; \\\n" .
    "\t    echo \"Restore of symbolic link <\$\@> failed.\"; \\\n" .
    "\t    echo; \\\n" .
    "\t    echo \"Either this file was lost, never committed to the CVS repository,\"; \\\n" .
    "\t    echo \"your working copy is not up to date or - if you were willingly\"; \\\n" .
    "\t    echo \"risking an inconsistent working copy and checked out individual\"; \\\n" .
    "\t    echo \"older parts from the CVS repository - this file has possibly not\"; \\\n" .
    "\t    echo \"been added yet at the date you selected for the checkout.\"; \\\n" .
    "\t    echo; exit 1; \\\n" .
    "\tfi;\n\n";

# Create default rule: show help
print MAKEFILE_HDL
    "# Default rule: show usage\n" .
    "default:\thelp\n\n";

# Create rule to create all symbolic links
print MAKEFILE_HDL
    "# Rule to create all symbolic links\n" .
    "symlinks:\t\$(LINKS)\n\n";

# The actual commands to restore the links
print MAKEFILE_HDL
    "# The actual commands to restore the links\n" .
    "\$(LINKS):\n" .
    "\t\$(call TEST_DIR_EXISTENCE)\n" .
    "\t\$(call TEST_TARGET_EXISTENCE)\n" .
    "\tln -s \$(TARGET) \$\@\n\n";

# Create rule for each separate link
foreach my $entry (sort keys %link) {
    print MAKEFILE_HDL
	"$entry: \\\n\tTARGET=$link{$entry}\n\n";
}

# Create rule to remove all symbolic links
# (but only if they are really symbolic links and not by any chance
# normal files with a name of a one of our symbolic links)
print MAKEFILE_HDL
    "# Remove all symbolic links\n" .
    "clean:\n" .
    "\t\@echo \"# Removing symbolic links\"\n" .
    "        # Check if file exists at all - if not, nothing has to be done\n" .
    "        # If it exists, check whether it's a symbolic link.\n" .
    "        # In case of a symbolic link, remove it. Otherwise give an error message.\n" .
    "        #\n" .
    "        # Note:\n" .
    "        # The test in the else-case could be abbreviated to \n" .
    "        #   test -e \$\$file\n" .
    "        # but that statement fails on Sun Solaris because of their outdated,\n" .
    "        # oldfashioned implemention of test that does not implement the -e\n" .
    "        # test when using /bin/sh.\n" .
    "\t\@for file in \$(LINKS); do \\\n" .
    "\t    if test -L \$\$file; then \\\n" .
    "\t\trm -f \$\$file; echo \"# \$\$file removed\"; \\\n" .
    "\t    elif test -f \$\$file -o -b \$\$file -o -c \$\$file -o -d \$\$file; then \\\n" .
    "\t\techo \"\$\$file is not a symbolic link. Removal cowardly refused.\"; \\\n" .
    "\t    fi; \\\n" .
    "\tdone\n\n" .

    "# Help\n" .
    "help:\n" .
    "\t\@echo \"Usage: make [targets...]\"\n" .
    "\t\@echo \"\"\n" .
    "\t\@echo \"where targets include:\"\n" .
    "\t\@echo \"\"\n" .
    "\t\@echo \"  symlinks       create all symbolic links for the FEAST package\"\n" .
    "\t\@echo \"                 (essential especially after a FEAST checkout from CVS)\"\n" .
    "\t\@echo \"  clean          remove all symbolic links\"\n";

close(MAKEFILE_HDL);

chmod 0755, MAKEFILE;

1;
