| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package App::DualLivedDiff; | 
| 2 | 1 |  |  | 1 |  | 27145 | use strict; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 44 |  | 
| 3 | 1 |  |  | 1 |  | 5 | use warnings; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 43 |  | 
| 4 |  |  |  |  |  |  |  | 
| 5 |  |  |  |  |  |  | our $VERSION = '1.07'; | 
| 6 |  |  |  |  |  |  |  | 
| 7 | 1 |  |  | 1 |  | 1312 | use Getopt::Long; | 
|  | 1 |  |  |  |  | 21506 |  | 
|  | 1 |  |  |  |  | 6 |  | 
| 8 | 1 |  |  | 1 |  | 16194 | use Parse::CPAN::Meta (); | 
|  | 1 |  |  |  |  | 1321 |  | 
|  | 1 |  |  |  |  | 26 |  | 
| 9 | 1 |  |  | 1 |  | 876 | use LWP::Simple; | 
|  | 1 |  |  |  |  | 116728 |  | 
|  | 1 |  |  |  |  | 10 |  | 
| 10 | 1 |  |  | 1 |  | 1958 | use File::Temp (); | 
|  | 1 |  |  |  |  | 26249 |  | 
|  | 1 |  |  |  |  | 24 |  | 
| 11 | 1 |  |  | 1 |  | 10 | use File::Spec; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 20 |  | 
| 12 | 1 |  |  | 1 |  | 1150 | use Archive::Extract; | 
|  | 1 |  |  |  |  | 238479 |  | 
|  | 1 |  |  |  |  | 44 |  | 
| 13 | 1 |  |  | 1 |  | 9 | use File::Find (); | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 14 |  | 
| 14 | 1 |  |  | 1 |  | 4913 | use CPAN (); | 
|  | 1 |  |  |  |  | 283950 |  | 
|  | 1 |  |  |  |  | 4526 |  | 
| 15 |  |  |  |  |  |  |  | 
| 16 |  |  |  |  |  |  | our $diff_cmd = 'diff'; | 
| 17 |  |  |  |  |  |  | our @exclude_files = ( | 
| 18 |  |  |  |  |  |  | qr(\.{1,2}$), | 
| 19 |  |  |  |  |  |  | qr(\.svn$), | 
| 20 |  |  |  |  |  |  | qr(\.git$), | 
| 21 |  |  |  |  |  |  | ); | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | sub usage { | 
| 24 | 0 | 0 |  | 0 | 0 |  | print "@_\n\n" if @_; | 
| 25 | 0 |  |  |  |  |  | print < | 
| 26 |  |  |  |  |  |  | Usage: $0 -d source-dist -b /path/to/blead/checkout | 
| 27 |  |  |  |  |  |  | Does a diff FROM a dual lived module distribution TO blead perl | 
| 28 |  |  |  |  |  |  |  | 
| 29 |  |  |  |  |  |  | -b/--blead   blead perl path | 
| 30 |  |  |  |  |  |  | -d/--dual    dual lived module distribution path, archive, URL, | 
| 31 |  |  |  |  |  |  | or module- or distribution name (default: .) | 
| 32 |  |  |  |  |  |  | -r/--reverse reverses the diff (blead to lib) | 
| 33 |  |  |  |  |  |  | -c/--config  name of the configuration file with file mappings | 
| 34 |  |  |  |  |  |  | (defaults to .dualLivedDiffConfig in the module path or current path) | 
| 35 |  |  |  |  |  |  | -o/--output  file name for output (defaults to STDOUT) | 
| 36 |  |  |  |  |  |  | useful to separate diff from CPAN.pm output | 
| 37 |  |  |  |  |  |  | -p/--paths   show the "dual"-lived module paths or the "blead" paths? | 
| 38 |  |  |  |  |  |  | defaults to "blead" or to "dual" if in --reverse mode | 
| 39 |  |  |  |  |  |  | -w/--ignore-all-space (same as for normal gnu diff) | 
| 40 |  |  |  |  |  |  |  | 
| 41 |  |  |  |  |  |  | Check perldoc "App::DualLivedDiff" for more info on the usage. | 
| 42 |  |  |  |  |  |  | The "diff" command is assumed to be in your PATH and will be run with the | 
| 43 |  |  |  |  |  |  | -u and -N options by default. | 
| 44 |  |  |  |  |  |  | HERE | 
| 45 | 0 |  |  |  |  |  | exit(1); | 
| 46 |  |  |  |  |  |  | } | 
| 47 |  |  |  |  |  |  |  | 
| 48 |  |  |  |  |  |  | my ( | 
| 49 |  |  |  |  |  |  | $bleadpath, $dualmodule, $reverse, | 
| 50 |  |  |  |  |  |  | $default_config_file, $config_file, | 
| 51 |  |  |  |  |  |  | $output_file, $paths, $ignore_space | 
| 52 |  |  |  |  |  |  | ); | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | sub run { | 
| 55 | 0 |  |  | 0 | 0 |  | $bleadpath           = undef; | 
| 56 | 0 |  |  |  |  |  | $dualmodule          = '.'; | 
| 57 | 0 |  |  |  |  |  | $reverse             = 0; | 
| 58 | 0 |  |  |  |  |  | $default_config_file = '.dualLivedDiffConfig'; | 
| 59 | 0 |  |  |  |  |  | $config_file         = $default_config_file; | 
| 60 | 0 |  |  |  |  |  | $output_file         = undef; | 
| 61 | 0 |  |  |  |  |  | $paths               = undef; | 
| 62 | 0 |  |  |  |  |  | $ignore_space        = undef; | 
| 63 | 0 |  |  |  |  |  | GetOptions( | 
| 64 |  |  |  |  |  |  | 'b|blead=s'                  => \$bleadpath, | 
| 65 |  |  |  |  |  |  | 'h|help'                     => \&usage, | 
| 66 |  |  |  |  |  |  | 'r|reverse'                  => \$reverse, | 
| 67 |  |  |  |  |  |  | 'd|dual=s'                   => \$dualmodule, | 
| 68 |  |  |  |  |  |  | 'c|conf|config|configfile=s' => \$config_file, | 
| 69 |  |  |  |  |  |  | 'o|out|output=s'             => \$output_file, | 
| 70 |  |  |  |  |  |  | 'p|path|paths=s'             => \$paths, | 
| 71 |  |  |  |  |  |  | 'w|ignore-all-space'         => \$ignore_space, | 
| 72 |  |  |  |  |  |  | ); | 
| 73 |  |  |  |  |  |  |  | 
| 74 | 0 | 0 |  |  |  |  | if (defined $output_file) { | 
| 75 | 0 | 0 |  |  |  |  | open my $fh, '>', $output_file or die "Could not open file '$output_file' for writing: $!"; | 
| 76 | 0 |  |  |  |  |  | $output_file = $fh; | 
| 77 |  |  |  |  |  |  | } | 
| 78 |  |  |  |  |  |  |  | 
| 79 | 0 | 0 | 0 |  |  |  | usage() if not defined $bleadpath or not -d $bleadpath; | 
| 80 |  |  |  |  |  |  |  | 
| 81 | 0 | 0 |  |  |  |  | if (not defined $paths) { | 
| 82 | 0 | 0 |  |  |  |  | $paths = $reverse ? "blead" : "dual"; | 
| 83 |  |  |  |  |  |  | } | 
| 84 |  |  |  |  |  |  | else { | 
| 85 | 0 | 0 |  |  |  |  | if ($paths =~ /^blead$/i) { | 
|  |  | 0 |  |  |  |  |  | 
| 86 | 0 |  |  |  |  |  | $paths = 'blead'; | 
| 87 |  |  |  |  |  |  | } elsif ($paths =~ /^dual$/i) { | 
| 88 | 0 |  |  |  |  |  | $paths = 'dual'; | 
| 89 |  |  |  |  |  |  | } else { | 
| 90 | 0 |  |  |  |  |  | die "Invalid path setting: --paths must be either 'dual' or 'blead'!\n"; | 
| 91 |  |  |  |  |  |  | } | 
| 92 |  |  |  |  |  |  | } | 
| 93 |  |  |  |  |  |  |  | 
| 94 | 0 |  |  |  |  |  | my $workdir         = get_dual_lived_distribution_dir($dualmodule); | 
| 95 | 0 |  |  |  |  |  | my $config          = get_config($workdir, $config_file); | 
| 96 |  |  |  |  |  |  |  | 
| 97 | 0 |  | 0 |  |  |  | my $files           = $config->{"files"} || {}; | 
| 98 | 0 | 0 |  |  |  |  | my $exclude_regexes = [ map {qr/$_/} @{$config->{"exclude-regexes"} || []} ]; | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 99 | 0 |  | 0 |  |  |  | my $dirs_flat       = $config->{"dirs-flat"} || {}; | 
| 100 | 0 |  | 0 |  |  |  | my $dirs_recursive  = $config->{"dirs-recursive"} || {}; | 
| 101 |  |  |  |  |  |  |  | 
| 102 | 0 |  |  |  |  |  | my $blead_module_base_path = $config->{"base-path-in-blead"}; | 
| 103 |  |  |  |  |  |  |  | 
| 104 | 0 |  |  |  |  |  | my $pathspec = { | 
| 105 |  |  |  |  |  |  | blead_path => $bleadpath, | 
| 106 |  |  |  |  |  |  | source_path => $workdir, | 
| 107 |  |  |  |  |  |  | blead_module_path => $blead_module_base_path, | 
| 108 |  |  |  |  |  |  | }; | 
| 109 |  |  |  |  |  |  |  | 
| 110 | 0 |  |  |  |  |  | foreach my $source_file (keys %$files) { | 
| 111 |  |  |  |  |  |  | # commented out since explicitly mapped files trump exclusion | 
| 112 |  |  |  |  |  |  | #if (grep {$source_file =~ $_} @$exclude_regexes) { next; } | 
| 113 | 0 |  |  |  |  |  | my $blead_file = $files->{$source_file}; | 
| 114 | 0 |  |  |  |  |  | $pathspec->{blead_file} = $blead_file; | 
| 115 | 0 |  |  |  |  |  | $pathspec->{source_file} = $source_file; | 
| 116 |  |  |  |  |  |  |  | 
| 117 | 0 |  |  |  |  |  | my $absolute_source_file = File::Spec->catdir($workdir, $source_file); | 
| 118 |  |  |  |  |  |  |  | 
| 119 | 0 | 0 |  |  |  |  | if (-f $absolute_source_file) { | 
|  |  | 0 |  |  |  |  |  | 
| 120 | 0 |  |  |  |  |  | file_diff( $output_file, $pathspec, $paths ); | 
| 121 |  |  |  |  |  |  | } | 
| 122 |  |  |  |  |  |  | elsif (-d $absolute_source_file) { | 
| 123 | 0 |  |  |  |  |  | warn "'$absolute_source_file' is not a file but a directory. Use the 'dirs-flat' or 'dirs-recursive' config options instead!"; | 
| 124 | 0 |  |  |  |  |  | next; | 
| 125 |  |  |  |  |  |  | } | 
| 126 |  |  |  |  |  |  | else { | 
| 127 | 0 |  |  |  |  |  | warn "Explicitly mapped file '$source_file' missing from dual lived module source tree!"; | 
| 128 | 0 |  |  |  |  |  | next; | 
| 129 |  |  |  |  |  |  | } | 
| 130 |  |  |  |  |  |  | } | 
| 131 |  |  |  |  |  |  |  | 
| 132 | 0 |  |  |  |  |  | foreach my $source_dir (keys %$dirs_flat) { | 
| 133 | 0 | 0 |  |  |  |  | if (grep {$source_dir =~ $_} @$exclude_regexes) { | 
|  | 0 |  |  |  |  |  |  | 
| 134 | 0 |  |  |  |  |  | warn "Explicitly mapped directory '$source_dir' is also excluded explicitly. Skipping it."; | 
| 135 | 0 |  |  |  |  |  | next; | 
| 136 |  |  |  |  |  |  | } | 
| 137 | 0 |  |  |  |  |  | my $blead_dir = $dirs_flat->{$source_dir}; | 
| 138 | 0 |  |  |  |  |  | $pathspec->{blead_file} = $blead_dir; | 
| 139 | 0 |  |  |  |  |  | $pathspec->{source_file} = $source_dir; | 
| 140 |  |  |  |  |  |  |  | 
| 141 | 0 |  |  |  |  |  | my $absolute_source_dir = File::Spec->catdir($workdir, $source_dir); | 
| 142 | 0 | 0 |  |  |  |  | if (-f $absolute_source_dir) { | 
|  |  | 0 |  |  |  |  |  | 
| 143 | 0 |  |  |  |  |  | warn "'$absolute_source_dir' is not a directory but a file. Use the 'files' config option instead!"; | 
| 144 | 0 |  |  |  |  |  | next; | 
| 145 |  |  |  |  |  |  | } | 
| 146 |  |  |  |  |  |  | elsif (-d $absolute_source_dir) { | 
| 147 | 0 |  |  |  |  |  | dir_diff( $output_file, $pathspec, $paths, 0, $exclude_regexes ); | 
| 148 |  |  |  |  |  |  | } | 
| 149 |  |  |  |  |  |  | else { | 
| 150 | 0 |  |  |  |  |  | warn "Explicitly mapped directory '$source_dir' missing from dual lived module source tree!"; | 
| 151 | 0 |  |  |  |  |  | next; | 
| 152 |  |  |  |  |  |  | } | 
| 153 |  |  |  |  |  |  | } | 
| 154 |  |  |  |  |  |  |  | 
| 155 | 0 |  |  |  |  |  | foreach my $source_dir (keys %$dirs_recursive) { | 
| 156 | 0 | 0 |  |  |  |  | if (grep {$source_dir =~ $_} @$exclude_regexes) { | 
|  | 0 |  |  |  |  |  |  | 
| 157 | 0 |  |  |  |  |  | warn "Explicitly mapped directory '$source_dir' is also excluded explicitly. Skipping it."; | 
| 158 | 0 |  |  |  |  |  | next; | 
| 159 |  |  |  |  |  |  | } | 
| 160 | 0 |  |  |  |  |  | my $blead_dir = $dirs_recursive->{$source_dir}; | 
| 161 | 0 |  |  |  |  |  | $pathspec->{blead_file} = $blead_dir; | 
| 162 | 0 |  |  |  |  |  | $pathspec->{source_file} = $source_dir; | 
| 163 |  |  |  |  |  |  |  | 
| 164 | 0 |  |  |  |  |  | my $absolute_source_dir = File::Spec->catdir($workdir, $source_dir); | 
| 165 | 0 | 0 |  |  |  |  | if (-f $absolute_source_dir) { | 
|  |  | 0 |  |  |  |  |  | 
| 166 | 0 |  |  |  |  |  | warn "'$absolute_source_dir' is not a directory but a file. Use the 'files' config option instead!"; | 
| 167 | 0 |  |  |  |  |  | next; | 
| 168 |  |  |  |  |  |  | } | 
| 169 |  |  |  |  |  |  | elsif (-d $absolute_source_dir) { | 
| 170 | 0 |  |  |  |  |  | dir_diff( $output_file, $pathspec, $paths, 1, $exclude_regexes ); | 
| 171 |  |  |  |  |  |  | } | 
| 172 |  |  |  |  |  |  | else { | 
| 173 | 0 |  |  |  |  |  | warn "Explicitly mapped directory '$source_dir' missing from dual lived module source tree!"; | 
| 174 | 0 |  |  |  |  |  | next; | 
| 175 |  |  |  |  |  |  | } | 
| 176 |  |  |  |  |  |  | } | 
| 177 |  |  |  |  |  |  | } | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  | # given a source specification, get the path to an extracted distribution | 
| 180 |  |  |  |  |  |  | sub get_dual_lived_distribution_dir { | 
| 181 | 0 |  |  | 0 | 0 |  | my $source = shift; | 
| 182 | 0 | 0 |  |  |  |  | usage("Bad source of the dual lived module distribution '$source'") | 
| 183 |  |  |  |  |  |  | if not defined $source; | 
| 184 |  |  |  |  |  |  |  | 
| 185 | 0 |  |  |  |  |  | my $distfile; | 
| 186 | 0 | 0 |  |  |  |  | if (-d $source) { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 187 |  |  |  |  |  |  | # already extracted or checkout | 
| 188 | 0 |  |  |  |  |  | return $source; | 
| 189 |  |  |  |  |  |  | } | 
| 190 |  |  |  |  |  |  | elsif (-f $source) { | 
| 191 |  |  |  |  |  |  | # distribution file | 
| 192 | 0 |  |  |  |  |  | $distfile = $source; | 
| 193 |  |  |  |  |  |  | } | 
| 194 |  |  |  |  |  |  | elsif ($source =~ m{^(?:ftp|https?)://}) { | 
| 195 | 0 |  |  |  |  |  | $distfile = download_distribution($source); | 
| 196 |  |  |  |  |  |  | } | 
| 197 |  |  |  |  |  |  | elsif ($source =~ m{^[^:/]+://}) { | 
| 198 | 0 |  |  |  |  |  | die "Support for VCS checkout and fancy protocols not implemented"; | 
| 199 |  |  |  |  |  |  | } | 
| 200 |  |  |  |  |  |  | else { | 
| 201 |  |  |  |  |  |  | # fallback, treat as module or distribution | 
| 202 | 0 |  |  |  |  |  | my $url = module_or_dist_to_url($source); | 
| 203 | 0 | 0 |  |  |  |  | die "Could not find CPAN module of that name ($source)" if not defined $url; | 
| 204 | 0 |  |  |  |  |  | $distfile = download_distribution($url); | 
| 205 |  |  |  |  |  |  | } | 
| 206 |  |  |  |  |  |  |  | 
| 207 |  |  |  |  |  |  | # extract distribution | 
| 208 | 0 |  |  |  |  |  | my $tmpdir = File::Temp::tempdir( CLEANUP => 1 ); | 
| 209 | 0 |  |  |  |  |  | my $ae = Archive::Extract->new( archive => $distfile ); | 
| 210 | 0 | 0 |  |  |  |  | $ae->extract( to => $tmpdir ) | 
| 211 |  |  |  |  |  |  | or die "Failed to extract distribution '$distfile' to temp. dir: " . $ae->error(); | 
| 212 |  |  |  |  |  |  |  | 
| 213 |  |  |  |  |  |  | # find the extracted distribution dir | 
| 214 | 0 | 0 |  |  |  |  | opendir my $dh, $tmpdir | 
| 215 |  |  |  |  |  |  | or die "Could not opendir '$tmpdir': $!"; | 
| 216 | 0 |  |  |  |  |  | my @stuff = readdir($dh); | 
| 217 | 0 |  |  |  |  |  | my @files = grep {-f File::Spec->catfile($tmpdir, $_)} @stuff; | 
|  | 0 |  |  |  |  |  |  | 
| 218 | 0 | 0 |  |  |  |  | my @dirs  = grep {!/^\.\.?$/ and -d File::Spec->catdir($tmpdir, $_)} @stuff; | 
|  | 0 |  |  |  |  |  |  | 
| 219 | 0 |  |  |  |  |  | closedir $dh; | 
| 220 |  |  |  |  |  |  |  | 
| 221 | 0 | 0 | 0 |  |  |  | if (@files or @dirs != 1) { | 
| 222 | 0 |  |  |  |  |  | die "Failed to find extracted distribution directory in '$tmpdir'. Found ".scalar(@files)." files and ".scalar(@dirs)." dirs"; | 
| 223 |  |  |  |  |  |  | } | 
| 224 |  |  |  |  |  |  |  | 
| 225 | 0 |  |  |  |  |  | return File::Spec->catdir($tmpdir, shift(@dirs)); | 
| 226 |  |  |  |  |  |  | } | 
| 227 |  |  |  |  |  |  |  | 
| 228 |  |  |  |  |  |  | sub download_distribution { | 
| 229 | 0 |  |  | 0 | 0 |  | my $url = shift; | 
| 230 | 0 |  |  |  |  |  | my $disttmpdir = File::Temp::tempdir( CLEANUP => 1 ); | 
| 231 | 0 | 0 |  |  |  |  | $url =~ m{/([^/]+)$} or die; | 
| 232 | 0 |  |  |  |  |  | my $file = File::Spec->catfile($disttmpdir, $1); | 
| 233 | 0 | 0 |  |  |  |  | if (is_success(getstore( $url, $file ))) { | 
| 234 | 0 |  |  |  |  |  | return $file; | 
| 235 |  |  |  |  |  |  | } | 
| 236 |  |  |  |  |  |  | else { | 
| 237 | 0 |  |  |  |  |  | die "Could not fetch '$url'"; | 
| 238 |  |  |  |  |  |  | } | 
| 239 |  |  |  |  |  |  | } | 
| 240 |  |  |  |  |  |  |  | 
| 241 |  |  |  |  |  |  | # find and load the configuration file | 
| 242 |  |  |  |  |  |  | sub get_config { | 
| 243 | 0 |  |  | 0 | 0 |  | my $source_dir = shift; | 
| 244 | 0 |  |  |  |  |  | my $config_file = shift; | 
| 245 | 0 |  |  |  |  |  | my $yaml; | 
| 246 | 0 | 0 |  |  |  |  | if (-f $config_file) { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 247 | 0 |  |  |  |  |  | $yaml = Parse::CPAN::Meta::LoadFile($config_file); | 
| 248 |  |  |  |  |  |  | } | 
| 249 |  |  |  |  |  |  | elsif ( -f File::Spec->catfile($source_dir, $config_file) ) { | 
| 250 | 0 |  |  |  |  |  | $yaml = Parse::CPAN::Meta::LoadFile( | 
| 251 |  |  |  |  |  |  | File::Spec->catfile($source_dir, $config_file) | 
| 252 |  |  |  |  |  |  | ); | 
| 253 |  |  |  |  |  |  | } | 
| 254 |  |  |  |  |  |  | elsif ( -f File::Spec->catfile($source_dir, $default_config_file) ) { | 
| 255 | 0 |  |  |  |  |  | $yaml = Parse::CPAN::Meta::LoadFile( | 
| 256 |  |  |  |  |  |  | File::Spec->catfile($source_dir, $default_config_file) | 
| 257 |  |  |  |  |  |  | ); | 
| 258 |  |  |  |  |  |  | } | 
| 259 |  |  |  |  |  |  | else { | 
| 260 | 0 |  |  |  |  |  | die "Could not find nor load configuration file"; | 
| 261 |  |  |  |  |  |  | } | 
| 262 |  |  |  |  |  |  |  | 
| 263 | 0 | 0 |  |  |  |  | $yaml = $yaml->[0] if ref($yaml) eq 'ARRAY'; | 
| 264 |  |  |  |  |  |  |  | 
| 265 | 0 |  |  |  |  |  | return $yaml; | 
| 266 |  |  |  |  |  |  | } | 
| 267 |  |  |  |  |  |  |  | 
| 268 |  |  |  |  |  |  | # given the two base dirs and two relative paths, transform a | 
| 269 |  |  |  |  |  |  | # directory mapping into file mappings recursively | 
| 270 |  |  |  |  |  |  | sub dirs_to_filemapping { | 
| 271 | 0 |  |  | 0 | 0 |  | my $pathspec        = shift; | 
| 272 | 0 |  |  |  |  |  | my $recursive       = shift; | 
| 273 |  |  |  |  |  |  |  | 
| 274 | 0 |  |  |  |  |  | my $full_source_dir = File::Spec->catdir($pathspec->{source_path}, $pathspec->{source_file}); | 
| 275 | 0 |  |  |  |  |  | my $full_blead_dir  = get_full_blead_path($pathspec, $pathspec->{blead_file}); | 
| 276 |  |  |  |  |  |  |  | 
| 277 | 0 | 0 |  |  |  |  | if (not -d $full_blead_dir) { | 
| 278 | 0 |  |  |  |  |  | warn "Specified directory '$pathspec->{blead_file}' could not be found in blead perl source tree (as $full_blead_dir)!"; | 
| 279 | 0 |  |  |  |  |  | return(); | 
| 280 |  |  |  |  |  |  | } | 
| 281 | 0 | 0 |  |  |  |  | if (not -d $full_source_dir) { | 
| 282 | 0 |  |  |  |  |  | warn "Specified directory '$pathspec->{source_file}' could not be found in dual lived module source tree (as $full_source_dir)!"; | 
| 283 | 0 |  |  |  |  |  | return(); | 
| 284 |  |  |  |  |  |  | } | 
| 285 |  |  |  |  |  |  |  | 
| 286 | 0 | 0 |  |  |  |  | my @source_files = $recursive ? recur_get_all_files($full_source_dir) : get_all_files($full_source_dir); | 
| 287 | 0 | 0 |  |  |  |  | if (!@source_files) { | 
| 288 | 0 |  |  |  |  |  | warn "Specified source directory '$pathspec->{source_file}' does not contain any files!"; | 
| 289 | 0 |  |  |  |  |  | return({}); | 
| 290 |  |  |  |  |  |  | } | 
| 291 |  |  |  |  |  |  |  | 
| 292 | 0 |  |  |  |  |  | my $mapping = {}; | 
| 293 |  |  |  |  |  |  | $mapping->{File::Spec->catfile($pathspec->{source_file}, $_)} = File::Spec->catfile($pathspec->{blead_file}, $_) | 
| 294 | 0 |  |  |  |  |  | for @source_files; | 
| 295 |  |  |  |  |  |  |  | 
| 296 | 0 |  |  |  |  |  | return $mapping; | 
| 297 |  |  |  |  |  |  | } | 
| 298 |  |  |  |  |  |  |  | 
| 299 |  |  |  |  |  |  | # get all files in a path with relative paths | 
| 300 |  |  |  |  |  |  | sub recur_get_all_files { | 
| 301 | 0 |  |  | 0 | 0 |  | my $path = shift; | 
| 302 |  |  |  |  |  |  |  | 
| 303 | 0 |  |  |  |  |  | my @files; | 
| 304 | 0 | 0 |  |  |  |  | return() if not -d $path; | 
| 305 |  |  |  |  |  |  |  | 
| 306 |  |  |  |  |  |  | File::Find::find( | 
| 307 |  |  |  |  |  |  | { | 
| 308 |  |  |  |  |  |  | preprocess => sub { | 
| 309 | 0 |  |  | 0 |  |  | my @return; | 
| 310 | 0 |  |  |  |  |  | FILE: foreach my $file (@_) { | 
| 311 | 0 |  |  |  |  |  | foreach my $exclude_regex (@exclude_files) { | 
| 312 | 0 | 0 |  |  |  |  | next FILE if $file =~ $exclude_regex; | 
| 313 |  |  |  |  |  |  | } | 
| 314 | 0 |  |  |  |  |  | push @return, $file; | 
| 315 |  |  |  |  |  |  | } | 
| 316 | 0 |  |  |  |  |  | return(@return); | 
| 317 |  |  |  |  |  |  | }, | 
| 318 |  |  |  |  |  |  | wanted => sub { | 
| 319 | 0 |  |  | 0 |  |  | foreach my $exclude_regex (@exclude_files) { | 
| 320 | 0 | 0 |  |  |  |  | return if $_ =~ $exclude_regex; | 
| 321 |  |  |  |  |  |  | } | 
| 322 | 0 | 0 |  |  |  |  | return unless -f $_; | 
| 323 | 0 |  |  |  |  |  | s{^\Q$path\E[\\/]*}{}; | 
| 324 | 0 |  |  |  |  |  | push @files, $_; | 
| 325 |  |  |  |  |  |  | }, | 
| 326 | 0 |  |  |  |  |  | no_chdir => 1, | 
| 327 |  |  |  |  |  |  | }, | 
| 328 |  |  |  |  |  |  | $path | 
| 329 |  |  |  |  |  |  | ); | 
| 330 | 0 |  |  |  |  |  | return(@files); | 
| 331 |  |  |  |  |  |  | } | 
| 332 |  |  |  |  |  |  |  | 
| 333 |  |  |  |  |  |  | # get all files in a path with relative paths | 
| 334 |  |  |  |  |  |  | sub get_all_files { | 
| 335 | 0 |  |  | 0 | 0 |  | my $path = shift; | 
| 336 |  |  |  |  |  |  |  | 
| 337 | 0 | 0 |  |  |  |  | return() if not -d $path; | 
| 338 |  |  |  |  |  |  |  | 
| 339 | 0 | 0 |  |  |  |  | opendir my $dh, $path or die "Could not open path '$path': $!"; | 
| 340 | 0 |  |  |  |  |  | my @files = readdir($dh); | 
| 341 | 0 |  |  |  |  |  | closedir $dh; | 
| 342 |  |  |  |  |  |  |  | 
| 343 | 0 |  |  |  |  |  | my @use_files; | 
| 344 | 0 |  |  |  |  |  | FILE: foreach my $file (@files) { | 
| 345 | 0 |  |  |  |  |  | foreach my $exclude_regex (@exclude_files) { | 
| 346 | 0 | 0 |  |  |  |  | next FILE if $file =~ $exclude_regex; | 
| 347 |  |  |  |  |  |  | } | 
| 348 | 0 | 0 |  |  |  |  | push @use_files, $file if -f File::Spec->catfile($path, $file); | 
| 349 |  |  |  |  |  |  | } | 
| 350 |  |  |  |  |  |  |  | 
| 351 | 0 |  |  |  |  |  | return(@use_files); | 
| 352 |  |  |  |  |  |  | } | 
| 353 |  |  |  |  |  |  |  | 
| 354 |  |  |  |  |  |  | # produce the diff of a full directory | 
| 355 |  |  |  |  |  |  | sub dir_diff { | 
| 356 | 0 |  |  | 0 | 0 |  | my $output_file     = shift; | 
| 357 | 0 |  |  |  |  |  | my $pathspec        = shift; | 
| 358 | 0 |  |  |  |  |  | my $paths           = shift; | 
| 359 | 0 |  |  |  |  |  | my $recursive       = shift; | 
| 360 | 0 |  |  |  |  |  | my $exclude_regexes = shift; | 
| 361 |  |  |  |  |  |  |  | 
| 362 | 0 |  |  |  |  |  | my $map = dirs_to_filemapping( $pathspec, $recursive ); | 
| 363 |  |  |  |  |  |  |  | 
| 364 | 0 |  |  |  |  |  | foreach my $source_file (keys %$map) { | 
| 365 | 0 | 0 |  |  |  |  | next if grep {$source_file =~ $_} @$exclude_regexes; | 
|  | 0 |  |  |  |  |  |  | 
| 366 | 0 |  |  |  |  |  | my $pathspec = {%$pathspec}; | 
| 367 | 0 |  |  |  |  |  | $pathspec->{source_file} = $source_file; | 
| 368 | 0 |  |  |  |  |  | $pathspec->{blead_file} = $map->{$source_file}; | 
| 369 | 0 |  |  |  |  |  | file_diff( $output_file, $pathspec, $paths ); | 
| 370 |  |  |  |  |  |  | } | 
| 371 |  |  |  |  |  |  | } | 
| 372 |  |  |  |  |  |  |  | 
| 373 |  |  |  |  |  |  | # produce the diff of a single file | 
| 374 |  |  |  |  |  |  | sub file_diff { | 
| 375 | 0 |  |  | 0 | 0 |  | my $output_file     = shift; | 
| 376 | 0 |  |  |  |  |  | my $pathspec        = shift; | 
| 377 | 0 |  |  |  |  |  | my $paths           = shift; | 
| 378 |  |  |  |  |  |  |  | 
| 379 | 0 |  |  |  |  |  | my $absolute_source_file = File::Spec->catfile($pathspec->{source_path}, $pathspec->{source_file}); | 
| 380 | 0 |  |  |  |  |  | my $absolute_blead_file  = get_full_blead_path( $pathspec, $pathspec->{blead_file} ); | 
| 381 |  |  |  |  |  |  | #warn "Diffing '$absolute_source_file' to '$absolute_blead_file'"; | 
| 382 |  |  |  |  |  |  |  | 
| 383 | 0 | 0 |  |  |  |  | my @cmd = ($diff_cmd, ($ignore_space ? ('-w') : ()), qw(-u -N)); | 
| 384 | 0 | 0 |  |  |  |  | if ($reverse) { | 
| 385 | 0 |  |  |  |  |  | push @cmd, $absolute_blead_file, $absolute_source_file; | 
| 386 |  |  |  |  |  |  | } | 
| 387 |  |  |  |  |  |  | else { | 
| 388 | 0 |  |  |  |  |  | push @cmd, $absolute_source_file, $absolute_blead_file; | 
| 389 |  |  |  |  |  |  | } | 
| 390 | 0 |  |  |  |  |  | my $result = `@cmd`; | 
| 391 | 0 | 0 |  |  |  |  | my $blead_prefix = quotemeta($reverse ? '---' : '+++'); | 
| 392 | 0 | 0 |  |  |  |  | my $source_prefix = quotemeta($reverse ? '+++' : '---'); | 
| 393 |  |  |  |  |  |  |  | 
| 394 | 0 |  |  |  |  |  | my $patched_filename; | 
| 395 | 0 | 0 |  |  |  |  | my $bleadpath_patched_filename = | 
| 396 |  |  |  |  |  |  | defined($pathspec->{blead_module_path}) | 
| 397 |  |  |  |  |  |  | ? File::Spec->catfile( $pathspec->{blead_module_path}, $pathspec->{blead_file} ) | 
| 398 |  |  |  |  |  |  | : $pathspec->{blead_file}; | 
| 399 |  |  |  |  |  |  |  | 
| 400 | 0 | 0 |  |  |  |  | if ($paths eq 'dual') { | 
|  |  | 0 |  |  |  |  |  | 
| 401 | 0 |  |  |  |  |  | $patched_filename = $pathspec->{source_file}; | 
| 402 |  |  |  |  |  |  | } elsif ($paths eq 'blead') { | 
| 403 | 0 |  |  |  |  |  | $patched_filename = $bleadpath_patched_filename; | 
| 404 |  |  |  |  |  |  | } else { | 
| 405 | 0 | 0 |  |  |  |  | $patched_filename = $reverse ? $bleadpath_patched_filename : $pathspec->{source_file}; | 
| 406 |  |  |  |  |  |  | } | 
| 407 |  |  |  |  |  |  | #my $patched_filename = $reverse ? $source_file : $blead_file; | 
| 408 |  |  |  |  |  |  |  | 
| 409 |  |  |  |  |  |  | #$result =~ s{^($blead_prefix\s*)(\S+)}{$1 . remove_path_prefix($2, $blead_base_dir)}gme; | 
| 410 |  |  |  |  |  |  | #$result =~ s{^($source_prefix\s*)(\S+)}{$1 . remove_path_prefix($2, $source_base_dir)}gme; | 
| 411 |  |  |  |  |  |  |  | 
| 412 | 0 |  |  |  |  |  | $result =~ s{^($blead_prefix\s+)(\S+)}{$1 . $patched_filename}gme; | 
|  | 0 |  |  |  |  |  |  | 
| 413 | 0 |  |  |  |  |  | $result =~ s{^($source_prefix\s+)(\S+)}{$1 . $patched_filename}gme; | 
|  | 0 |  |  |  |  |  |  | 
| 414 |  |  |  |  |  |  |  | 
| 415 | 0 | 0 |  |  |  |  | if (defined $output_file) { | 
| 416 | 0 |  |  |  |  |  | print $output_file $result; | 
| 417 |  |  |  |  |  |  | } | 
| 418 |  |  |  |  |  |  | else { | 
| 419 | 0 |  |  |  |  |  | print $result; | 
| 420 |  |  |  |  |  |  | } | 
| 421 |  |  |  |  |  |  | } | 
| 422 |  |  |  |  |  |  |  | 
| 423 |  |  |  |  |  |  | # remove a prefix from a path | 
| 424 |  |  |  |  |  |  | sub remove_path_prefix { | 
| 425 | 0 |  |  | 0 | 0 |  | my $path   = shift; | 
| 426 | 0 |  |  |  |  |  | my $prefix = shift; | 
| 427 | 0 |  |  |  |  |  | $path =~ s/^\Q$prefix\E//; | 
| 428 | 0 |  |  |  |  |  | $path =~ s/^[\/\\]+//; | 
| 429 | 0 |  |  |  |  |  | return $path; | 
| 430 |  |  |  |  |  |  | } | 
| 431 |  |  |  |  |  |  |  | 
| 432 |  |  |  |  |  |  | # turn something that may look like a module or | 
| 433 |  |  |  |  |  |  | # distribution into an URL using CPAN | 
| 434 |  |  |  |  |  |  | sub module_or_dist_to_url { | 
| 435 | 0 |  |  | 0 | 0 |  | my $module_name = shift; | 
| 436 |  |  |  |  |  |  | #my $use_dev_versions = shift; | 
| 437 |  |  |  |  |  |  |  | 
| 438 | 0 |  |  |  |  |  | my $distro; | 
| 439 | 0 | 0 |  |  |  |  | if ($module_name =~ /[\/.]/) { | 
| 440 | 0 |  |  |  |  |  | my $dist = CPAN::Shell->expandany($module_name); | 
| 441 | 0 | 0 |  |  |  |  | if (not defined $dist) { | 
| 442 | 0 |  |  |  |  |  | warn "Could not find distribution '$module_name' on CPAN\n"; | 
| 443 | 0 |  |  |  |  |  | return(); | 
| 444 |  |  |  |  |  |  | } | 
| 445 | 0 | 0 |  |  |  |  | $dist = $dist->distribution() if ref($dist) eq 'CPAN::Module'; | 
| 446 | 0 | 0 |  |  |  |  | if (not ref($dist) eq 'CPAN::Distribution') { | 
| 447 | 0 |  |  |  |  |  | warn "Could not find distribution '$module_name' on CPAN\n"; | 
| 448 | 0 |  |  |  |  |  | return(); | 
| 449 |  |  |  |  |  |  | } | 
| 450 | 0 |  |  |  |  |  | $distro = $dist->pretty_id(); | 
| 451 | 0 |  |  |  |  |  | warn "Assuming you specified a distribution name. Found the '$distro' distribution on CPAN\n"; | 
| 452 |  |  |  |  |  |  | } | 
| 453 |  |  |  |  |  |  | else { | 
| 454 | 0 |  |  |  |  |  | my $module = CPAN::Shell->expand("Module", $module_name); | 
| 455 | 0 | 0 |  |  |  |  | if (not defined $module) { | 
| 456 | 0 |  |  |  |  |  | warn "Could not find module '$module_name' on CPAN\n"; | 
| 457 | 0 |  |  |  |  |  | return(); | 
| 458 |  |  |  |  |  |  | } | 
| 459 | 0 |  |  |  |  |  | $distro = $module->distribution()->pretty_id(); | 
| 460 | 0 |  |  |  |  |  | warn "Assuming you specified a module name. Found the '$distro' distribution on CPAN\n"; | 
| 461 |  |  |  |  |  |  | } | 
| 462 |  |  |  |  |  |  |  | 
| 463 | 0 | 0 |  |  |  |  | $distro =~ /^([^\/]+)/ or die; | 
| 464 | 0 |  |  |  |  |  | $distro = substr($1, 0, 1) . "/" . substr($1, 0, 2) . "/" . $distro; | 
| 465 |  |  |  |  |  |  |  | 
| 466 | 0 |  |  |  |  |  | my $mirrors = $CPAN::Config->{urllist}; | 
| 467 | 0 | 0 | 0 |  |  |  | if (not defined $mirrors or not ref($mirrors) eq 'ARRAY' or not @$mirrors) { | 
|  |  |  | 0 |  |  |  |  | 
| 468 | 0 |  |  |  |  |  | warn "Could not determine CPAN mirror"; | 
| 469 | 0 |  |  |  |  |  | return(); | 
| 470 |  |  |  |  |  |  | } | 
| 471 |  |  |  |  |  |  |  | 
| 472 | 0 |  |  |  |  |  | my $url = $mirrors->[0]; | 
| 473 | 0 |  |  |  |  |  | $url =~ s/\/+$//; | 
| 474 | 0 |  |  |  |  |  | return $url . '/authors/id/' . $distro; | 
| 475 |  |  |  |  |  |  | } | 
| 476 |  |  |  |  |  |  |  | 
| 477 |  |  |  |  |  |  | sub get_full_blead_path { | 
| 478 | 0 |  |  | 0 | 0 |  | my $pathspec = shift; | 
| 479 | 0 |  |  |  |  |  | my $path = shift; | 
| 480 | 0 | 0 |  |  |  |  | if (defined $pathspec->{blead_module_path}) { | 
| 481 | 0 |  |  |  |  |  | return File::Spec->catdir($pathspec->{blead_path}, $pathspec->{blead_module_path}, $path); | 
| 482 |  |  |  |  |  |  | } | 
| 483 |  |  |  |  |  |  | else { | 
| 484 | 0 |  |  |  |  |  | return File::Spec->catdir($pathspec->{blead_path}, $path); | 
| 485 |  |  |  |  |  |  | } | 
| 486 |  |  |  |  |  |  | } | 
| 487 |  |  |  |  |  |  |  | 
| 488 |  |  |  |  |  |  | 1; | 
| 489 |  |  |  |  |  |  | __END__ |