#-*- perl -*- # Copyright (C) 2000-7 R Development Core Team # # 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, 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. # # A copy of the GNU General Public License is available at # http://www.r-project.org/Licenses/ # Send any bug reports to r-bugs@r-project.org use Cwd; use File::Basename; use File::Path; use Getopt::Long; use R::Dcf; use R::Utils; my $revision = ' $Rev$ '; my $version; my $name; $revision =~ / ([\d\.]*) /; $version = $1; ($name = $0) =~ s|.*/||; my $startdir = cwd(); my $lockdir; my @bundlepkgs; my $library; $SIG{'INT'} = 'Cleanup'; sub Cleanup { print "*** user interrupt ***\n"; if(defined @bundlepkgs) { if($is_bundle) { # back out the whole bundle for $pkgname (@bundlepkgs) { $pkgname .= "_" . $description->{"Version"} if $opt_with_package_versions; rmtree($library . "/" . $pkgname); $dppkg = $lockdir . "/" . $pkgname; system("mv $dppkg $library") if ($safe && -d $dppkg); } } else { print "Removing '$dpkg'\n"; rmtree($dpkg); # move back any previously installed package $dppkg = $lockdir . "/" . $decpkgname; if ($safe && -d $dppkg) { print "Restoring previous '$dpkg'\n"; system("mv $dppkg $library"); } } } if (defined $lockdir) { print "Removing lock directory\n"; rmtree($lockdir); } chdir($startdir); if (defined $tardir) { print "Removing untar-ed package sources\n"; rmtree($tardir); } die "*** exiting after cleanup ***\n"; } my @knownoptions = ("help|h", "version|v", "debug|d", "library|l:s", "clean|c", "preclean", "docs:s", "with-package-versions", "use-zip", "use-zip-data", "use-zip-help", "auto-zip", "build", "fake", "unsafe", "pkglock"); ## topLevelFiles will be files that sit in the top level of a ## zip/tarball along with any included directories. ## This needs to be fixed: this file shouldn't do binary builds, ## that's what build.in is for - DJM ## No, that is what INSTALL --build is for, pace DJM my $topLevelFiles = "DESCRIPTION"; GetOptions (@knownoptions) || usage(); R_version($name, $version) if $opt_version; my $R_HOME = $ENV{'R_HOME'} || die "Error: Environment variable R_HOME not found\n"; usage() if $opt_help; if ($opt_with_package_versions) { warn "\n WARNING: --with-package-versions is deprecated:\n suppport on Windows is incomplete, so use at your own risk\n\n" } ## ===== to allow to be converted for use when cross-compiling ===== my $BUILD = "GCC4"; my $Rversion; if($BUILD eq "CROSS") { my $startdir = cwd(); chdir("../.."); $R_HOME = cwd(); chdir($startdir); open FILE, "< $R_HOME/VERSION"; $Rversion = ; close FILE; $Rversion =~ s/ .*\n$//; } else { $Rversion = $ENV{'R_VERSION'} || die "Error: Environment variable R_VERSION not found\n"; } ## ===== end ===== if($opt_debug) { print "\nOption -d or --debug is accepted for compatibility with Unix.\n"; print "However, it does less.\n\n"; } if($opt_library){ # remove quotes around the library path $opt_library =~ s/^['"]//; $opt_library =~ s/['"]$//; #'" chdir($opt_library) || die "Error: cannot change to library directory '$opt_library'\n"; $library = cwd(); $library = Win32::GetShortPathName($library) if $library =~ / /; my $R_LIBS = $ENV{'R_LIBS'}; $ENV{'R_LIBS'} = join(";", $library, $R_LIBS); chdir($startdir); } else { my @out = R_runR("cat('\n~~~', .libPaths()[1], '\n', sep = '')", "--no-restore --slave"); foreach $f (@out) { if($f =~ /^~~~/) { $library = $f; $library =~ s/^~~~//; } } $library = Win32::GetShortPathName($library) if $library =~ / /; print "installing to '$library'\n"; } die "ERROR: a library on a network share is not supported\n" if $library =~ /^\\\\/; $library =~ s+\\+/+g; my $helpflags = "HELP=YES WINHELP=CHM"; if($opt_docs) { my $tmp, $chm, @l; if ($opt_docs eq "none") { $helpflags = "HELP=NO"; } elsif ($opt_docs eq "normal") { $helpflags = "HELP=YES WINHELP=NO"; } elsif ($opt_docs eq "all") { $helpflags = "HELP=YES WINHELP=CHM"; } elsif ($opt_docs =~ /,/) { @l = split /, */, $opt_docs; $tmp = ""; $chm = "NO"; foreach $f (@l) { if ($f eq "chm") { $chm = "CHM"; next; } $tmp .= " -$f"; } $helpflags = "HELP=YES WINHELP=$chm HELPTYPES='$tmp'"; } elsif ($opt_docs eq "txt"|| $opt_docs eq "html" || $opt_docs eq "latex" || $opt_docs eq "example") { $helpflags = "HELP=YES WINHELP=NO HELPTYPES=-$opt_docs"; } elsif ($opt_docs eq "chm") { $helpflags = "HELP=YES WINHELP=CHM HELPTYPES="; } else { die "ERROR: invalid --docs value '$opt_docs'\n"; } } $safe = 1; $safe = 0 if $opt_unsafe; $safe = 2 if $opt_pkglock; print " *** safe is $safe ***\n" if $opt_debug; if((-e "$R_HOME/doc/html/R.css") && !(-e "$library/R.css")) { printf "installing R.css in $library\n"; system("cp $R_HOME/doc/html/R.css $library/R.css"); } my $istar = 0; # any tar in the args ## this is the main loop over all packages/bundles to be installed my $pkg; foreach $pkg (@ARGV){ # remove misguided trailing separator (/ or \) $pkg =~ s/\/$//; $pkg =~ s/\\$//; my $pkgname = basename($pkg); $is_bundle = 0; if (!(-e $pkg)) { warn "'$pkg' does not exist: skipping\n"; next; } ## is someone trying to install a Windows binary package if($pkgname =~ /\.zip$/) { warn "'$pkg':Windows binary packages in zipfiles are not supported: skipping\n"; next; } ## is this a tar archive? if($pkgname =~ /\.tar\.gz$/ || $pkgname =~ /\.tgz$/) { $pkgname =~ s/\.tar\.gz$//; $pkgname =~ s/\.tgz$//; $pkgname =~ s/_[0-9\.-]*$//; my $dir = "R.INSTALL.$$"; rmtree($dir); # zap in the likely event it is already there if(! -d $dir) { # reuse if we failed to zap mkdir($dir, 0755) or die "Error: cannot create $dir\n"; } $tardir = $dir; ## workaround for paths in Cygwin tar $pkg =~ s+^([A-Za-x]):+/cygdrive/\1+; if(system("tar -zxf '$pkg' -C $dir")) { die "Error: cannot untar the package\n";} $pkg = $dir."/".$pkgname; $istar = 1; } chdir($pkg) || die "Error: cannot change to directory '$pkg'\n";; my $canonpkg = cwd(); $pkgname = basename($canonpkg); chdir(".."); my $pkgdir = cwd(); chdir($startdir); $description = new R::Dcf("$pkg/DESCRIPTION"); my $intname = $description->{"Package"}; my $Type = $description->{"Type"}; if($Type && $Type ne "Package") { if($Type eq "Translation") { chdir($pkg); system("cp -r ./share $R_HOME") if -d "share"; system("cp -r ./library $R_HOME") if -d "library"; if($opt_build) { my $filename = "${pkgname}_" . $description->{"Version"}; $startdir = Win32::GetShortPathName($startdir) if $startdir =~ / /; chdir($pkgdir); system("rm -f $startdir/$filename.zip"); system("zip -r9Xq $startdir/$filename.zip $pkgname"); } next; } if (defined($tardir)) { chdir($startdir); rmtree($tardir); } die "cannot install package '${pkgname}' of type '$Type'\n" } die "no valid package name found\n" unless length($pkgname) > 0; my $OS_type = $description->{"OS_type"}; die "cannot install package '${pkgname}' for '$OS_type'\n" if $OS_type && $OS_type ne "windows"; my $pkgoutdir = "$library/$pkgname"; die " *** can't install into source directory ***\n" if $canonpkg eq $pkgoutdir; $pkgdir = Win32::GetShortPathName($pkgdir) if $pkgdir =~ / /; my $makecmd = "pkg"; $makecmd = "pkgfake" if $opt_fake; ## do we have a package bundle? my $zippkgs = $pkgname; @bundlepkgs = ($pkgname); if($description->{"Contains"}) { print "\nLooks like '${pkg}' is a package bundle\n"; $is_bundle = 1; @bundlepkgs = split(/\s+/, $description->{"Contains"}); foreach $ppkg (@bundlepkgs) { die "ERROR: package $pkg/$ppkg does not exist\n" unless -d "$pkg/$ppkg"; } $zippkgs = join(" ", @bundlepkgs); ## join up description files R_runR("tools:::.vcreate_bundle_package_descriptions('${pkg}')", "--vanilla", "R_DEFAULT_PACKAGES=NULL LC_ALL=C"); $pkgdir .= "/$pkgname"; $intname = $description->{"Bundle"}; } print "\n"; if($description->{"Depends"}) { my %result = R_run_R("options(warn=1); tools:::.test_package_depends_R_version('$pkg')", "--vanilla --slave", "R_DEFAULT_PACKAGES=NULL LC_ALL=C"); if($result{"status"}) { my @out = grep(!/^\>/, @{$result{"out"}}); print join("\n", @out) . "\n\n"; next; } } ## Now we are ready to install if($safe) { if($safe == 2) { $lockdir = $library . "/" . "00LOCK-" . $pkgname; printf "Using lock directory $lockdir\n" if $opt_debug; } else { $lockdir = $library . "/" . "00LOCK"; } if(-d $lockdir || -f $lockdir) { print "Failed to lock directory '${library}' for modifying\n"; print "Try removing '${lockdir}'\n"; if (defined($tardir)) { chdir($startdir); rmtree($tardir); } exit 3; } mkpath($lockdir); if(! -d $lockdir) { print "Failed to create lock directory '${library}'\n"; print "Permission problem?\n"; if (defined($tardir)) { chdir($startdir); rmtree($tardir); } exit 3; } } my $res; ## loop over pkgs in the bundle for $pkgname (@bundlepkgs) { if($opt_with_package_versions) { $decpkgname = $pkgname . "_" . $description->{"Version"}; } else { $decpkgname = $pkgname; } $dpkg = $library. "/" . $decpkgname; # if exists, move existing installation to $lockdir system("mv $dpkg $lockdir") if ($safe && -d $dpkg); system("make --no-print-directory -C $R_HOME/src/gnuwin32 PKGDIR=$pkgdir RLIB=$library pkgclean-$pkgname") if ($opt_preclean); $description = new R::Dcf("$pkgdir/$pkgname/DESCRIPTION"); my $lazyload = 1; my $tmp = parse_description_field("LazyLoad"); $lazyload = $tmp if($tmp >= 0); print "*** lazyload is $lazyload ***\n" if $opt_debug; my $lazydata = 0; my $tmp = parse_description_field("LazyData"); $lazydata = $tmp if($tmp >= 0); print "*** lazydata is $lazydata ***\n" if $opt_debug; my $opt = ""; if($opt_auto_zip || $opt_build) { my $dir = $pkgdir. "/" . $pkgname . "/data"; my $tmp = parse_description_field("ZipData"); if(-d $dir && $tmp != 0) { my $Rout = "datacnt"; my @tmp = `ls.exe -s1 $dir`; # system > no longer works my $out = 0, $nodups=1, $prev="", $this; foreach $line (@tmp) { chomp $line; if($line =~ /^total/) { $line =~ s/^total //; $out = $line; } else { ## if there is only one file there will not be a total $this = $line; $this =~ s/([ 0-9]*)(.*)/$1/; $out = $this unless $out > 0; $line =~ s/\s*[0-9]+\ //; $line =~ s/\.[a-zA-Z]+$//; $nodups = 0 if $line eq $prev; $prev = $line; } } # print ("out was $out, nodups was $nodups\n"); $opt = "$pkgname-ZIPDATA=zip" if ($tmp == 1) || ($out > 100 && $nodups); } $dir = $pkgdir. "/" . $pkgname . "/man"; if(-d $dir) { opendir(DIR, $dir) or die "cannot opendir $dir: $!"; @files = grep{ /\.Rd$/ } readdir(DIR); closedir(DIR); $opt = $opt . " $pkgname-HELP=ziponly" if @files > 20; } print "Using auto-selected zip options '$opt'\n"; } else { $opt = "$pkgname-ZIPDATA=zip" if ($opt_use_zip || $opt_use_zip_data); $opt = $opt . " $pkgname-HELP=ziponly" if ($opt_use_zip || $opt_use_zip_help); } $opt = $opt . " DPKG=$dpkg"; $opt = $opt . " $pkgname-LAZYDATA=true" if $lazydata; $opt = $opt . " $pkgname-LAZY=true" if $lazyload > 0; # print "make --no-print-directory -C $R_HOME/src/gnuwin32 PKGDIR=$pkgdir PKGNAME=$decpkgname RLIB=$library $opt $helpflags $makecmd-$pkgname\n"; $res = system("make --no-print-directory -C $R_HOME/src/gnuwin32 PKGDIR=$pkgdir PKGNAME=$decpkgname RLIB=$library $opt $helpflags $makecmd-$pkgname"); if($res == 0) { ## add MD5 sums last of all my $cmd = "make --no-print-directory -f $R_HOME/src/gnuwin32/MakePkg "; $cmd .= "BUILD=$BUILD RHOME=$R_HOME DPKG=$library/$decpkgname md5sums"; # print "$cmd\n"; $res = system($cmd); } # end of if($res) if($res) { printf "*** Installation of $pkgname failed ***\n"; } system("make --no-print-directory -C $R_HOME/src/gnuwin32 PKGDIR=$pkgdir RLIB=$library pkgclean-$pkgname") if ($opt_clean); print("\n"); if($res) { if($is_bundle) { # back out the whole bundle for $pkgname (@bundlepkgs) { $pkgname .= "_" . $description->{"Version"} if $opt_with_package_versions; rmtree($library . "/" . $pkgname); $dppkg = $lockdir . "/" . $pkgname; system("mv $dppkg $library") if ($safe && -d $dppkg); } } else { print "Removing '$dpkg'\n"; rmtree($dpkg); # move back any previously installed package $dppkg = $lockdir . "/" . $decpkgname; if ($safe && -d $dppkg) { print "Restoring previous '$dpkg'\n"; system("mv $dppkg $library"); } } last; } } # end of loop over bundle if(!$res && $opt_build) { my $filename = "${pkgname}_" . $description->{"Version"}; chdir($library); $startdir = Win32::GetShortPathName($startdir) if $startdir =~ / /; system("rm -f $startdir/$filename.zip"); system("zip -r9Xq $startdir/$filename.zip $zippkgs"); if ($is_bundle) { ## Need to get the toplevel files in there $currentDir = cwd(); chdir($canonpkg); system("zip $startdir/$filename.zip $topLevelFiles"); chdir($currentDir); } chdir($startdir); print "packaged installation of "; if ($is_bundle) {print "bundle";} else {print "package";}; print " '$pkgname' as $filename.zip\n"; } # wipe out lockdir rmtree($lockdir) if $safe; if (defined($tardir)) { chdir($startdir); rmtree($tardir); } exit $res if $res; print("\* DONE ($intname)\n"); } sub parse_description_field { # fieldname my ($field) = @_; my $tmp = $description->{$field}, $res = -1; if($tmp ne "") { $tmp = lc $tmp; $res = 1 if ($tmp eq "yes") || ($tmp eq "true"); $res = 0 if ($tmp eq "no") || ($tmp eq "false"); # or throw an error, but then we need to recover } return $res; } sub usage { print STDERR <. END exit 0; }