package XS::Install::Util; use strict; use warnings; use XS::Install::Payload; use Fcntl qw(:flock); # import LOCK_* constants sub linearize_dependent { my $modules = shift; my %modules = map { $_ => 1 } @$modules; # make list of all dependent my %dependent; for my $module (@$modules) { my $info = XS::Install::Payload::binary_module_info($module) or next; my $dependent = $info->{BIN_DEPENDENT} || []; for my $d_module (@$dependent) { next unless $modules{$d_module}; push @{ $dependent{$module} }, $d_module; } } my $get_score; $get_score = sub { my $module = shift; my $score = 1; # initial value for myself my $dependent = $dependent{$module} || []; for my $d_module (@$dependent) { $score += $get_score->($d_module); } return $score; }; my %scores = map { $_ => $get_score->($_) } @$modules; my @ordered_modules = sort { $scores{$a} <=> $scores{$b} || $a cmp $b } @$modules; return \@ordered_modules; } sub cmd_sync_bin_deps { my $myself = shift @ARGV; my @modules = @ARGV; foreach my $module (sort @modules) { my $file = XS::Install::Payload::binary_module_info_file($module); my $lock_file = "$file.lock"; my $fh_lock; open $fh_lock, '>', $lock_file or warn "Cannot open $lock_file for writing: $!\n"; if ($fh_lock) { my $ok = eval { flock($fh_lock, LOCK_EX); 1 }; warn "Cannot lock $lock_file: $! ($@)\n" unless $ok; } my $info = XS::Install::Payload::binary_module_info($module) or next; my $dependent = $info->{BIN_DEPENDENT} || []; my %tmp = map {$_ => 1} grep {$_ ne $module} @$dependent; $tmp{$myself} = 1; $info->{BIN_DEPENDENT} = linearize_dependent([keys %tmp]); delete $info->{BIN_DEPENDENT} unless @{$info->{BIN_DEPENDENT}}; my $ok = eval { module_info_write($file, $info); 1 }; unless ($ok) { warn("Reverse dependency write failed: $@"); } if ($fh_lock) { # possible errors are ignored, as we can do nothing flock($fh_lock, LOCK_UN) && unlink($lock_file); } } } sub cmd_check_dependencies { require XS::Install::Deps; my $objext = shift @ARGV; my (@inc, @cfiles, @xsfiles); my $curlist = \@cfiles; foreach my $arg (@ARGV) { if ($arg =~ s/^-I//) { push @inc, $arg; } elsif ($arg eq '-xs') { $curlist = \@xsfiles; } else { push @$curlist, $arg; } } my @touch_list = ( _check_mtimes( XS::Install::Deps::find_header_deps({ files => \@cfiles, headers => ['./'], inc => \@inc, }), sub { my $ofile = shift; $ofile =~ s/\.[^.]+$//; $ofile .= $objext; return $ofile; }, ), _check_mtimes(XS::Install::Deps::find_xsi_deps(\@xsfiles)) ); if (@touch_list) { my $now = time(); utime($now, $now, @touch_list); } } sub _check_mtimes { my ($deps, $reference_file_sub) = @_; my %mtimes; my @touch_list; foreach my $file (keys %$deps) { my $list = $deps->{$file} or next; my $reference_file = $reference_file_sub ? $reference_file_sub->($file) : $file; my $reference_time = (stat($reference_file))[9] or next; foreach my $depfile (@$list) { my $mtime = $mtimes{$depfile} ||= (stat($depfile))[9]; next if $mtime <= $reference_time; #warn "for file $file dependency $depfile changed"; push @touch_list, $file; last; } } return @touch_list; } sub module_info_write { my ($file, $info) = @_; require Data::Dumper; local $Data::Dumper::Terse = 1; local $Data::Dumper::Indent = 0; my $content = Data::Dumper::Dumper($info); my $restore_mode; if (-e $file) { # make sure we have permissions to write, because perl installs files with 444 perms my $mode = (stat $file)[2]; unless ($mode & 0200) { # if not, temporary enable write permissions $restore_mode = $mode; $mode |= 0200; chmod $mode, $file; } } my $temp_file = "$file.$$"; open my $fh, '>', $temp_file or die "Cannot open $temp_file for writing: $!, binary data could not be written\n"; print $fh $content; close $fh; rename $temp_file, $file || die("Cannot rename $temp_file to $file\n"); chmod $restore_mode, $file if $restore_mode; # restore old perms if we changed it } 1;