[svn:parrot] r39165 - in trunk: . config/gen/makefiles config/init examples/languages/abc/config/makefiles examples/languages/squaak/config/makefiles examples/pir/befunge/config/makefiles ext/SQLite3 lib/Parrot t/tools/install t/tools/install/testlib tools/dev

jkeenan at svn.parrot.org jkeenan at svn.parrot.org
Mon May 25 01:15:29 UTC 2009


Author: jkeenan
Date: Mon May 25 01:15:28 2009
New Revision: 39165
URL: https://trac.parrot.org/parrot/changeset/39165

Log:
Merge better_install_tools branch into trunk.  This branch superseded the install_tools branch but had the same objective:  Refactor code found in both tools/dev/install_files.pl and tools/dev/install_dev_files.pl out of those files, place in lib/Parrot/Install.pm, test those subroutines.  Cf.:  https://trac.parrot.org/parrot/ticket/426.  wayland++ for initiating this project and keeping it moving forward.

Added:
   trunk/lib/Parrot/Install.pm
      - copied unchanged from r39164, branches/better_install_tools/lib/Parrot/Install.pm
   trunk/t/tools/install/01-create_directories.t
      - copied unchanged from r39164, branches/better_install_tools/t/tools/install/01-create_directories.t
   trunk/t/tools/install/02-install_files.t
      - copied unchanged from r39164, branches/better_install_tools/t/tools/install/02-install_files.t
   trunk/t/tools/install/03-lines_to_files.t
      - copied unchanged from r39164, branches/better_install_tools/t/tools/install/03-lines_to_files.t
   trunk/t/tools/install/testlib/MANIFEST.1defective
      - copied unchanged from r39164, branches/better_install_tools/t/tools/install/testlib/MANIFEST.1defective
   trunk/t/tools/install/testlib/README
      - copied unchanged from r39164, branches/better_install_tools/t/tools/install/testlib/README
   trunk/t/tools/install/testlib/generated_pseudo_with_dupe
      - copied unchanged from r39164, branches/better_install_tools/t/tools/install/testlib/generated_pseudo_with_dupe
   trunk/t/tools/install/testlib/phony
      - copied unchanged from r39164, branches/better_install_tools/t/tools/install/testlib/phony
   trunk/t/tools/install/testlib/phony.exe
      - copied unchanged from r39164, branches/better_install_tools/t/tools/install/testlib/phony.exe
Modified:
   trunk/MANIFEST
   trunk/config/gen/makefiles/root.in
   trunk/config/init/install.pm
   trunk/examples/languages/abc/config/makefiles/root.in
   trunk/examples/languages/squaak/config/makefiles/root.in
   trunk/examples/pir/befunge/config/makefiles/root.in
   trunk/ext/SQLite3/Makefile.in
   trunk/t/tools/install/dev_overall.t
   trunk/t/tools/install/overall.t
   trunk/tools/dev/install_dev_files.pl
   trunk/tools/dev/install_files.pl
   trunk/tools/dev/mk_language_shell.pl

Modified: trunk/MANIFEST
==============================================================================
--- trunk/MANIFEST	Mon May 25 00:47:41 2009	(r39164)
+++ trunk/MANIFEST	Mon May 25 01:15:28 2009	(r39165)
@@ -1,7 +1,7 @@
 # ex: set ro:
 # $Id$
 #
-# generated by tools/dev/mk_manifest_and_skip.pl Sun May 24 12:35:39 2009 UT
+# generated by tools/dev/mk_manifest_and_skip.pl Sun May 24 16:30:09 2009 UT
 #
 # See tools/dev/install_files.pl for documentation on the
 # format of this file.
@@ -1049,6 +1049,7 @@
 lib/Parrot/IO/Directory.pm                                  [devel]lib
 lib/Parrot/IO/File.pm                                       [devel]lib
 lib/Parrot/IO/Path.pm                                       [devel]lib
+lib/Parrot/Install.pm                                       [devel]lib
 lib/Parrot/Manifest.pm                                      [devel]lib
 lib/Parrot/Op.pm                                            [devel]lib
 lib/Parrot/OpTrans.pm                                       [devel]lib
@@ -2002,9 +2003,14 @@
 t/tools/dev/searchops.t                                     [test]
 t/tools/dev/searchops/samples.pm                            [test]
 t/tools/dump_pbc.t                                          [test]
+t/tools/install/01-create_directories.t                     [test]
+t/tools/install/02-install_files.t                          [test]
+t/tools/install/03-lines_to_files.t                         [test]
 t/tools/install/dev_overall.t                               [test]
 t/tools/install/overall.t                                   [test]
 t/tools/install/testlib/LICENSE                             [test]
+t/tools/install/testlib/MANIFEST.1defective                 [test]
+t/tools/install/testlib/README                              []doc
 t/tools/install/testlib/compilers/nqp/bootstrap/actions.pm  [test]
 t/tools/install/testlib/compilers/pge/PGE.pir               [test]
 t/tools/install/testlib/dev_generated_pseudo                [test]
@@ -2014,11 +2020,14 @@
 t/tools/install/testlib/docs/pct/past_building_blocks.pod   [test]
 t/tools/install/testlib/docs/resources/phony_resource       [test]
 t/tools/install/testlib/generated_pseudo                    [test]
+t/tools/install/testlib/generated_pseudo_with_dupe          [test]
 t/tools/install/testlib/include/parrot/charset.h            [test]
 t/tools/install/testlib/install_config.fpmc                 [test]
 t/tools/install/testlib/lib/Parrot/Configure.pm             [test]
 t/tools/install/testlib/manifest_pseudo                     [test]
 t/tools/install/testlib/parrot.pc                           [test]
+t/tools/install/testlib/phony                               [test]
+t/tools/install/testlib/phony.exe                           [test]
 t/tools/install/testlib/runtime/parrot/include/sockets.pasm [test]
 t/tools/install/testlib/runtime/parrot/library/TGE.pbc      [test]
 t/tools/install/testlib/src/ops/ops.num                     [test]

Modified: trunk/config/gen/makefiles/root.in
==============================================================================
--- trunk/config/gen/makefiles/root.in	Mon May 25 00:47:41 2009	(r39164)
+++ trunk/config/gen/makefiles/root.in	Mon May 25 01:15:28 2009	(r39165)
@@ -32,7 +32,7 @@
 LIB_DIR         := @libdir@
 INCLUDE_DIR     := @includedir@
 DATA_DIR        := @datadir@
-DOC_DIR         := @doc_dir@
+DOC_DIR         := @docdir@
 VERSION_DIR     = @versiondir@
 
 

Modified: trunk/config/init/install.pm
==============================================================================
--- trunk/config/init/install.pm	Mon May 25 00:47:41 2009	(r39164)
+++ trunk/config/init/install.pm	Mon May 25 01:15:28 2009	(r39165)
@@ -105,7 +105,7 @@
         srcdir         => $srcdir,
 
         # parrot internal use only
-        doc_dir        => $datadir . "/doc",
+        docdir         => $datadir . "/doc",
         versiondir     => $versiondir,
     );
 

Modified: trunk/examples/languages/abc/config/makefiles/root.in
==============================================================================
--- trunk/examples/languages/abc/config/makefiles/root.in	Mon May 25 00:47:41 2009	(r39164)
+++ trunk/examples/languages/abc/config/makefiles/root.in	Mon May 25 01:15:28 2009	(r39165)
@@ -8,7 +8,7 @@
 VERSION       := @versiondir@
 BIN_DIR       := @bindir@
 LIB_DIR       := @libdir@$(VERSION)
-DOC_DIR       := @doc_dir@$(VERSION)
+DOC_DIR       := @docdir@$(VERSION)
 MANDIR        := @mandir@$(VERSION)
 
 # Set up extensions

Modified: trunk/examples/languages/squaak/config/makefiles/root.in
==============================================================================
--- trunk/examples/languages/squaak/config/makefiles/root.in	Mon May 25 00:47:41 2009	(r39164)
+++ trunk/examples/languages/squaak/config/makefiles/root.in	Mon May 25 01:15:28 2009	(r39165)
@@ -8,7 +8,7 @@
 VERSION       := @versiondir@
 BIN_DIR       := @bindir@
 LIB_DIR       := @libdir@$(VERSION)
-DOC_DIR       := @doc_dir@$(VERSION)
+DOC_DIR       := @docdir@$(VERSION)
 MANDIR        := @mandir@$(VERSION)
 
 # Set up extensions

Modified: trunk/examples/pir/befunge/config/makefiles/root.in
==============================================================================
--- trunk/examples/pir/befunge/config/makefiles/root.in	Mon May 25 00:47:41 2009	(r39164)
+++ trunk/examples/pir/befunge/config/makefiles/root.in	Mon May 25 01:15:28 2009	(r39165)
@@ -8,7 +8,7 @@
 VERSION       := @versiondir@
 BIN_DIR       := @bindir@
 LIB_DIR       := @libdir@$(VERSION)
-DOC_DIR       := @doc_dir@$(VERSION)
+DOC_DIR       := @docdir@$(VERSION)
 MANDIR        := @mandir@$(VERSION)
 
 # Set up extensions

Modified: trunk/ext/SQLite3/Makefile.in
==============================================================================
--- trunk/ext/SQLite3/Makefile.in	Mon May 25 00:47:41 2009	(r39164)
+++ trunk/ext/SQLite3/Makefile.in	Mon May 25 01:15:28 2009	(r39165)
@@ -10,10 +10,10 @@
 PREFIX          = @prefix@
 EXEC_PREFIX     = @exec_prefix@
 DESTDIR         =
-BIN_DIR         = @bin_dir@
-LIB_DIR         = @lib_dir@
-INCLUDE_DIR     = @include_dir@
-DOC_DIR         = @doc_dir@
+BIN_DIR         = @bindir@
+LIB_DIR         = @libdir@
+INCLUDE_DIR     = @includedir@
+DOC_DIR         = @docdir@
 INC_DIR         = @inc@
 RM_F             = @rm_f@
 RM_RF            = @rm_rf@

Copied: trunk/lib/Parrot/Install.pm (from r39164, branches/better_install_tools/lib/Parrot/Install.pm)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/lib/Parrot/Install.pm	Mon May 25 01:15:28 2009	(r39165, copy of r39164, branches/better_install_tools/lib/Parrot/Install.pm)
@@ -0,0 +1,239 @@
+package Parrot::Install;
+# Copyright (C) 2001-2009, Parrot Foundation.
+# $Id$
+use strict;
+use warnings;
+use File::Basename qw(dirname);
+use File::Copy;
+use File::Path; # mkpath
+use File::Spec;
+use base qw( Exporter );
+our @EXPORT_OK = qw(
+    lines_to_files
+    create_directories
+    install_files
+);
+
+#################### DOCUMENTATION ####################
+
+=head1 NAME
+
+Parrot::Install - Functionality for installation programs
+
+=head1 SYNOPSIS
+
+    use Parrot::Install qw(
+        install_files
+        create_directories
+        lines_to_files
+    );
+
+=head1 DESCRIPTION
+
+This module exports on demand only three subroutines used in the Parrot
+installation programs F<tools/dev/install_files.pl> and
+F<tools/dev/install_dev_files.pl>.  The subroutines are tested by tests found
+in F<t/tools/install/>.
+
+=head1 SUBROUTINES
+
+=head2 C<lines_to_files()>
+
+B<Purpose:> Suck in the lines from the mentioned manifests, and turn them into
+file locations.
+
+B<Arguments:> List of five scalars.
+
+    ($files, $directories) =
+        lines_to_files(
+            \%metatransforms,
+            \@transformorder,
+            \@manifests,
+            \%options,
+            $parrotdir,
+        );
+
+B<Return Value:> List of three scalars.
+
+B<Comment:>
+
+=cut
+
+sub lines_to_files {
+    my ($metatransforms, $transformorder, $manifests_ref,
+        $options_ref, $parrotdir) = @_;
+    my @files;
+    my %directories;
+    my($tkey, $thash);
+    my $filehash;
+
+    # We'll report multiple occurrences of the same file
+    my(%seen);
+
+    # Check $manifests_ref
+    ref($manifests_ref) eq 'ARRAY'
+        or die "Manifests must be listed in an array reference: $!";
+    @{ $manifests_ref } > 0 or die "No manifests specified";
+
+    # Check $transformorder
+    ref($transformorder) eq 'ARRAY'
+        or die "Transform order should be an array of keys\n";
+
+    @ARGV = @{ $manifests_ref };
+    LINE: while ( my $entry = <> ) {
+        chomp $entry;
+
+        $entry =~ s/\#.*//;    # Ignore comments
+        next if $entry =~ /^\s*$/;    # Skip blank lines
+
+        my ( $src, $meta, $dest ) = split( /\s+/, $entry );
+        $dest = $src unless $dest;
+
+        if ( $seen{$src}++ ) {
+            print STDERR "$ARGV:$.: Duplicate entry $src\n";
+        }
+
+        # Parse out metadata
+        die "Malformed line in MANIFEST: $entry" if not defined $meta;
+        my $generated = $meta =~ s/^\*//;
+        my ($package) = $meta =~ /^\[(.*?)\]/;
+        $meta =~ s/^\[(.*?)\]//;
+        next unless $package;    # Skip if this file belongs to no package
+
+        my $plist = defined ( $options_ref->{packages})
+            ? $options_ref->{packages}
+            : '.*';
+        next unless $package =~ /$plist/;
+
+        my %metadata;
+        @metadata{ split( /,/, $meta ) } = ();
+        $metadata{$_} = 1 for ( keys %metadata );          # Laziness
+
+        $filehash = {
+            Source => $src,
+            Dest => $dest,
+            DestDirs => [],
+        };
+
+        FIXFILE: {
+            # Have to catch this case early for some unknown reason
+            if ( $entry =~ /^runtime/ ) {
+                $filehash->{Dest} =~ s/^runtime\/parrot\///;
+                $filehash->{Dest} = File::Spec->catdir(
+                    $options_ref->{libdir}, $parrotdir, $filehash->{Dest}
+                );
+                last FIXFILE;
+            }
+            foreach my $tkey (@$transformorder) {
+                $thash = $metatransforms->{$tkey};
+                unless($thash->{ismeta} ? $metadata{$tkey} : $entry =~ /$tkey/) { next; }
+                $filehash = &{ $thash->{transform} }($filehash);
+                ref($filehash) eq 'HASH' or die "Error: transform didn't return a hash for key '$tkey'\n";
+                $filehash->{Dest} = File::Spec->catdir(
+                    $options_ref->{$thash->{optiondir} . 'dir'},
+                    @{ $filehash->{DestDirs} },
+                    $filehash->{Dest}
+                );
+                last FIXFILE;
+            }
+            die "Unknown install location in MANIFEST for file '$entry'\n";
+        }
+
+        if(! $filehash->{Installable}) {
+            $filehash->{Dest} = File::Spec->catdir( $options_ref->{buildprefix}, $filehash->{Dest} )
+                if $options_ref->{buildprefix};
+        }
+
+        $directories{ dirname($filehash->{Dest}) } = 1;
+        push( @files, $filehash );
+    }
+    continue {
+        close ARGV if eof;    # Reset line numbering for each input file
+    }
+
+    (grep { ! ref } @files) and die "lines_to_files from Parrot::Install created a bad hash!\n";
+    return(\@files, \%directories);
+}
+
+=head2 C<create_directories()>
+
+B<Purpose:> Creates the directories passed in.
+
+B<Arguments:>  Two scalar arguments.
+
+    create_directories(
+        $destination_directory,
+        $directories_hash_ref,
+    );
+
+B<Return Value:>  True value.
+
+B<Comment:>
+
+=cut
+
+sub create_directories {
+    my($destdir, $directories) = @_;
+
+    mkpath([
+        grep { ! -d } map { $destdir . $_ } keys %$directories
+    ],0,0777);
+}
+
+=head2 C<install_files()>
+
+B<Purpose:> Install the mentioned files into the appropriate locations.
+
+    install_files(
+        $destination_directory,
+        $dry_run_option,
+        $list_of_files_and_executables,
+    );
+
+B<Arguments:>  Takes two scalar arguments, followed by a reference to a
+list consisting of hashes.
+
+B<Return Value:>  True value.
+
+B<Comment:>
+
+=cut
+
+sub install_files {
+    my($destdir, $dryrun, $files) = @_;
+    my($src, $dest, $mode);
+
+    ref($files) eq 'ARRAY' or die "Error: parameter \$files must be an array\n";
+    print("Installing ...\n");
+    foreach my $el ( @$files ) {
+        unless(ref($el) eq 'HASH') {
+            my($ref) = ref($el);
+            warn "Bad reference passed in \$files (want a HASH, got a '$ref')\n";
+            next;
+        }
+        ( $src, $dest ) = map { $el->{$_} } qw(Source Dest);
+        $dest = $destdir . $dest;
+        if ( $dryrun ) {
+            print "$src -> $dest\n";
+            next;
+        }
+        else {
+            next unless -e $src;
+            next if $^O eq 'cygwin' and -e "$src.exe"; # stat works, copy not
+            copy( $src, $dest ) or die "Error: couldn't copy $src to $dest: $!\n";
+            print "$dest\n";
+        }
+        $mode = ( stat($src) )[2];
+        chmod $mode, $dest;
+    }
+    return 1;
+}
+
+1;
+
+# Local Variables:
+#   mode: cperl
+#   cperl-indent-level: 4
+#   fill-column: 100
+# End:
+# vim: expandtab shiftwidth=4:

Copied: trunk/t/tools/install/01-create_directories.t (from r39164, branches/better_install_tools/t/tools/install/01-create_directories.t)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/t/tools/install/01-create_directories.t	Mon May 25 01:15:28 2009	(r39165, copy of r39164, branches/better_install_tools/t/tools/install/01-create_directories.t)
@@ -0,0 +1,101 @@
+#! perl
+# Copyright (C) 2007, Parrot Foundation.
+# $Id$
+# 01-create_directories.t
+
+use strict;
+use warnings;
+
+use Test::More tests =>  6;
+use Carp;
+use File::Path qw( mkpath );
+use File::Temp qw( tempdir );
+use lib qw( lib );
+use Parrot::Install qw(
+    create_directories
+);
+use IO::CaptureOutput qw( capture );
+
+{
+    my $tdir = tempdir( CLEANUP => 1 );
+    $tdir .= '/';
+
+    my @dirs = qw(foo/bar foo/bar/baz);
+    create_directories($tdir, { map { $_ => 1 } @dirs });
+    my $dirs_seen = 0;
+    foreach my $d (@dirs) {
+        $dirs_seen++ if -d "$tdir$d";
+    }
+    is($dirs_seen, 2, 'got expected number of directories created');
+}
+
+{
+    my $tdir = tempdir( CLEANUP => 1 );
+    $tdir .= '/';
+
+    my @dirs = qw(foo/bar foo/bar/baz);
+    my @created = mkpath( "$tdir$dirs[0]" );
+    ok( ( -d $created[0] ),
+        "one directory created before create_directories() is called" );
+
+    create_directories($tdir, { map { $_ => 1 } @dirs });
+    my $dirs_seen = 0;
+    foreach my $d (@dirs) {
+        $dirs_seen++ if -d "$tdir$d";
+    }
+    is($dirs_seen, 2,
+        "create_directories() handled case where one directory already existed" );
+}
+
+{
+    my $tdir = tempdir( CLEANUP => 1 );
+    $tdir .= '/';
+
+    my @dirs = qw(foo/bar foo/bar/baz);
+    my @created = mkpath( $tdir . 'foo' );
+    ok( ( -d $created[0] ),
+        "one directory created before create_directories() is called" );
+
+    create_directories($tdir, { map { $_ => 1 } @dirs });
+    my $dirs_seen = 0;
+    foreach my $d (@dirs) {
+        $dirs_seen++ if -d "$tdir$d";
+    }
+    is($dirs_seen, 2,
+        "create_directories() handled case where one path partially existed" );
+}
+
+pass("Completed all tests in $0");
+
+################### DOCUMENTATION ###################
+
+=head1 NAME
+
+01-create_directories.t - test subroutines exported by C<Parrot::Install>
+
+=head1 SYNOPSIS
+
+    % prove t/tools/install/01-create_directories.t
+
+=head1 DESCRIPTION
+
+The files in this directory test functionality used by
+F<tools/dev/install_files.pl> and F<tools/dev/install_dev_files.pl> and
+exported by F<lib/Parrot/Install.pm>.
+
+=head1 AUTHOR
+
+James E Keenan and Timothy S Nelson
+
+=head1 SEE ALSO
+
+Parrot::Install, F<tools/dev/install_files.pl>, F<tools/dev/install_dev_files.pl>
+
+=cut
+
+# Local Variables:
+#   mode: cperl
+#   cperl-indent-level: 4
+#   fill-column: 100
+# End:
+# vim: expandtab shiftwidth=4:

Copied: trunk/t/tools/install/02-install_files.t (from r39164, branches/better_install_tools/t/tools/install/02-install_files.t)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/t/tools/install/02-install_files.t	Mon May 25 01:15:28 2009	(r39165, copy of r39164, branches/better_install_tools/t/tools/install/02-install_files.t)
@@ -0,0 +1,283 @@
+#! perl
+# Copyright (C) 2007, Parrot Foundation.
+# $Id$
+# 02-install_files.t
+
+use strict;
+use warnings;
+
+use Test::More tests => 18;
+use Carp;
+use Cwd;
+use File::Copy;
+use File::Path qw( mkpath );
+use File::Temp qw( tempdir );
+use lib qw( lib );
+use Parrot::Install qw(
+    install_files
+    create_directories
+);
+use IO::CaptureOutput qw( capture );
+
+my $cwd = cwd();
+my $testsourcedir = qq{$cwd/t/tools/install/testlib};
+
+{
+    my $tdir = tempdir( CLEANUP => 1 );
+    $tdir .= '/';
+
+    my @dirs = qw(foo/bar foo/bar/baz);
+    create_directories($tdir, { map { $_ => 1 } @dirs });
+
+    {
+        my ( $stdout, $stderr, $rv );
+    
+        eval {
+            capture(
+                sub { $rv = install_files($tdir, 1); },
+                \$stdout,
+                \$stderr,
+            );
+        };
+        like($@, qr/Error: parameter \$files must be an array/s, "Catches non-ARRAY \$files");
+    }
+}
+
+{
+    my $tdir = tempdir( CLEANUP => 1 );
+    $tdir .= '/';
+
+    my @dirs = qw(foo/bar foo/bar/baz);
+    create_directories($tdir, { map { $_ => 1 } @dirs });
+
+    # Case where element in @files is not a hash ref
+    my $files_ref = [ q[] ];
+
+    {
+        my ( $stdout, $stderr, $rv );
+        capture(
+            sub { $rv = install_files($tdir, 0, $files_ref); },
+            \$stdout,
+            \$stderr,
+        );
+        like($stderr, qr/Bad reference passed in \$files/, "Catches non-HASH files");
+    
+        like( $stdout, qr/Installing \.\.\./, 
+            'Got expected installation message' );
+    }
+}
+
+{
+    my $tdir = tempdir( CLEANUP => 1 );
+    $tdir .= '/';
+
+    my @dirs = qw(foo/bar foo/bar/baz);
+    create_directories($tdir, { map { $_ => 1 } @dirs });
+
+    my $files_ref = [ {
+        Source => "$testsourcedir/README",
+        Dest => "$dirs[0]/README",
+    } ];
+
+    {
+        my ( $stdout, $stderr, $rv );
+
+        capture(
+            sub { $rv = install_files($tdir, 1, $files_ref); },
+            \$stdout,
+            \$stderr,
+        );
+        ok( $rv, 'install_files() completed successfully in dry-run case' );
+    
+        my $files_created = 0;
+        foreach my $el (@$files_ref) {
+            $files_created++ if -f $tdir . $el->{Dest};
+        }
+        is( $files_created, 0, 'Dry run, so no files created' );
+
+        like( $stdout, qr/Installing.*README.*README/s,
+            'Got expected installation message' );
+        $stdout =~ qr/Installing.*README.*README/s or print "Warning was: $stderr";
+    }
+
+    {
+        my ( $stdout, $stderr, $rv );
+        capture(
+            sub { $rv = install_files($tdir, 0, $files_ref); },
+            \$stdout,
+            \$stderr,
+        );
+        ok( $rv, 'install_files() completed successfully in production case' );
+    
+        my $files_created = 0;
+        foreach my $el (@$files_ref) {
+            $files_created++ if -f "$tdir$el->{Dest}";
+        }
+        is( $files_created, 1, 'Production, so 1 file created' );
+
+        like( $stdout, qr/Installing.*README/s,
+            'Got expected installation message' );
+    }
+}
+
+{
+    local $^O = 'cygwin';
+    my $tdir = tempdir( CLEANUP => 1 );
+    chdir $tdir or die "Unable to change to testing directory: $!";
+    $tdir .= '/';
+
+    my @dirs = qw(foo/bar foo/bar/baz);
+    create_directories($tdir, { map { $_ => 1 } @dirs });
+
+    my @testingfiles = qw( README phony );
+    foreach my $f ( @testingfiles ) {
+        copy "$testsourcedir/$f", "$tdir/$f"
+            or die "Unable to copy $f prior to testing: $!";
+    }
+    my $files_ref = [
+        {
+            Source => "$tdir/README",
+            Dest => "$dirs[0]/README",
+        },
+        {
+            Source => "$tdir/phony",
+            Dest => "$dirs[0]/phony",
+        },
+    ];
+
+    {
+        my ( $stdout, $stderr, $rv );
+        capture(
+            sub { $rv = install_files($tdir, 0, $files_ref); },
+            \$stdout,
+            \$stderr,
+        );
+        ok( $rv, 'install_files() completed successfully in mock-Cygwin case' );
+    
+        my $files_created = 0;
+        foreach my $el (@$files_ref) {
+            $files_created++ if -f "$tdir$el->{Dest}";
+        }
+        is( $files_created, 2, 'Production, so 2 files created' );
+
+        like( $stdout, qr/Installing.*README.*phony/s,
+            'Got expected installation message' );
+    }
+    chdir $cwd or die "Unable to change back to starting directory: $!";
+}
+
+{
+    local $^O = 'cygwin';
+    my $tdir = tempdir( CLEANUP => 1 );
+    chdir $tdir or die "Unable to change to testing directory: $!";
+    $tdir .= '/';
+
+    my @dirs = qw(foo/bar foo/bar/baz);
+    create_directories($tdir, { map { $_ => 1 } @dirs });
+
+    my @testingfiles = qw( README phony phony.exe );
+    foreach my $f ( @testingfiles ) {
+        copy "$testsourcedir/$f", "$tdir/$f"
+            or die "Unable to copy $f prior to testing: $!";
+    }
+    my $files_ref = [
+        {
+            Source => "$tdir/README",
+            Dest => "$dirs[0]/README"
+        },
+        {
+            Source => "$tdir/phony",
+            Dest => "$dirs[0]/phony"
+        },
+        {
+            Source => "$tdir/phony.exe",
+            Dest => "$dirs[0]/phony.exe"
+        },
+    ];
+
+    {
+        my ( $stdout, $stderr, $rv );
+        capture(
+            sub { $rv = install_files($tdir, 0, $files_ref); },
+            \$stdout,
+            \$stderr,
+        );
+        ok( $rv, 'install_files() completed successfully in mock-Cygwin case' );
+    
+        my $files_created = 0;
+        foreach my $el (@$files_ref) {
+            $files_created++ if -f "$tdir$el->{Dest}";
+        }
+        is( $files_created, 2,
+            'Production, so 2 files created; 1 file passed over' );
+
+        like( $stdout, qr/Installing.*README.*phony/s,
+            'Got expected installation message' );
+    }
+    chdir $cwd or die "Unable to change back to starting directory: $!";
+}
+
+{
+    my $tdir = tempdir( CLEANUP => 1 );
+    $tdir .= '/';
+
+    my @dirs = qw(foo/bar foo/bar/baz);
+    create_directories($tdir, { map { $_ => 1 } @dirs });
+
+    # Case where element in @files does not hold existent file
+    my $nonexistent = q{ajdpfadksjfjvjkvds} . $$;
+    my $files_ref = [
+        {
+            Source => $nonexistent,
+            Dest => "$dirs[0]/$nonexistent",
+        }
+    ];
+
+    {
+        my ( $stdout, $stderr, $rv );
+        capture(
+            sub { $rv = install_files($tdir, 0, $files_ref); },
+            \$stdout,
+            \$stderr,
+        );
+        ok( $rv, 'install_files() handled non-existent file as expected' );
+    
+        like( $stdout, qr/Installing \.\.\./, 
+            'Got expected installation message' );
+    }
+}
+
+pass("Completed all tests in $0");
+
+################### DOCUMENTATION ###################
+
+=head1 NAME
+
+02-install_files.t - test subroutines exported by C<Parrot::Install>
+
+=head1 SYNOPSIS
+
+    % prove t/tools/install/02-install_files.t
+
+=head1 DESCRIPTION
+
+The files in this directory test functionality used by
+F<tools/dev/install_files.pl> and F<tools/dev/install_dev_files.pl> and
+exported by F<lib/Parrot/Install.pm>.
+
+=head1 AUTHOR
+
+James E Keenan and Timothy S Nelson
+
+=head1 SEE ALSO
+
+Parrot::Install, F<tools/dev/install_files.pl>, F<tools/dev/install_dev_files.pl>
+
+=cut
+
+# Local Variables:
+#   mode: cperl
+#   cperl-indent-level: 4
+#   fill-column: 100
+# End:
+# vim: expandtab shiftwidth=4:

Copied: trunk/t/tools/install/03-lines_to_files.t (from r39164, branches/better_install_tools/t/tools/install/03-lines_to_files.t)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/t/tools/install/03-lines_to_files.t	Mon May 25 01:15:28 2009	(r39165, copy of r39164, branches/better_install_tools/t/tools/install/03-lines_to_files.t)
@@ -0,0 +1,292 @@
+#! perl
+# Copyright (C) 2007, Parrot Foundation.
+# $Id$
+# 03-lines_to_files.t
+
+use strict;
+use warnings;
+
+use Test::More tests =>  8;
+use Carp;
+use Cwd;
+use File::Copy;
+use File::Path qw( mkpath );
+use File::Temp qw( tempdir );
+use lib qw( lib );
+use Parrot::Install qw(
+    install_files
+    create_directories
+    lines_to_files
+);
+use IO::CaptureOutput qw( capture );
+
+my $cwd = cwd();
+my $testsourcedir = qq{$cwd/t/tools/install/testlib};
+
+my $parrotdir = q{};
+
+my %metatransforms = (
+    doc => {
+        optiondir => 'doc',
+        transform => sub {
+            my($dest) = @_;
+            # resources go in the top level of docs
+            $dest =~ s/^docs\/resources/resources/;
+            # other docs are actually raw Pod
+            $dest =~ s/^docs/pod/;
+            $parrotdir, $dest;
+        },
+    },
+    '.*' => {
+        optiondir => 'foo',
+        transform => sub {
+            return($_[0]);
+        }
+    }
+);
+my(@transformorder) = ('doc', '.*');
+
+my %badmetatransforms = (
+    doc => {
+        optiondir => 'doc',
+        transform => sub {
+            my($dest) = @_;
+            $dest =~ s/^docs\/resources/resources/; # resources go in the top level of docs
+            $dest =~ s/^docs/pod/; # other docs are actually raw Pod
+            $parrotdir, $dest;
+        },
+    },
+    '.*' => {
+        optiondir => 'foo',
+        transform => sub {
+            return(@_);
+        }
+    }
+);
+
+my @manifests = qw(MANIFEST MANIFEST.generated);
+my %options = (
+    packages => 'main',
+);
+
+my ($files_ref, $directories_ref, %badtransformorder);
+
+eval {
+    ($files_ref, $directories_ref) =
+        lines_to_files(
+            \%metatransforms,
+            \@transformorder,
+            {},
+            \%options,
+            $parrotdir,
+        );
+};
+like($@, qr/Manifests must be listed in an array reference/,
+    "Correctly detected lack of array ref as 3rd argument"
+);
+
+eval {
+    ($files_ref, $directories_ref) =
+        lines_to_files(
+            \%metatransforms,
+            \@transformorder,
+            [],
+            \%options,
+            $parrotdir,
+        );
+};
+like($@, qr/No manifests specified/,
+    "Correctly detected lack of manifest files"
+);
+
+eval {
+    ($files_ref, $directories_ref) =
+        lines_to_files(
+            \%metatransforms,
+            \%badtransformorder,
+            [ qw( MANIFEST MANIFEST.generated ) ],
+            \%options,
+            $parrotdir,
+        );
+};
+like($@, qr/Transform order should be an array of keys/,
+    "Correctly detected incorrect type for transform order"
+);
+
+{
+    my $tdir = tempdir( CLEANUP => 1 );
+    chdir $tdir or die "Unable to change to testing directory: $!";
+    copy qq{$testsourcedir/manifest_pseudo} => qq{$tdir/MANIFEST}
+        or die "Unable to copy file to tempdir for testing:  $!";
+    copy qq{$testsourcedir/generated_pseudo} => qq{$tdir/MANIFEST.generated}
+        or die "Unable to copy file to tempdir for testing:  $!";
+
+    my ($stdout, $stderr);
+    eval {
+        ($files_ref, $directories_ref) =
+            lines_to_files(
+                \%badmetatransforms,
+                \@transformorder,
+                [ qw( MANIFEST MANIFEST.generated ) ],
+                \%options,
+                $parrotdir,
+            );
+    };
+    like($@, qr/transform didn't return a hash for key/,
+        "Correctly detected transform with a bad return value"
+    );
+
+    chdir $cwd or die "Unable to return to starting directory: $!";
+}
+
+{
+    my $tdir = tempdir( CLEANUP => 1 );
+    chdir $tdir or die "Unable to change to testing directory: $!";
+    copy qq{$testsourcedir/manifest_pseudo} => qq{$tdir/MANIFEST}
+        or die "Unable to copy file to tempdir for testing:  $!";
+    copy qq{$testsourcedir/generated_pseudo_with_dupe} => qq{$tdir/MANIFEST.generated}
+        or die "Unable to copy file to tempdir for testing:  $!";
+
+    my ($stdout, $stderr);
+    eval {
+        capture(
+            sub {
+                ($files_ref, $directories_ref) =
+                    lines_to_files(
+                        \%metatransforms,
+                        \@transformorder,
+                        [ qw( MANIFEST MANIFEST.generated ) ],
+                        \%options,
+                        $parrotdir,
+                    );
+            },
+            \$stdout,
+            \$stderr,
+        );
+    };
+    like($stderr, qr/MANIFEST\.generated:\d+:\s+Duplicate entry/,
+        "Detected duplicate entries in one or more manifest files" );
+    is( scalar(grep { $_->{Installable} } @$files_ref), 0,
+        "No installable executables in this test" );
+
+    chdir $cwd or die "Unable to return to starting directory: $!";
+}
+
+{
+    my $tdir = tempdir( CLEANUP => 1 );
+    chdir $tdir or die "Unable to change to testing directory: $!";
+    my $defective_man = q{MANIFEST.1defective};
+    copy qq{$testsourcedir/$defective_man} => qq{$tdir/MANIFEST}
+        or die "Unable to copy file to tempdir for testing:  $!";
+
+    eval {
+        ($files_ref, $directories_ref) =
+            lines_to_files(
+                \%metatransforms,
+                \@transformorder,
+                [ q{MANIFEST} ],
+                \%options,
+                $parrotdir,
+            );
+    };
+    like($@, qr/Malformed line in MANIFEST: ChangeLog/,
+        "Got expected error message with defective manifest" );
+
+    chdir $cwd or die "Unable to return to starting directory: $!";
+}
+
+
+pass("Completed all tests in $0");
+
+################### DOCUMENTATION ###################
+
+=head1 NAME
+
+03-lines_to_files.t - test subroutines exported by C<Parrot::Install>
+
+=head1 SYNOPSIS
+
+    % prove t/tools/install/03-lines_to_files.t
+
+=head1 DESCRIPTION
+
+The files in this directory test functionality used by
+F<tools/dev/install_files.pl> and F<tools/dev/install_dev_files.pl> and
+exported by F<lib/Parrot/Install.pm>.
+
+=head1 AUTHOR
+
+James E Keenan and Timothy S Nelson
+
+=head1 SEE ALSO
+
+Parrot::Install, F<tools/dev/install_files.pl>, F<tools/dev/install_dev_files.pl>
+
+=cut
+
+# Local Variables:
+#   mode: cperl
+#   cperl-indent-level: 4
+#   fill-column: 100
+# End:
+# vim: expandtab shiftwidth=4:
+
+__END__
+
+# Can't safely run lines_to_files() more than once in a program until it's been fixed, 
+# and we can't fix it until its tested, so I've commented most of these out until we've
+# fixed lines_to_files() not to use @ARGV
+
+
+
+## In the code below:
+## - othertransforms needs to be merged into metatransforms
+## - transformorder needs to be added
+## - $installable_exe needs to be removed
+
+#{
+#    my($metatransforms, $transformorder, $manifests, $options, $parrotdir,
+#        $files, $installable_exe, $directories);
+#
+#    # First lines_to_files test
+##    eval { lines_to_files(); };
+##    $@ or die "lines_to_files didn't die with no parameters\n";
+##    ok($@ =~ /^.manifests must be an array reference$/, 'lines_to_files dies with bad parameters');
+#
+#    # Second lines_to_files test
+##    eval { lines_to_files(
+##        $metatransforms, $transformorder, 
+##        [qw(MANIFEST MANIFEST.generated)], 
+##        $options, $parrotdir
+##    ); };
+##    ok($@ =~ /^Unknown install location in MANIFEST for file/, 'fails for install locations not specified in transforms');
+#
+#    # Third lines_to_files test
+#    $metatransforms = {
+#        doc => {
+#            optiondir => 'doc',
+#            transform => sub {
+#                my($dest) = @_;
+#                $dest =~ s/^docs\/resources/resources/; # resources go in the top level of docs
+#                $dest =~ s/^docs/pod/; # other docs are actually raw Pod
+#                $parrotdir, $dest;
+#            },
+#        },
+#    };
+#    $othertransforms = {
+#        '.*' => {
+#            optiondir => 'foo',
+#            transform => sub {
+#                return(@_);
+#            }
+#        }
+#    };
+#
+#    ($files, $installable_exe, $directories) = lines_to_files(
+#        $metatransforms, $othertransforms, 
+#        [qw(MANIFEST MANIFEST.generated)], 
+#        { packages => 'main' }, $parrotdir
+#    );
+#    ok((ref($files) and ref($installable_exe) and ref($directories)), 'lines_to_files returns something vaguely sensible');
+#    ok(1, 'lines_to_files passed all tests');
+#}

Modified: trunk/t/tools/install/dev_overall.t
==============================================================================
--- trunk/t/tools/install/dev_overall.t	Mon May 25 00:47:41 2009	(r39164)
+++ trunk/t/tools/install/dev_overall.t	Mon May 25 01:15:28 2009	(r39165)
@@ -123,7 +123,8 @@
         my $des = File::Spec->catfile( $builddir, $testfiles{$f}{start} );
         copy $src, $des or croak "Unable to copy $f for testing: $!";
     }
-    my $cmd = qq{$^X $full_installer --prefix=$prefixdir};
+    my $addlib = qq{$cwd/lib};
+    my $cmd = qq{$^X -I$addlib $full_installer --prefix=$prefixdir};
     $cmd .= qq{ --includedir=$includedir};
     $cmd .= qq{ --libdir=$libdir};
     $cmd .= qq{ --versiondir=$versiondir};

Modified: trunk/t/tools/install/overall.t
==============================================================================
--- trunk/t/tools/install/overall.t	Mon May 25 00:47:41 2009	(r39164)
+++ trunk/t/tools/install/overall.t	Mon May 25 01:15:28 2009	(r39165)
@@ -15,6 +15,11 @@
 use File::Temp qw( tempdir );
 use lib qw( lib );
 use Parrot::Config qw( %PConfig );
+use Parrot::Install qw(
+    install_files
+    create_directories
+    lines_to_files
+);
 use IO::CaptureOutput qw( capture );
 
 my $DEBUG = 0;
@@ -108,7 +113,8 @@
         my $des = File::Spec->catfile( $builddir, $testfiles{$f}{start} );
         copy $src, $des or croak "Unable to copy $f for testing: $!";
     }
-    my $cmd = qq{$^X $full_installer --prefix=$prefixdir};
+    my $addlib = qq{$cwd/lib};
+    my $cmd = qq{$^X -I$addlib $full_installer --prefix=$prefixdir};
     $cmd .= qq{ --includedir=$includedir};
     $cmd .= qq{ --libdir=$libdir};
     $cmd .= qq{ --versiondir=$versiondir};
@@ -131,7 +137,6 @@
     my $expected = scalar keys %testfiles;
     foreach my $f ( keys %testfiles ) {
         my $des = $testfiles{$f}{end};
-        print STDERR "wanted:  $des\n" if $DEBUG;
         $seen++ if -f $des;
     }
     is( $seen, $expected,

Copied: trunk/t/tools/install/testlib/MANIFEST.1defective (from r39164, branches/better_install_tools/t/tools/install/testlib/MANIFEST.1defective)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/t/tools/install/testlib/MANIFEST.1defective	Mon May 25 01:15:28 2009	(r39165, copy of r39164, branches/better_install_tools/t/tools/install/testlib/MANIFEST.1defective)
@@ -0,0 +1,5 @@
+# $Id$
+#
+# generated by hand for testing purposes only
+CREDITS                       []    /destination/to/file
+ChangeLog

Copied: trunk/t/tools/install/testlib/README (from r39164, branches/better_install_tools/t/tools/install/testlib/README)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/t/tools/install/testlib/README	Mon May 25 01:15:28 2009	(r39165, copy of r39164, branches/better_install_tools/t/tools/install/testlib/README)
@@ -0,0 +1,155 @@
+This is Parrot, version 0.9.1
+------------------------------
+
+Parrot is Copyright (C) 2001-2009, Parrot Foundation.
+
+$Id$
+
+LICENSE INFORMATION
+-------------------
+
+This code is distributed under the terms of the Artistic License 2.0.
+For more details, see the full text of the license in the file LICENSE.
+
+PREREQUISITES
+-------------
+
+You need a C compiler, a linker, and a make program of course. If you will be
+linking with the ICU library you have to download and install it before
+configuring Parrot.
+
+Get it from http://www-306.ibm.com/software/globalization/icu/downloads.jsp
+
+You also need Perl 5.8.4 or newer, Storable 2.12 or newer, and Bundle::Parrot
+to run various configure and build scripts.
+
+For most of the platforms that we are supporting initially, Parrot should build
+out of the box.  PLATFORM lists our target platforms.
+
+The current configure system is primitive, as it's only a temporary solution.
+It will be happy with most any answers you decide to feed it. Garbage In,
+Garbage Out.
+
+INSTRUCTIONS
+------------
+
+For now, unpack your Parrot tarball, (if you're reading this, you've
+probably already done that) and type
+
+    perl Configure.pl
+
+to run the Configure script. The Configure.pl script extracts
+configuration from the running perl5 program.  Unfortunately, the perl5
+configuration is not set up to compile and link c++ programs, so you
+may need to explicitly tell Configure.pl which compiler and linker to
+use.  For example, to compile C files with 'cc', C++ files with
+'CC', and link everything together with 'CC', you would type
+
+    perl Configure.pl --cc=cc --cxx=CC --link=CC --ld=CC
+
+See "perl Configure.pl --help" for more options and docs/configuration.pod
+for more details.
+
+For systems like HPUX that don't have inet_pton please run
+
+    perl Configure.pl --define=inet_aton
+
+Running Configure.pl will generate a config.h header, a Parrot::Config
+module, platform files and many Makefiles.
+
+The file "myconfig" has an overview of configure settings.
+
+Next, run make. (Configure.pl will tell you which version of make it
+recommends for your system.)
+
+Now, the interpreter should build.  If you are building the ICU library
+(this is the default on most systems), you need to use GNU make instead
+(or something compatible with it).
+
+NOTE: If you have trouble linking parrot, this *may* be due to a pre-existing
+parrot version installed via 'make install'. Until this issue is resolved,
+you may have to delete the installed version of parrot before building a new
+one. Our apologies.
+
+You can test parrot by running "make test". You can run the tests in parallel
+with "make TEST_JOBS=3 test".
+
+You can run the full test suite with
+
+    make fulltest
+
+Note: PLATFORMS contains notes about whether test failures are expected
+on your system.
+
+On some systems you can install parrot:
+
+    make install
+
+This installs a bunch of files in /usr/local. The parrot executable is in
+/usr/local/bin.   Please note that this feature is currently experimental.
+(It's so experimental that you have to read the directions it gives you.)
+
+If you want to install Parrot into a non-standard location use:
+
+    perl Configure.pl --prefix=/Users/foo/parrot-0.7.0
+    make install
+
+But please note that dynamic libs will not be found for non-standard
+locations unless you set LD_LIBRARY_PATH or similar.
+
+Look at docs/parrot.pod and docs/intro.pod for where to go from here.  If you
+have any problems, see the section "How To Submit A Bug Report" in
+docs/submissions.pod.  These documents are in POD format. You can view these
+files with the command:
+
+    perldoc -F docs/intro.pod
+
+NOTES
+-----
+
+On some older computers with little RAM, the computed-goto dispatch core
+(ops/core_ops_cg.c) may take a while to compile or may fail to compile at all.
+You can pass a flag to Configure.pl (--cgoto=0) to disable the computed-goto
+core, at a slight cost in runtime speed.
+
+CHANGES
+-------
+
+For documentation on the user-visible changes between this version and
+previous versions, please see NEWS.
+
+MAILING LISTS
+-------------
+
+The mailing list for parrot is parrot-dev at lists.parrot.org. Subscribe by
+filling out the form at http://lists.parrot.org/mailman/listinfo/parrot-dev
+It is archived at http://lists.parrot.org/pipermail/parrot-dev/
+
+The old development list is archived at
+http://www.nntp.perl.org/group/perl.perl6.internals
+and available via NNTP at nntp://nntp.perl.org/perl.perl6.internals
+
+You can also read the (old) list via Google Groups at
+http://groups-beta.google.com/group/perl.perl6.internals
+
+FEEDBACK, PATCHES, etc.
+-----------------------
+
+See F<docs/submissions.pod> for more information on reporting bugs and
+submitting patches.
+
+WEB SITES
+---------
+
+The following web sites have all the information you need about Parrot:
+    http://www.parrot.org/
+    http://trac.parrot.org/
+    http://www.parrotblog.org/
+
+And Perl6:
+    http://rakudo.org/
+    http://dev.perl.org/perl6/
+    http://pugscode.org/
+
+Have fun,
+   The Parrot Team.

Copied: trunk/t/tools/install/testlib/generated_pseudo_with_dupe (from r39164, branches/better_install_tools/t/tools/install/testlib/generated_pseudo_with_dupe)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/t/tools/install/testlib/generated_pseudo_with_dupe	Mon May 25 01:15:28 2009	(r39165, copy of r39164, branches/better_install_tools/t/tools/install/testlib/generated_pseudo_with_dupe)
@@ -0,0 +1,10 @@
+# $Id$
+# See tools/dev/install_files.pl for documentation on the
+# format of this file.
+# Please re-sort this file after *EVERY* modification
+install_config.fpmc                               [main]lib
+parrot.pc                                         [main]pkgconfig
+parrot.pc                                         [main]pkgconfig
+# Local variables:
+#   mode: text
+# End:

Copied: trunk/t/tools/install/testlib/phony (from r39164, branches/better_install_tools/t/tools/install/testlib/phony)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/t/tools/install/testlib/phony	Mon May 25 01:15:28 2009	(r39165, copy of r39164, branches/better_install_tools/t/tools/install/testlib/phony)
@@ -0,0 +1 @@
+File used in testing of Parrot::Install.

Copied: trunk/t/tools/install/testlib/phony.exe (from r39164, branches/better_install_tools/t/tools/install/testlib/phony.exe)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/t/tools/install/testlib/phony.exe	Mon May 25 01:15:28 2009	(r39165, copy of r39164, branches/better_install_tools/t/tools/install/testlib/phony.exe)
@@ -0,0 +1 @@
+File used in testing of Parrot::Install; not a real executable.

Modified: trunk/tools/dev/install_dev_files.pl
==============================================================================
--- trunk/tools/dev/install_dev_files.pl	Mon May 25 00:47:41 2009	(r39164)
+++ trunk/tools/dev/install_dev_files.pl	Mon May 25 01:15:28 2009	(r39165)
@@ -57,9 +57,13 @@
 
 use strict;
 use warnings;
-use File::Basename qw(dirname basename);
-use File::Copy;
-use File::Spec;
+use File::Basename qw(basename);
+use lib qw( lib );
+use Parrot::Install qw(
+    install_files
+    create_directories
+    lines_to_files
+);
 
 # When run from the makefile, which is probably the only time this
 # script will ever be used, all of these defaults will get overridden.
@@ -74,7 +78,9 @@
     docdir      => '/usr/share/doc', # parrot/ subdir added below
     datadir     => '/usr/share/',    # parrot/ subdir added below
     srcdir      => '/usr/src/',      # parrot/ subdir added below
+    versiondir  => '',
     'dry-run'   => 0,
+    packages    => 'devel|pct|tge|nqp',
 );
 
 my @manifests;
@@ -89,118 +95,88 @@
 
 my $parrotdir = $options{versiondir};
 
-# We'll report multiple occurrences of the same file
-my %seen;
-
-my @files;
-my @installable_exe;
-my %directories;
- at ARGV = @manifests;
-while (<>) {
-    chomp;
-
-    s/\#.*//;    # Ignore comments
-    next if /^\s*$/;    # Skip blank lines
-
-    my ( $src, $meta, $dest ) = split( /\s+/, $_ );
-    $dest ||= $src;
-
-    if ( $seen{$src}++ ) {
-        print STDERR "$ARGV:$.: Duplicate entry $src\n";
-    }
-
-    # Parse out metadata
-    die "Malformed line in MANIFEST: $_" if not defined $meta;
-    my $generated = $meta =~ s/^\*//;
-    my ($package) = $meta =~ /^\[(.*?)\]/;
-    $meta =~ s/^\[(.*?)\]//;
-    next unless $package;    # Skip if this file belongs to no package
-
-    next unless $package =~ /devel|pct|tge|nqp/;
-
-    my %meta;
-    @meta{ split( /,/, $meta ) } = ();
-    $meta{$_} = 1 for ( keys %meta );          # Laziness
-
-    if ( $meta{lib} ) {
-        $dest = File::Spec->catdir( $options{libdir}, $parrotdir, "tools", $dest );
-    }
-    elsif ( $meta{share} ) {
-        $dest = File::Spec->catdir( $options{datadir}, $parrotdir, basename($dest) );
-    }
-    elsif ( $meta{include} ) {
-        $dest =~ s/^src//; # strip off leading src/ dir
-        $dest =~ s/^include//;
-        $dest = File::Spec->catdir( $options{includedir}, $parrotdir, $dest );
-    }
-    elsif ( $meta{src} ) {
-        $dest =~ s/^src//; # strip off leading src/ dir
-        $dest = File::Spec->catdir( $options{srcdir}, $parrotdir, $dest );
-    }
-    elsif ( $meta{doc} ) {
-        $dest =~ s/^docs/pod/; # docs dir are actually raw Pod
-        $dest = File::Spec->catdir( $options{docdir}, $parrotdir, $dest );
-    }
-    elsif ( /^runtime/ ) {
-        $dest =~ s/^runtime\/parrot\///;
-        $dest = File::Spec->catdir( $options{libdir}, $parrotdir, $dest );
-    }
-    elsif ( /^tools/ ) {
-        $dest = File::Spec->catdir( $options{libdir}, $parrotdir, $dest );
-    }
-    elsif ( /^VERSION/ ) {
-        $dest = File::Spec->catdir( $options{libdir}, $parrotdir, $dest );
-    }
-    elsif ( /^compilers/ ) {
-        $dest =~ s/^compilers/languages/;
-        $dest = File::Spec->catdir( $options{libdir}, $parrotdir, $dest );
-    }
-    else {
-        die "Unknown file type in MANIFEST: $_";
-    }
-
-    $dest = File::Spec->catdir( $options{buildprefix}, $dest )
-        if $options{buildprefix};
+# Set up transforms on filenames
+my(@transformorder) = (qw(lib share include src doc), '^(tools|VERSION)', '^compilers');
+my(%metatransforms) = (
+    lib => {
+        ismeta => 1,
+        optiondir => 'lib',
+        transform => sub {
+            my($filehash) = @_;
+            $filehash->{DestDirs} = [$parrotdir, "tools"];
+            return($filehash);
+        },
+    },
+    share => {
+        ismeta => 1,
+        optiondir => 'data',
+        transform => sub {
+            my($filehash) = @_;
+            $filehash->{Dest} = basename($filehash->{Dest});
+            $filehash->{DestDirs} = [$parrotdir];
+            return($filehash);
+        },
+    },
+    include => {
+        ismeta => 1,
+        optiondir => 'include',
+        transform => sub {
+            my($filehash) = @_;
+            $filehash->{Dest} =~ s/^src//; # strip off leading src/ dir
+            $filehash->{Dest} =~ s/^include//;
+            $filehash->{DestDirs} = [$parrotdir];
+            return($filehash);
+        },
+    },
+    src => {
+        ismeta => 1,
+        optiondir => 'src',
+        transform => sub {
+            my($filehash) = @_;
+            $filehash->{Dest} =~ s/^src//; # strip off leading src/ dir
+            $filehash->{DestDirs} = [$parrotdir];
+            return($filehash);
+        },
+    },
+    doc => {
+        ismeta => 1,
+        optiondir => 'doc',
+        transform => sub {
+            my($filehash) = @_;
+            $filehash->{Dest} =~ s/^docs/pod/; # other docs are actually raw Pod
+            $filehash->{DestDirs} = [$parrotdir];
+            return($filehash);
+        },
+    },
+    '^(tools|VERSION)' => {
+        optiondir => 'lib',
+        transform => sub {
+            my($filehash) = @_;
+            $filehash->{DestDirs} = [$parrotdir];
+            return($filehash);
+        },
+    },
+    '^compilers' => {
+        optiondir => 'lib',
+        transform => sub {
+            my($filehash) = @_;
+            $filehash->{Dest} =~ s/^compilers/languages/;
+            $filehash->{DestDirs} = [$parrotdir];
+            return($filehash);
+        },
+    },
+);
 
-    $directories{ dirname($dest) } = 1;
-    push( @files, [ $src => $dest ] );
-}
-continue {
-    close ARGV if eof;    # Reset line numbering for each input file
-}
+my($filehashes, $directories) = lines_to_files(
+    \%metatransforms, \@transformorder, \@manifests, \%options, $parrotdir
+);
 
 unless ( $options{'dry-run'} ) {
-    for my $dir ( map { $options{destdir} . $_ } keys %directories ) {
-        unless ( -d $dir ) {
-
-            # Make full path to the directory $dir
-            my @dirs;
-            while ( !-d $dir ) {    # Scan up to nearest existing ancestor
-                unshift @dirs, $dir;
-                $dir = dirname($dir);
-            }
-            foreach (@dirs) {
-                mkdir( $_, 0777 ) or die "mkdir $_: $!\n";
-            }
-        }
-    }
-}
-print("Installing ...\n");
-foreach ( @files, @installable_exe ) {
-    my ( $src, $dest ) = @$_;
-    $dest = $options{destdir} . $dest;
-    if ( $options{'dry-run'} ) {
-        print "$src -> $dest\n";
-        next;
-    }
-    else {
-        next unless -e $src;
-        copy( $src, $dest ) or die "copy $src to $dest: $!\n";
-        print "$dest\n";
-    }
-    my $mode = ( stat($src) )[2];
-    chmod $mode, $dest;
+    create_directories($options{destdir}, $directories);
 }
+install_files($options{destdir}, $options{'dry-run'}, $filehashes);
+
+print "Finished install_dev_files.pl\n";
 
 # Local Variables:
 #   mode: cperl

Modified: trunk/tools/dev/install_files.pl
==============================================================================
--- trunk/tools/dev/install_files.pl	Mon May 25 00:47:41 2009	(r39164)
+++ trunk/tools/dev/install_files.pl	Mon May 25 01:15:28 2009	(r39165)
@@ -1,199 +1,9 @@
 #! perl
 ################################################################################
-# Copyright (C) 2001-2008, Parrot Foundation.
+# Copyright (C) 2001-2009, Parrot Foundation.
 # $Id$
 ################################################################################
 
-use strict;
-use warnings;
-use File::Basename qw(dirname basename);
-use File::Copy;
-use File::Spec;
-
-# When run from the makefile, which is probably the only time this
-# script will ever be used, all of these defaults will get overridden.
-my %options = (
-    buildprefix => '',
-    prefix      => '/usr',
-    destdir     => '',
-    exec_prefix => '/usr',
-    bindir      => '/usr/bin',
-    libdir      => '/usr/lib',       # parrot/ subdir added below
-    includedir  => '/usr/include',   # parrot/ subdir added below
-    docdir      => '/usr/share/doc', # parrot/ subdir added below
-    version     => '',
-    'dry-run'   => 0,
-);
-
-my @manifests;
-foreach my $arg (@ARGV) {
-    if ( $arg =~ m/^--([^=]+)=(.*)/ ) {
-        $options{$1} = $2;
-    }
-    else {
-        push @manifests, $arg;
-    }
-}
-
-my $parrotdir = $options{versiondir};
-
-# We'll report multiple occurrences of the same file
-my %seen;
-
-my @files;
-my @installable_exe;
-my %directories;
- at ARGV = @manifests;
-while (<>) {
-    chomp;
-
-    s/\#.*//;    # Ignore comments
-    next if /^\s*$/;    # Skip blank lines
-
-    my ( $src, $meta, $dest ) = split( /\s+/, $_ );
-    $dest ||= $src;
-
-    if ( $seen{$src}++ ) {
-        print STDERR "$ARGV:$.: Duplicate entry $src\n";
-    }
-
-    # Parse out metadata
-    die "Malformed line in MANIFEST: $_" if not defined $meta;
-    my $generated = $meta =~ s/^\*//;
-    my ($package) = $meta =~ /^\[(.*?)\]/;
-    $meta =~ s/^\[(.*?)\]//;
-    next unless $package;    # Skip if this file belongs to no package
-
-    next unless $package =~ /main|library|pge/;
-
-    my %meta;
-    @meta{ split( /,/, $meta ) } = ();
-    $meta{$_} = 1 for ( keys %meta );          # Laziness
-
-    if ( /^runtime/ ) {
-         # have to catch this case early.
-        $dest =~ s/^runtime\/parrot\///;
-        $dest = File::Spec->catdir( $options{libdir}, $parrotdir, $dest );
-    }
-    elsif ( $meta{lib} ) {
-        if ( $dest =~ /^install_/ ) {
-            $dest =~ s/^install_//;            # parrot with different config
-            $dest = File::Spec->catdir( $options{libdir}, $parrotdir, 'include', $dest );
-        }
-        else {
-            # don't allow libraries to be installed into subdirs of libdir
-            $dest = File::Spec->catdir( $options{libdir}, basename($dest) );
-        }
-    }
-    elsif ( $meta{bin} ) {
-        my $copy = $dest;
-        $dest =~ s/^installable_//;            # parrot with different config
-        $dest = File::Spec->catdir( $options{bindir}, $dest );
-        # track bin here to check later below
-        # https://trac.parrot.org/parrot/ticket/434
-        if ( $copy =~ /^installable/ ) {
-            push @installable_exe, [ $src, $dest ];
-            next;
-        }
-    }
-    elsif ( $meta{include} ) {
-        $dest =~ s/^include//;
-        $dest = File::Spec->catdir( $options{includedir}, $parrotdir, $dest );
-    }
-    elsif ( $meta{doc} ) {
-        $dest =~ s/^docs\/resources/resources/; # resources go in the top level of docs
-        $dest =~ s/^docs/pod/; # other docs are actually raw Pod
-        $dest = File::Spec->catdir( $options{docdir}, $parrotdir, $dest );
-    }
-    elsif ( $meta{pkgconfig} ) {
-
-        # For the time being this is hardcoded as being installed under libdir
-        # as it is typically done with automake installed packages.  If there
-        # is a use case to make this configurable we'll add a seperate
-        # --pkgconfigdir option.
-        $dest = File::Spec->catdir( $options{libdir}, 'pkgconfig', $parrotdir, $dest );
-    }
-    elsif ( /^compilers/ ) {
-        $dest =~ s/^compilers/languages/;
-        $dest = File::Spec->catdir( $options{libdir}, $parrotdir, $dest );
-    }
-    else {
-        die "Unknown install location in MANIFEST: $_";
-    }
-
-    $dest = File::Spec->catdir( $options{buildprefix}, $dest )
-        if $options{buildprefix};
-
-    $directories{ dirname($dest) } = 1;
-    push( @files, [ $src => $dest ] );
-}
-continue {
-    close ARGV if eof;    # Reset line numbering for each input file
-}
-
-unless ( $options{'dry-run'} ) {
-    for my $dir ( map { $options{destdir} . $_ } keys %directories ) {
-        unless ( -d $dir ) {
-
-            # Make full path to the directory $dir
-            my @dirs;
-            while ( !-d $dir ) {    # Scan up to nearest existing ancestor
-                unshift @dirs, $dir;
-                $dir = dirname($dir);
-            }
-            foreach my $d (@dirs) {
-                mkdir( $d, 0777 ) or die "mkdir $d: $!\n";
-            }
-        }
-    }
-}
-
-for my $iref (@installable_exe) {
-    my ( $i, $dest ) = @{ $iref };
-    my ($file) = $i =~ /installable_(.+)$/;
-    next unless $file;
-    my @f = map { $_ ? $_->[0] : '' } @files;
-    if (grep(/^$file$/, @f)) {
-        if (-e $file) {
-            print "skipping $file, using installable_$file instead\n";
-            @files = map {$_ and $_->[0] !~ /^$file$/ ? $_ : undef} @files;
-        }
-    }
-}
-# for every .exe check if there's an installable. Fail if not
-foreach my $fref (@files ) {
-    next unless $fref;
-    my ( $src, $dest ) = @{ $fref };
-    my $i;
-    # This logic will fail on non-win32 if the generated files are really
-    # generated as with rt #40817. We don't have [main]bin here.
-    $i = "installable_$src" if $src =~ /\.exe$/;
-    next unless $i;
-    unless (map {$_->[0] =~ /^$i$/} @installable_exe) {
-        die "$i is missing in MANIFEST or MANIFEST.generated\n";
-    }
-}
-print("Installing ...\n");
-foreach my $fref ( @files, @installable_exe ) {
-    next unless $fref;
-    my ( $src, $dest ) = @{ $fref };
-    $dest = $options{destdir} . $dest;
-    if ( $options{'dry-run'} ) {
-        print "$src -> $dest\n";
-        next;
-    }
-    else {
-        next unless -e $src;
-        next if $^O eq 'cygwin' and -e "$src.exe"; # stat works, copy not
-        copy( $src, $dest ) or die "copy $src to $dest: $!\n";
-        print "$dest\n";
-    }
-    my $mode = ( stat($src) )[2];
-    chmod $mode, $dest;
-}
-
-################################################################################
-
 =head1 TITLE
 
 tools/dev/install_files.pl - Copy files to their correct locations
@@ -298,6 +108,163 @@
 
 =cut
 
+################################################################################
+
+use strict;
+use warnings;
+use File::Basename qw(basename);
+use lib qw( lib );
+use Parrot::Install qw(
+    install_files
+    create_directories
+    lines_to_files
+);
+
+# When run from the makefile, which is probably the only time this
+# script will ever be used, all of these defaults will get overridden.
+my %options = (
+    buildprefix => '',
+    prefix      => '/usr',
+    destdir     => '',
+    exec_prefix => '/usr',
+    bindir      => '/usr/bin',
+    libdir      => '/usr/lib',       # parrot/ subdir added below
+    includedir  => '/usr/include',   # parrot/ subdir added below
+    docdir      => '/usr/share/doc', # parrot/ subdir added below
+    versiondir  => '',
+    'dry-run'   => 0,
+    packages    => 'main|library|pge',
+);
+
+my @manifests;
+foreach (@ARGV) {
+    if (/^--([^=]+)=(.*)/) {
+        $options{$1} = $2;
+    }
+    else {
+        push @manifests, $_;
+    }
+}
+
+my $parrotdir = $options{versiondir};
+
+# Set up transforms on filenames
+my(@transformorder) = qw(lib bin include doc pkgconfig ^compilers);
+my(%metatransforms) = (
+    lib => {
+        ismeta => 1,
+        optiondir => 'lib',
+        transform => sub {
+            my($filehash) = @_;
+            local($_) = $filehash->{Dest};
+            if ( /^install_/ ) {
+                s/^install_//;     # parrot with different config
+                $filehash->{DestDirs} = [$parrotdir, 'include'];
+                $filehash->{Dest} = $_;
+            }
+            else {
+                # don't allow libraries to be installed into subdirs of libdir
+                $filehash->{Dest} = basename($_);
+            }
+            return($filehash);
+        },
+    },
+    bin => {
+        ismeta => 1,
+        optiondir => 'bin',
+        transform => sub {
+            my($filehash) = @_;
+            # parrot with different config
+            $filehash->{Installable} = $filehash->{Dest} =~ s/^installable_//;
+            return($filehash);
+        },
+        isbin => 1,
+    },
+    include => {
+        ismeta => 1,
+        optiondir => 'include',
+        transform => sub {
+            my($filehash) = @_;
+            $filehash->{Dest} =~ s/^include//;
+            $filehash->{DestDirs} = [$parrotdir];
+            return($filehash);
+        },
+    },
+    doc => {
+        ismeta => 1,
+        optiondir => 'doc',
+        transform => sub {
+            my($filehash) = @_;
+            $filehash->{Dest} =~ s#^docs/resources#resources#; # resources go in the top level of docs
+            $filehash->{Dest} =~ s/^docs/pod/; # other docs are actually raw Pod
+            $filehash->{DestDirs} = [$parrotdir];
+            return($filehash);
+        },
+    },
+    pkgconfig => {
+        ismeta => 1,
+        optiondir => 'lib',
+        transform => sub {
+            my($filehash) = @_;
+            # For the time being this is hardcoded as being installed under
+            # libdir as it is typically done with automake installed packages.
+            # If there is a use case to make this configurable we'll add a
+            # seperate --pkgconfigdir option.
+            $filehash->{DestDirs} = ['pkgconfig', $parrotdir];
+            return($filehash);
+        },
+    },
+    '^compilers' => {
+        optiondir => 'lib',
+        transform => sub {
+            my($filehash) = @_;
+            $filehash->{Dest} =~ s/^compilers/languages/;
+            $filehash->{DestDirs} = [$parrotdir];
+            return($filehash);
+        },
+    },
+);
+
+my($filehashes, $directories) = lines_to_files(
+    \%metatransforms, \@transformorder, \@manifests, \%options, $parrotdir
+);
+
+unless ( $options{'dry-run'} ) {
+    create_directories($options{destdir}, $directories);
+}
+
+# TT #347
+# 1. skip build_dir-only binaries for files marked Installable
+my($filehash, @removes, $removes);
+foreach $filehash (grep { $_->{Installable} } @$filehashes) {
+    my( $src, $dest ) = map { $filehash->{$_} } qw(Source Dest);
+    my ($file) = $src =~ /installable_(.+)$/;
+    next unless $file;
+    if((grep { $_->{Source} =~ /^$file$/ } @$filehashes) and -e $file) {
+        print "skipping $file, using installable_$file instead\n";
+        push @removes, $file;
+    }
+}
+$removes = join '|', @removes;
+@$filehashes = grep { $_->{Source} !~ /^($removes)$/ } @$filehashes;
+
+# 2. for every .exe check if there's an installable. Fail if not
+my $i;
+foreach $filehash (grep { ! $_->{Installable} } @$filehashes ) {
+    my( $src, $dest ) = map { $filehash->{$_} } qw(Source Dest);
+    next unless $src =~ /\.exe$/;
+    # This logic will fail on non-win32 if the generated files are really
+    # generated as with rt #40817. We don't have [main]bin here.
+    $i = "installable_$src";
+    unless (map {$_->{Source} =~ /^$i$/} grep { $_->{Installable} } @$filehashes) {
+        die "$i is missing in MANIFEST or MANIFEST.generated\n";
+    }
+}
+
+install_files($options{destdir}, $options{'dry-run'}, $filehashes);
+
+print "Finished install_files.pl\n";
+
 # Local Variables:
 #   mode: cperl
 #   cperl-indent-level: 4

Modified: trunk/tools/dev/mk_language_shell.pl
==============================================================================
--- trunk/tools/dev/mk_language_shell.pl	Mon May 25 00:47:41 2009	(r39164)
+++ trunk/tools/dev/mk_language_shell.pl	Mon May 25 01:15:28 2009	(r39165)
@@ -492,7 +492,7 @@
 VERSION       := @versiondir@
 BIN_DIR       := @bindir@
 LIB_DIR       := @libdir@$(VERSION)
-DOC_DIR       := @doc_dir@$(VERSION)
+DOC_DIR       := @docdir@$(VERSION)
 MANDIR        := @mandir@$(VERSION)
 
 # Set up extensions


More information about the parrot-commits mailing list