--- /dev/null
+package Wx::build::Utils;
+
+use strict;
+use Config;
+use base 'Exporter';
+use File::Spec::Functions qw(curdir catdir catfile updir);
+use File::Find qw(find);
+use File::Path qw(mkpath);
+use File::Basename qw(dirname);
+use Carp;
+
+use vars qw(@EXPORT @EXPORT_OK);
+@EXPORT_OK = qw(obj_from_src xs_dependencies write_string
+ lib_file arch_file arch_auto_file
+ path_search files_with_overload files_with_constants
+ pipe_stderr read_file write_file);
+
+=head1 NAME
+
+Wx::build::Utils - utility routines
+
+=head1 SUBROUTINES
+
+=head2 xs_dependencies
+
+ my %dependencies = xs_dependencies( $mm_object, [ 'dir1', 'dir2' ] );
+
+=cut
+
+sub _uniq {
+ my( %x );
+ $x{$_} = 1 foreach @_;
+ return sort keys %x;
+}
+
+sub xs_dependencies {
+ my( $this, $dirs, $top_dir ) = @_;
+
+ my( %depend );
+ my( $c, $o, $cinclude, $xsinclude );
+
+ foreach ( keys %{ $this->{XS} } ) {
+ ( $cinclude, $xsinclude ) = scan_xs( $_, $dirs, $top_dir );
+
+ $c = $this->{XS}{$_};
+ $o = obj_from_src( $c );
+
+ $depend{$c} = $_ . ' ' . join( ' ', _uniq( @$xsinclude ) );
+ $depend{$o} = $c . ' ' . join( ' ', _uniq( @$cinclude ) );
+ }
+
+ return %depend;
+}
+
+=head2 obj_from_src
+
+ my @obj_files = obj_from_src( 'Foo.xs', 'bar.c', 'cpp/bar.cpp' );
+
+Calculates the object file name from the source file name.
+In scalar context returns the first file.
+
+=cut
+
+sub obj_from_src {
+ my @xs = @_;
+ my $obj_ext = $Config{obj_ext} || $Config{_o};
+
+ foreach ( @xs ) { s[\.(?:xs|c|cc|cpp)$][$obj_ext] }
+
+ return wantarray ? @xs : $xs[0];
+}
+
+sub src_dir {
+ my( $file ) = @_;
+ my $d = curdir;
+
+ for ( 1 .. 5 ) {
+ return $d if -f catfile( $d, $file );
+ $d = catdir( updir, $d );
+ }
+
+ confess "Unable to find top level directory ($file)";
+}
+
+#
+# quick and dirty method for creating dependencies:
+# considers files included via #include "..." or INCLUDE: ...
+# (not #include <...>) and does not take into account preprocessor directives
+#
+sub scan_xs($$$);
+
+sub scan_xs($$$) {
+ my( $xs, $incpath, $top_dir ) = @_;
+
+ local( *IN, $_ );
+ my( @cinclude, @xsinclude );
+
+ open IN, $xs;
+
+ my $file;
+ my $arr;
+
+ while( defined( $_ = <IN> ) ) {
+ undef $file;
+
+ m/^\#\s*include\s+"([^"]*)"\s*$/ and $file = $1 and $arr = \@cinclude;
+ m/^\s*INCLUDE:\s+(.*)$/ and $file = $1 and $arr = \@xsinclude;
+ m/^\s*INCLUDE:\s+.*\s(\S+\.xsp?)\s*\|/ and $file = $1 and
+ $arr = \@xsinclude;
+
+ if( defined $file ) {
+ $file = catfile( split '/', $file );
+
+ foreach my $dir ( @$incpath ) {
+ my $f = $dir eq curdir() ? $file : catfile( $dir, $file );
+ if( -f $f ) {
+ push @$arr, $f;
+ my( $cinclude, $xsinclude ) = scan_xs( $f, $incpath, $top_dir );
+ push @cinclude, @$cinclude;
+ push @xsinclude, @$xsinclude;
+ last;
+ } elsif( $file =~ m/ovl_const\.(?:cpp|h)/i
+ || $file =~ m/v_cback_def\.h/i
+ || $file =~ m/ItemContainer(?:Immutable)?\.xs/i
+ || $file =~ m/Var[VH]{0,2}ScrollHelper(?:Base)?\.xs/i ) {
+ push @$arr, ( ( $top_dir eq curdir() ) ?
+ $file :
+ catfile( $top_dir, $file ) );
+ }
+ }
+ }
+ }
+
+ close IN;
+
+ ( \@cinclude, \@xsinclude );
+}
+
+=head2 write_string, write_file
+
+ write_string( 'file', $scalar );
+ write_file( 'file', $scalar );
+
+Like File::Slurp.
+
+=head2 read_file
+
+ my $string = read_file( 'file' );
+
+=cut
+
+*write_string = \&write_file;
+
+sub write_file {
+ my( $file, $string ) = @_;
+
+ mkpath( dirname( $file ) ) if dirname( $file );
+ open my $fh, ">", $file or die "open '$file': $!";
+ binmode $fh;
+ print $fh $string or die "print '$file': $!";
+ close $fh or die "close '$file': $!";
+}
+
+sub read_file {
+ my( $file ) = @_;
+
+ local $/ = wantarray ? $/ : undef;;
+ open my $fh, "<", $file or die "open '$file': $!";
+ binmode $fh;
+
+ return <$fh>;
+}
+
+=head2 lib_file, arch_file, arch_auto_file
+
+ my $file = lib_file( 'Foo.pm' ); # blib/lib/Foo.pm on *nix
+ my $file = lib_file( 'Foo/Bar.pm' ); # blib\lib\Foo\Bar.pm on Win32
+ my $file = arch_auto_file( 'My\My.dll' ); # blib\arch\auto\My\My.dll
+
+All input paths must be relative, output paths may be absolute.
+
+=cut
+
+sub _split {
+ require File::Spec::Unix;
+
+ my $path = shift;
+ my( $volume, $dir, $file ) = File::Spec::Unix->splitpath( $path );
+ my @dirs = File::Spec::Unix->splitdir( $dir );
+
+ return ( @dirs, $file );
+}
+
+sub lib_file {
+ my @split = _split( shift );
+
+ return File::Spec->catfile( 'blib', 'lib', @split );
+}
+
+sub arch_file {
+ my @split = _split( shift );
+
+ return File::Spec->catfile( 'blib', 'arch', @split );
+}
+
+sub arch_auto_file {
+ my @split = _split( shift );
+
+ return File::Spec->catfile( 'blib', 'arch', 'auto', @split );
+}
+
+=head2 path_search
+
+ my $file = path_search( 'foo.exe' );
+
+Searches PATH for the given executable.
+
+=cut
+
+sub path_search {
+ my $file = shift;
+
+ foreach my $d ( File::Spec->path ) {
+ my $full = File::Spec->catfile( $d, $file );
+ return $full if -f $full;
+ }
+
+ return;
+}
+
+=head2 files_with_constants
+
+ my @files = files_with_constants;
+
+Finds files containing constants
+
+=cut
+
+sub files_with_constants {
+ my @files;
+
+ my $wanted = sub {
+ my $name = $File::Find::name;
+
+ m/\.(?:pm|xsp?|cpp|h)$/i && do {
+ local *IN;
+ my $line;
+
+ open IN, "< $_" || warn "unable to open '$_'";
+ while( defined( $line = <IN> ) ) {
+ $line =~ m/^\W+\!\w+:/ && do {
+ push @files, $name;
+ return;
+ };
+ };
+ };
+ };
+
+ find( $wanted, curdir );
+
+ return @files;
+}
+
+=head2 files_with_overload
+
+ my @files = files_with_overload;
+
+Finds files containing overloaded XS/Perl subroutines
+
+=cut
+
+sub files_with_overload {
+ my @files;
+
+ my $wanted = sub {
+ my $name = $File::Find::name;
+
+ m/\.pm$/i && do {
+ my $line;
+ local *IN;
+
+ open IN, "< $_" || warn "unable to open '$_'";
+ while( defined( $line = <IN> ) ) {
+ $line =~ m/Wx::_match/ && do {
+ push @files, $name;
+ return;
+ };
+ }
+ };
+
+ m/\.xsp?$/i && do {
+ my $line;
+ local *IN;
+
+ open IN, "< $_" || warn "unable to open '$_'";
+ while( defined( $line = <IN> ) ) {
+ $line =~ m/wxPli_match_arguments|BEGIN_OVERLOAD\(\)/ && do {
+ push @files, $name;
+ return;
+ };
+ }
+ };
+ };
+
+ find( $wanted, curdir );
+
+ return @files;
+}
+
+sub pipe_stderr {
+ my( $cmd ) = @_;
+ my $pipe = File::Spec->catfile( 'script', 'pipe.pl' );
+
+ if( -f $pipe ) {
+ return qx{$^X $pipe $cmd};
+ } else {
+ # fix quoting later if necessary
+ return qx[$^X -e "open STDERR, q{>&STDOUT}; exec q{$cmd}"];
+ }
+}
+
+1;
+
+# local variables:
+# mode: cperl
+# end: