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