#!/usr/bin/perl -w # Description: copies the necessary OpenGL and Glut header and include files # to the current users directory. Also capable of updating # Makefiles and cleaning up after itself (uninstall). # Author: Spencer R. Shimko # Email: sshimko1ATumbc.edu use Getopt::Std; use File::Copy; use File::Path; use File::Find; use File::Spec; use File::Spec::Functions qw(:ALL); use Fcntl qw(:flock); ## GLOBALS my $lib = "/usr/lib"; my $inc = "/usr/include/GL"; my $glut = "libglut"; my $uid = getlogin (); my $f = substr ( $uid , 0 , 1 ); my $s = substr ( $uid , 1 , 1 ); my $afshome; # 1 letter names differently if ( $s =~ /\d/ ){ $afshome = File::Spec->catdir( "/afs/umbc.edu/users" ,$f , $f , $uid, "home"); } else { $afshome = File::Spec->catdir( "/afs/umbc.edu/users" ,$f , $s , $uid, "home"); } my $ulib = File::Spec->catdir( $afshome, "gllib"); # one is used for removal, the other installations my $rinc = File::Spec->catdir( $afshome, "glinclude"); my $uinc = File::Spec->catdir( $rinc, "GL" ); my $menu = "Please choose from the following options:\ 1. Copy the GL libraries and toolkit to your AFS directory. This allows you to\ compile and run GL applications using full hardware acceleration and allows\ you to compile these apps locally in an ITE lab (without ssh'ing in to a GL server).\ 2. Update a Makefile to utilize your AFS copy of the GL libraries and toolkit.\ 3. Remove the copied libraries and headers from your AFS account.\ Enter you choice: \ 4. Exit\ note: there is also a non-interactive commandline version, see \"$0 -h\" for information\ Enter choice: "; my $usage = "usage: $0 [] \ optional flags:\ -h this help message\ -u uninstall GL/GLUT libs and headers from AFS (recover space) but only allowed\ as a single argument\ -c copy the GL/GLUT libs and headers to your AFS directory\ -m update a standard style Makefile to include and link to the\ user's AFS directories.\n"; ## END GLOBALS ## MAIN $| = 1; print "Welcome the the UMBC OpenGL/GLUT local installer.\n\n"; # if no arguments given use menu driven interface if ( ! defined $ARGV[0] ){ my $choice = 0; while ( $choice !~ /^[1234]$/ ){ print $menu; chomp ($choice = <>); } if ( $choice eq '4' ){ exit; } elsif ( $choice eq '3' ){ uninstall ( ); } elsif ( $choice eq '2' ){ updateMf ( ); } elsif ( $choice eq '1' ){ copyLibs ( ); } } else { # else we have command line arguments my %opts; getopts('hucm:', \%opts); if ( defined $opts{'h'} ){ print $usage; exit; } if ( defined $opts{'u'} ){ if ( (keys %opts) > 1 ){ print $usage; exit; } uninstall ( ); exit; } if ( defined $opts{'c'} ){ copyLibs ( ); } if ( defined $opts{'m'} ){ updateMf ( $opts{'m'} ); } exit; } ## END MAIN ## SUBS # copy the libraries and include files sub copyLibs { # should only run on gl servers if ( `uname -n` !~ /^linux[123].gl.umbc.edu$/ ){ die "Error, this script should only be run from a linux GL server at umbc."; } if ( -e $ulib or -e $uinc) { print "Before proceeding we should cleanup after previous installs.\n"; uninstall(); } mkpath( [ $ulib, $uinc ], 0, 0711 ); my $size = 0; my %files; # file list to sidestep symlink double counts # recursively find the necessary files and copy them to the users directory find( sub { return if ( /^\./ ); my $pwd = $File::Find::dir; $pwd = s~^$lib~~; mkpath( File::Spec->catdir($ulib , $pwd ) ) if not -e File::Spec->catdir($ulib , $pwd ); if ( -f && ( /$glut/ ) ){ if ( -l ){ my $link = $_; my @linklist; push @linklist, $_; if ( -e readlink ){ while ( -l readlink ( $link ) ){ push @linklist, readlink ( $link ); $link = readlink( $link ); } # no need to double create files here (results from dealing with symlinks) if ( ! ( defined $files{ File::Spec->catfile($ulib , (splitpath(readlink($link)))[2])} ) ){ copy ( readlink, File::Spec->catfile( $ulib, (splitpath(readlink($link)))[2])); print "Created: " . File::Spec->catfile( $ulib, (splitpath(readlink($link)))[2]) . "\n"; $size += -s readlink($link); $files{File::Spec->catfile( $ulib, (splitpath(readlink($link)))[2])} = 1; } # now create trail of symlinks foreach my $l ( @linklist ){ # if symbolic and filename have same name, do not create a link if ( (splitpath(readlink($link)))[2] ne (splitpath($l))[2] ){ if ( ! ( defined $files{ File::Spec->catfile($ulib , (splitpath($l))[2])} ) ){ symlink( File::Spec->catfile( $ulib, (splitpath(readlink($link)))[2]), File::Spec->catfile( $ulib, $l )); $files{File::Spec->catfile( $ulib, $l)} = 1; print "Linked: " . File::Spec->catfile( $ulib, $l ) . "\n"; $size += (lstat( $l ))[7]; } } } } } else { if ( ! ( defined $files{ File::Spec->catfile($ulib , (splitpath($_))[2])} ) ){ copy( $_, File::Spec->catfile( $ulib, $_) ); $size += -s; print "Created: " . File::Spec->catfile( $ulib, $_) . "\n"; $files{File::Spec->catfile( $ulib, $_)} = 1; } } } }, $lib ); find( sub { return if ( /^\./ ); my $pwd = $File::Find::dir; $pwd = s~^$inc~~; mkpath( File::Spec->catdir($uinc , $pwd ) ) if not -e File::Spec->catdir($uinc , $pwd ); if ( -f ){ if ( -l ){ my $link = $_; my @linklist; push @linklist, $_; if ( -e readlink ){ while ( -l readlink ( $link ) ){ push @linklist, readlink ( $link ); $link = readlink( $link ); } # no need to double create files here (results from dealing with symlinks) if ( ! ( defined $files{ File::Spec->catfile($uinc , (splitpath(readlink($link)))[2])} ) ){ copy ( readlink, File::Spec->catfile( $uinc, (splitpath(readlink($link)))[2])); print "Created: " . File::Spec->catfile( $uinc, (splitpath(readlink($link)))[2]) . "\n"; $size += -s readlink($link); $files{File::Spec->catfile( $uinc, (splitpath(readlink($link)))[2])} = 1; } # now create trail of symlinks foreach my $l ( @linklist ){ # if symbolic and filename have same name, do not create a link if ( (splitpath(readlink($link)))[2] ne (splitpath($l))[2] ){ if ( ! ( defined $files{ File::Spec->catfile($uinc , (splitpath($l))[2])} ) ){ symlink( File::Spec->catfile( $uinc, (splitpath(readlink($link)))[2]), File::Spec->catfile( $uinc, $l )); $files{File::Spec->catfile( $uinc, $l)} = 1; print "Linked: " . File::Spec->catfile( $uinc, $l ) . "\n"; $size += (lstat( $l ))[7]; } } } } } else { if ( ! ( defined $files{ File::Spec->catfile($uinc , (splitpath($_))[2])} ) ){ copy( $_, File::Spec->catfile( $uinc, $_) ); $size += -s; print "Created: " . File::Spec->catfile( $uinc, $_) . "\n"; $files{File::Spec->catfile( $uinc, $_)} = 1; } } } }, $inc ); printf ("Copied %.2f Kbytes.\n", ($size/1000)); } # update a makefile sub updateMf { $_ = shift; glob if defined $_; while ( ! ( defined && -f && -w ) ){ print "Filename not valid or not readable or writable!\n" if defined; print "Enter filename to update or leave blank to quit: "; $_ = ; chomp; glob; return if ( $_ eq '' ); } $mf = $_; # $_ will be redefined eventually # open the file for read/write (update) and slurp file into an array open(MF, "+<" . $_ ) or die "Failed to open $_ : $!"; flock(MF, LOCK_EX) or die "Failed to lock $_: $!"; print "Slurping $_...\n"; @CONTENTS = ; print "Searching for a nice place to insert the libs and includes...\n"; ML: foreach ( @CONTENTS ){ if ( not defined $ul ){ # if lib link hasn't been updated if ( /\s*LIBGL=/ ){ if ( m{-L$ulib} ){ print "User's libraries appear to already be linked in this file (previously updated?)\n"; $ul = 0; } else { print "Inserting lib link in LIBGL\n"; s/^(.*?)\s*$/$1/; $_ .= " -L$ulib\n"; $ul = 1; } } } if ( not defined $ui ){ # if includes haven't been updated if ( /\s*CFLAGS=/ ){ if ( m{-I$rinc} ){ print "Users includes appear to already be linked in this file (previuosly updated?)\n"; $ui = 0; } else { print "Inserting include link in CFLAGS\n"; s/^(.*?)\s*$/$1/; $_ .= " -I$rinc\n"; $ui = 1; } } } if ( defined $ui && defined $ul ){ last ML; } } if ( defined $ui && defined $ul ){ if ( $ul or $ui ){ print "Creating backup file $_.bak\n"; copy ( $mf, $mf . ".bak" ) or die "Failed to create backup file: $!"; print "Updating $_...\n"; seek(MF,0,0); truncate (MF, tell(MF)); print MF @CONTENTS; print "Successfully updated $_!\n"; } else{ print "Apparently your file was previously updated.\n"; print "If you are still having problems compiling try adding these lines to the \"make\" command:\n"; print "\"-I$uinc\"\n\"-L$ulib\"\n"; } } else { print "ERROR: Could not find a suitable insertion point in $_.\n"; print "This is not necessarily bad, it means your file didn't meet my strict expectations.\n"; print "I'm aware of LIBGL and CFLAGS declarations.\n"; print "Add these lines to $_ correctly:\n\"-I$uinc\"\n\"-L$ulib\"\n"; print "The last option is to just specify those lines as part of the \"make\" command.\n"; } close (MF); } sub uninstall { print "WARNING: You are about to recursively delete these directories:\n$ulib\n$rinc\nAre you sure sure (yes or no)? "; $_ = ; chomp; if ( /^yes$/ ){ rmtree( [ $ulib, $rinc ], 1 , 1 ); } else { print "Aborted!\n"; exit; } } ## END SUBS