File Coverage

inc/Module/Build/Sqitch.pm
Criterion Covered Total %
statement 57 167 34.1
branch 7 66 10.6
condition 4 37 10.8
subroutine 13 22 59.0
pod 0 7 0.0
total 81 299 27.0


line stmt bran cond sub pod time code
1             package Module::Build::Sqitch;
2              
3 1     1   9 use strict;
  1         4  
  1         54  
4 1     1   6 use warnings;
  1         11  
  1         78  
5 1     1   830 use Module::Build 0.35;
  1         110806  
  1         56  
6 1     1   9 use base 'Module::Build';
  1         2  
  1         235  
7 1     1   591 use IO::File ();
  1         9157  
  1         32  
8 1     1   16 use File::Spec ();
  1         2  
  1         47  
9 1     1   7 use Config ();
  1         1  
  1         21  
10 1     1   5 use File::Path ();
  1         1  
  1         18  
11 1     1   4 use File::Copy ();
  1         2  
  1         2177  
12              
13             __PACKAGE__->add_property($_) for qw(etcdir installed_etcdir);
14              
15             # List one more more engines to include in a bundle install.
16             # --with postgres --with mysql
17             __PACKAGE__->add_property(with => []);
18              
19             # Set dual_life to true to force dual-life modules such as Pod::Simple to be
20             # included in the bundle directory.
21             # --dual_life 1
22             __PACKAGE__->add_property(dual_life => 0);
23              
24             sub new {
25 0     0 0 0 my ( $class, %p ) = @_;
26 0 0       0 if ($^O eq 'MSWin32') {
27 0   0     0 my $recs = $p{recommends} ||= {};
28 0         0 $recs->{$_} = 0 for qw(
29             Win32
30             Win32::Console::ANSI
31             Win32API::Net
32             );
33 0         0 $p{requires}{'Win32::Locale'} = 0;
34 0         0 $p{requires}{'Win32::ShellQuote'} = 0;
35 0         0 $p{requires}{'DateTime::TimeZone::Local::Win32'} = 0;
36             }
37 0 0 0     0 if (eval { require Hash::Merge; 1 } && $Hash::Merge::VERSION eq '0.298') {
  0         0  
  0         0  
38 0         0 warn join "\n", (
39             '**************************************************************',
40             '* You have Hash::Merge $Hash::Merge::VERSION, which is broken.',
41             "**************************************************************\n",
42             );
43 0         0 $p{requires}{'Hash::Merge'} = '0.299';
44             }
45 0         0 my $self = $class->SUPER::new(%p);
46 0         0 $self->add_build_element('etc');
47 0         0 $self->add_build_element('mo');
48 0         0 $self->add_build_element('sql');
49 0         0 return $self;
50             }
51              
52             sub _getetc {
53 2     2   29 my $self = shift;
54 2         5 my $prefix;
55              
56 2 50       18 if ($self->installdirs eq 'site') {
    0          
57 2   33     189 $prefix = $Config::Config{siteprefix} // $Config::Config{prefix};
58             } elsif ($self->installdirs eq 'vendor') {
59 0   0     0 $prefix = $Config::Config{vendorprefix} // $Config::Config{siteprefix} // $Config::Config{prefix};
      0        
60             } else {
61 0         0 $prefix = $Config::Config{prefix};
62             }
63              
64             # Prefer the user-specified directory.
65 2 50       15 if (my $etc = $self->etcdir) {
66 0         0 return $etc;
67             }
68              
69             # Use a directory under the install base (or prefix).
70 2         31 my @subdirs = qw(etc sqitch);
71 2 50 33     26 if ( my $dir = $self->install_base || $self->prefix ) {
72 0         0 return File::Spec->catdir( $dir, @subdirs );
73             }
74              
75             # Go under Perl's prefix.
76 2         84 return File::Spec->catdir( $prefix, @subdirs );
77             }
78              
79             sub ACTION_move_old_templates {
80 0     0 0 0 my $self = shift;
81 0         0 $self->depends_on('build');
82              
83             # First, rename existing etc dir templates; They were moved in v0.980.
84 0         0 my $notify = 0;
85 0 0       0 my $tmpl_dir = File::Spec->catdir(
86             ( $self->destdir ? $self->destdir : ()),
87             $self->_getetc,
88             'templates'
89             );
90 0 0 0     0 if (-e $tmpl_dir && -d _) {
91             # Scan for old templates, but only if we can read the directory.
92 0 0       0 if (opendir my $dh, $tmpl_dir) {
93 0         0 while (my $bn = readdir $dh) {
94 0 0       0 next unless $bn =~ /^(deploy|verify|revert)[.]tmpl([.]default)?$/;
95 0         0 my ($action, $default) = ($1, $2);
96 0         0 my $file = File::Spec->catfile($tmpl_dir, $bn);
97 0 0       0 if ($default) {
98 0         0 $self->log_verbose("Unlinking $file\n");
99             # Just unlink default files.
100 0         0 unlink $file;
101 0         0 next;
102             }
103             # Move action templates to $action/pg.tmpl and $action/sqlite.tmpl.
104 0         0 my $action_dir = File::Spec->catdir($tmpl_dir, $action);
105 0 0       0 File::Path::mkpath($action_dir) or die;
106 0         0 for my $engine (qw(pg sqlite)) {
107 0         0 my $dest = File::Spec->catdir($action_dir, "$engine.tmpl");
108 0         0 $self->log_info("Copying old $bn to $dest\n");
109 0 0       0 File::Copy::copy($file, $dest)
110             or die "Cannot copy('$file', '$dest'): $!\n";
111             }
112              
113 0         0 $self->log_verbose("Unlinking $file\n");
114 0         0 unlink $file;
115 0         0 $notify = 1;
116             }
117             }
118             }
119              
120             # If we moved any files, nofify the user that custom templates will need
121             # to be updated, too.
122 0 0       0 if ($notify) {
123 0         0 $self->log_warn(q{
124             #################################################################
125             # WARNING #
126             # #
127             # As of v0.980, the location of script templates has changed. #
128             # The system-wide templates have been moved to their new #
129             # locations as described above. However, user-specific #
130             # templates have not been moved. #
131             # #
132             # Please inform all users that any custom Sqitch templates in #
133             # their ~/.sqitch/templates directories must be moved into #
134             # subdirectories using the appropriate engine name (pg, sqlite, #
135             # or oracle) as follows: #
136             # #
137             # deploy.tmpl -> deploy/$engine.tmpl #
138             # revert.tmpl -> revert/$engine.tmpl #
139             # verify.tmpl -> verify/$engine.tmpl #
140             # #
141             #################################################################
142             } . "\n");
143             }
144             }
145              
146             sub ACTION_install {
147 0     0 0 0 my ($self, @params) = @_;
148 0         0 $self->depends_on('move_old_templates');
149 0         0 $self->SUPER::ACTION_install(@_);
150             }
151              
152             sub process_etc_files {
153 1     1 0 20380 my $self = shift;
154 1         19 my $etc = $self->_getetc;
155 1         25 $self->install_path( etc => $etc );
156              
157 1 50       37 if (my $ddir = $self->destdir) {
158             # Need to search the final destination directory.
159 0         0 $etc = File::Spec->catdir($ddir, $etc);
160             }
161              
162 1 100   38   51 for my $file ( @{ $self->rscan_dir( 'etc', sub { -f && !/\.\#/ } ) } ) {
  1         20  
  38         3006  
163 32         28718 $file = $self->localize_file_path($file);
164              
165             # Remove leading `etc/` to get path relative to $etc.
166 32         1243 my ($vol, $dirs, $fn) = File::Spec->splitpath($file);
167 32         205 my (undef, @segs) = File::Spec->splitdir($dirs);
168 32         338 my $rel = File::Spec->catpath($vol, File::Spec->catdir(@segs), $fn);
169              
170 32         59 my $dest = $file;
171              
172             # Append .default if file already exists at its ultimate destination
173             # or if it exists with an old name (to be moved by move_old_templates).
174 32 50 0     903 if ( -e File::Spec->catfile($etc, $rel) || (
      0        
      33        
175             $segs[0] eq 'templates'
176             && $fn =~ /^(?:pg|sqlite)[.]tmpl$/
177             && -e File::Spec->catfile($etc, 'templates', "$segs[1].tmpl")
178             ) ) {
179 32         64 $dest .= '.default';
180             }
181              
182             $self->copy_if_modified(
183 32         141 from => $file,
184             to => File::Spec->catfile( $self->blib, $dest )
185             );
186             }
187             }
188              
189             sub process_pm_files {
190 1     1 0 3461 my $self = shift;
191 1         13 my $ret = $self->SUPER::process_pm_files(@_);
192 1         5892 my $pm = File::Spec->catfile(qw(blib lib App Sqitch Config.pm));
193 1   33     9 my $etc = $self->installed_etcdir || $self->_getetc;
194              
195 1         7 $self->do_system(
196             $self->perl, '-i.bak', '-pe',
197             qq{s{my \\\$SYSTEM_DIR = undef}{my \\\$SYSTEM_DIR = q{\Q$etc\E}}},
198             $pm,
199             );
200 1         538308 unlink "$pm.bak";
201              
202 1         41 return $ret;
203             }
204              
205             sub fix_shebang_line {
206 0     0 0   my $self = shift;
207             # Noting to do after 5.10.0.
208 0 0         return $self->SUPER::fix_shebang_line(@_) if $] > 5.010000;
209              
210             # Remove -C from the shebang line.
211 0           for my $file (@_) {
212 0 0         my $FIXIN = IO::File->new($file) or die "Can't process '$file': $!";
213 0           local $/ = "\n";
214 0           chomp(my $line = <$FIXIN>);
215 0 0         next unless $line =~ s/^\s*\#!\s*//; # Not a shebang file.
216              
217 0           my ($cmd, $arg) = (split(' ', $line, 2), '');
218 0 0 0       next unless $cmd =~ /perl/i && $arg =~ s/ -C\w+//;
219              
220             # We removed -C; write the file out.
221 0 0         my $FIXOUT = IO::File->new(">$file.new")
222             or die "Can't create new $file: $!\n";
223 0           local $\;
224 0           undef $/; # Was localized above
225 0           print $FIXOUT "#!$cmd $arg", <$FIXIN>;
226 0           close $FIXIN;
227 0           close $FIXOUT;
228              
229 0 0         rename($file, "$file.bak")
230             or die "Can't rename $file to $file.bak: $!";
231              
232 0 0         rename("$file.new", $file)
233             or die "Can't rename $file.new to $file: $!";
234              
235 0 0         $self->delete_filetree("$file.bak")
236             or $self->log_warn("Couldn't clean up $file.bak, leaving it there");
237             }
238              
239             # Back at it now.
240 0           return $self->SUPER::fix_shebang_line(@_);
241             }
242              
243             sub ACTION_bundle {
244 0     0 0   my ($self, @params) = @_;
245 0 0         my $base = $self->install_base or die "No --install_base specified\n";
246              
247             # XXX Consider replacing with a Carton or Carmel-based solution?
248             SHHH: {
249 0     0     local $SIG{__WARN__} = sub {}; # Menlo has noisy warnings.
  0            
250 0           local $ENV{PERL_CPANM_OPT}; # Override cpanm options.
251 0           require Menlo::Sqitch;
252 0   0       my $feat = $self->with || [];
253 0 0         $feat = [$feat] unless ref $feat;
254             my $app = Menlo::Sqitch->new(
255             quiet => $self->quiet,
256             verbose => $self->verbose,
257             notest => 1,
258             self_contained => 1,
259             skip_installed => 0,
260             install_types => [qw(requires recommends)],
261             local_lib => File::Spec->rel2abs($base),
262             pod2man => undef,
263 0           features => { map { $_ => 1 } @{ $feat } },
  0            
  0            
264             );
265              
266 0 0         if ($self->dual_life) {
267             # Force Install dual-life modules.
268 0           $app->{argv} = [qw(
269             File::Temp Scalar::Util Pod::Usage Digest::SHA Pod::Escapes
270             Pod::Find Getopt::Long Time::HiRes File::Path List::Util
271             Encode Pod::Simple Time::Local parent IO::File if
272             Term::ANSIColor
273             )];
274 0 0         die "Error installing modules: $@\n" if $app->run;
275             }
276              
277             # Install required modules, but not Sqitch itself.
278 0           $app->{argv} = ['.'];
279 0           $app->{installdeps} = 1;
280 0 0         die "Error installing modules: $@\n" if $app->run;
281              
282             # Remove unneeded build-time dependencies.
283 0 0         die "Error removing build modules: $@\n"
284             unless $app->remove_build_dependencies;
285             }
286              
287             # Install Sqitch. Required to intall man pages.
288 0           $self->depends_on('install');
289              
290             # Delete unneeded files.
291 0           $self->delete_filetree(File::Spec->catdir($base, qw(lib perl5 Test)));
292 0           $self->delete_filetree(File::Spec->catdir($base, qw(bin)));
293 0           for my $file (@{ $self->rscan_dir($base, qr/[.](?:meta|packlist)$/) }) {
  0            
294 0           $self->delete_filetree($file);
295             }
296              
297             # Install sqitch script using FindBin.
298 0           $self->_copy_findbin_script;
299              
300             # Delete empty directories.
301 0     0     File::Find::finddepth(sub{rmdir},$base);
  0            
302             }
303              
304             sub _copy_findbin_script {
305 0     0     my $self = shift;
306             # XXX Switch to lib/perl5.
307 0           my $bin = $self->install_destination('script');
308 0           my $script = File::Spec->catfile(qw(bin sqitch));
309 0           my $dest = File::Spec->catfile($bin, 'sqitch');
310 0 0         my $result = $self->copy_if_modified($script, $bin, 'flatten') or return;
311 0 0         $self->fix_shebang_line($result) unless $self->is_vmsish;
312 0           $self->_set_findbin($result);
313 0           $self->make_executable($result);
314             }
315              
316             sub _set_findbin {
317 0     0     my ($self, $file) = @_;
318 0           local $^I = '';
319 0           local @ARGV = ($file);
320 0           while (<>) {
321 0           s{^use App::Sqitch}{use FindBin;\nuse lib "\$FindBin::RealBin/../lib/perl5";\nuse App::Sqitch};
322 0           print;
323             }
324             }