You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
675 lines
22 KiB
675 lines
22 KiB
package ksb::Updater::Svn; |
|
|
|
# Module responsible for source code updates on Subversion modules. Used as a |
|
# superclass for our l10n update/build system as well. |
|
|
|
use strict; |
|
use warnings; |
|
use 5.014; |
|
|
|
our $VERSION = '0.10'; |
|
|
|
use ksb::Debug; |
|
use ksb::Util; |
|
use ksb::Updater; |
|
|
|
our @ISA = qw(ksb::Updater); |
|
|
|
use IPC::Open3 qw(open3); |
|
|
|
# Returns true if a module has a base component to their name (e.g. KDE/, |
|
# extragear/, or playground). Note that modules that aren't in trunk/KDE |
|
# don't necessary meet this criteria (e.g. kdereview is a module itself). |
|
sub _has_base_module |
|
{ |
|
my $moduleName = shift; |
|
|
|
return $moduleName =~ /^(extragear|playground|KDE)(\/[^\/]+)?$/; |
|
} |
|
|
|
# Subroutine to return the branch prefix. i.e. the part before the branch |
|
# name and module name. |
|
# |
|
# The first parameter is the module name in question. |
|
# The second parameter should be 'branches' if we're dealing with a branch |
|
# or 'tags' if we're dealing with a tag. |
|
# |
|
# Ex: 'kdelibs' => 'branches/KDE' |
|
# 'kdevelop' => 'branches/kdevelop' |
|
sub _branch_prefix |
|
{ |
|
my $moduleName = shift; |
|
my $type = shift; |
|
|
|
# These modules seem to have their own subdir in /tags. |
|
my @tag_components = qw/arts koffice amarok kst qt taglib/; |
|
|
|
# The map call adds the kde prefix to the module names because I don't feel |
|
# like typing them all in. |
|
my @kde_module_list = ((map {'kde' . $_} qw/-base-artwork -wallpapers accessibility |
|
addons admin artwork base bindings edu games graphics libs |
|
network pim pimlibs plasma-addons sdk toys utils webdev/)); |
|
|
|
# If the user already has the module in the form KDE/foo, it's already |
|
# done. |
|
return "$type/KDE" if $moduleName =~ /^KDE\//; |
|
|
|
# KDE proper modules seem to use this pattern. |
|
return "$type/KDE" if list_has(\@kde_module_list, $moduleName); |
|
|
|
# KDE extragear / playground modules use this pattern |
|
return "$type" if _has_base_module($moduleName); |
|
|
|
# If we doing a tag just return 'tags' because the next part is the actual |
|
# tag name, which is added by the caller, unless the module has its own |
|
# subdirectory in /tags. |
|
return "$type" if $type eq 'tags' and not list_has(\@tag_components, $moduleName); |
|
|
|
# Everything else. |
|
return "$type/$moduleName"; |
|
} |
|
|
|
# This subroutine is responsible for stripping the KDE/ part from the |
|
# beginning of modules that were entered by the user like "KDE/kdelibs" |
|
# instead of the normal "kdelibs". That way you can search for kdelibs |
|
# without having to strip KDE/ everywhere. |
|
sub _moduleBaseName |
|
{ |
|
my $moduleName = shift; |
|
$moduleName =~ s/^KDE\///; |
|
|
|
return $moduleName; |
|
} |
|
|
|
# Subroutine to return a module URL for a module using the 'branch' option. |
|
# First parameter is the module in question. |
|
# Second parameter is the type ('tags' or 'branches') |
|
sub _handle_branch_tag_option |
|
{ |
|
my $module = assert_isa(shift, 'ksb::Module'); |
|
my $type = shift; |
|
my $branch = _branch_prefix($module->name(), $type); |
|
my $svn_server = $module->getOption('svn-server'); |
|
my $branchname = $module->getOption($type eq 'branches' ? 'branch' : 'tag'); |
|
|
|
# Remove trailing slashes. |
|
$svn_server =~ s/\/*$//; |
|
|
|
# Remove KDE/ prefix for module name. |
|
my $moduleName = _moduleBaseName($module->name()); |
|
|
|
# KDE modules have a different module naming scheme than the rest it seems. |
|
return "$svn_server/$branch/$branchname/$moduleName" if $branch =~ /\/KDE\/?$/; |
|
|
|
# Non-trunk translations happen in a single branch. Assume all non-trunk |
|
# global branches are intended for the stable translations. |
|
if ($moduleName =~ /^l10n-kde4\/?/ && $branch ne 'trunk') { |
|
return "$svn_server/branches/stable/$moduleName"; |
|
} |
|
|
|
# Otherwise don't append the module name by default since it makes more |
|
# sense to branch this way in many situations (i.e. kdesupport tags, phonon) |
|
return "$svn_server/$branch/$branchname"; |
|
} |
|
|
|
# Subroutine to return the appropriate SVN URL for a given module, based on |
|
# the user settings. For example, 'kdelibs' -> |
|
# https://svn.kde.org/home/kde/trunk/KDE/kdelibs |
|
# |
|
# This operates under a double hierarchy: |
|
# 1. If any module-specific option is present, it wins. |
|
# 2. If only global options are present, the order override-url, tag, |
|
# branch, module-base-path, is preferred. |
|
sub svn_module_url |
|
{ |
|
my $self = assert_isa(shift, 'ksb::Updater::Svn'); |
|
my $module = $self->module(); |
|
my $svn_server = $module->getOption('svn-server'); |
|
my $modulePath; |
|
|
|
foreach my $levelLimit ('module', 'allow-inherit') { |
|
$modulePath = $module->getOption('module-base-path', $levelLimit); |
|
|
|
# Allow user to override normal processing of the module in a few ways, |
|
# to make it easier to still be able to use kdesrc-build even when I |
|
# can't be there to manually update every little special case. |
|
if($module->getOption('override-url', $levelLimit)) |
|
{ |
|
return $module->getOption('override-url', $levelLimit); |
|
} |
|
|
|
if($module->getOption('tag', $levelLimit)) |
|
{ |
|
return _handle_branch_tag_option($module, 'tags'); |
|
} |
|
|
|
my $branch = $module->getOption('branch', $levelLimit); |
|
if($branch and $branch ne 'trunk') |
|
{ |
|
return _handle_branch_tag_option($module, 'branches'); |
|
} |
|
|
|
my $moduleName = _moduleBaseName($module->name()); |
|
|
|
# The following modules are in /trunk, not /trunk/KDE. There are others, |
|
# but these are the important ones. |
|
my @non_trunk_modules = qw(extragear kdesupport koffice icecream kde-common |
|
playground KDE kdereview www l10n-kde4); |
|
|
|
my $module_root = $moduleName; |
|
$module_root =~ s/\/.*//; # Remove everything after the first slash |
|
|
|
if (not $modulePath and $levelLimit eq 'allow-inherit') |
|
{ |
|
$modulePath = "trunk/KDE/$moduleName"; |
|
$modulePath = "trunk/$moduleName" if list_has(\@non_trunk_modules, $module_root); |
|
$modulePath =~ s/^\/*//; # Eliminate / at beginning of string. |
|
$modulePath =~ s/\/*$//; # Likewise at the end. |
|
} |
|
|
|
last if $modulePath; |
|
} |
|
|
|
# Remove trailing slashes. |
|
$svn_server =~ s/\/*$//; |
|
|
|
# Note that the module name is no longer appended if module-base-path is used (i.e. |
|
# $branch variable was set. This is a change as of version 1.8. |
|
return "$svn_server/$modulePath"; |
|
} |
|
|
|
# Subroutine to determine whether or not the given module has the correct |
|
# URL. If not, a warning is printed out. |
|
# First parameter: module to check. |
|
# Return: Nothing. |
|
sub _verifyCorrectServerURL |
|
{ |
|
my $self = assert_isa(shift, 'ksb::Updater::Svn'); |
|
my $module = $self->module(); |
|
|
|
my $module_expected_url = $self->svn_module_url(); |
|
my $module_actual_url = $self->svnInfo('URL'); |
|
|
|
if (!$module_actual_url) { |
|
croak_runtime ("Unable to determine working copy's svn URL for $module"); |
|
} |
|
|
|
$module_expected_url =~ s{/+$}{}; # Remove trailing slashes |
|
$module_actual_url =~ s{/+$}{}; # Remove trailing slashes |
|
|
|
if ($module_actual_url ne $module_expected_url) |
|
{ |
|
# Check if the --src-only flag was passed. |
|
my $module = $self->module(); |
|
if ($module->buildContext()->getOption('#allow-auto-repo-move')) |
|
{ |
|
note ("g[$module] is checked out from a different location than expected."); |
|
note ("Attempting to correct to $module_expected_url"); |
|
|
|
my ($expected_host, $expected_path) = |
|
($module_expected_url =~ m{://([^/]+)/(.*)$}); |
|
my ($actual_host, $actual_path) = |
|
($module_actual_url =~ m{://([^/]+)/(.*)$}); |
|
|
|
# If the path didn't change but the host info did try --relocate |
|
# otherwise try regular svn switch. |
|
if (($expected_path eq $actual_path) && ($expected_host ne $actual_host)) { |
|
log_command($module, 'svn-switch', [ |
|
'svn', 'switch', '--relocate', |
|
$module_actual_url, $module_expected_url]); |
|
} |
|
else { |
|
log_command($module, 'svn-switch', [ |
|
'svn', 'switch', $module_expected_url]); |
|
} |
|
return; |
|
} |
|
|
|
warning (<<EOF); |
|
y[!!] |
|
y[!!] g[$module] seems to be checked out from somewhere other than expected. |
|
y[!!] |
|
|
|
kdesrc-build expects: y[$module_expected_url] |
|
The module is actually from: y[$module_actual_url] |
|
|
|
If the module location is incorrect, you can fix it by either deleting the |
|
g[b[source] directory, or by changing to the source directory and running |
|
svn switch $module_expected_url |
|
|
|
If the module is fine, please update your configuration file. |
|
|
|
If you use kdesrc-build with --src-only it will try switching for you (might not work |
|
correctly). |
|
EOF |
|
} |
|
else { # The two URLs match, but are they *right*? Things changed June 2013 |
|
my ($uid, $url); |
|
# uid might be empty, we use $url to see if the match succeeds. |
|
($uid, $url) = $module_actual_url =~ m{^svn\+ssh://(?:([a-z]+)\@)?(svn\.kde\.org)}; |
|
|
|
if ($url && (!$uid || $uid ne 'svn')) { |
|
error ("SVN login scheme has changed for y[b[$module] as of 2013-06-21"); |
|
error ("\tPlease see https://mail.kde.org/pipermail/kde-cvs-announce/2013/000112.html"); |
|
error ("\tPlease update your b[svn-server] option to be:"); |
|
error ("\tb[g[svn+ssh://svn\@svn.kde.org/home/kde"); |
|
error ("\n\tThen, re-run kdesrc-build with the b[--src-only] option to complete the repair."); |
|
|
|
if (!$uid) { |
|
error (" r[b[* * *]: Note that your SVN URL has *no* username"); |
|
error (" r[b[* * *]: You should probably also double-check ~/.ssh/config"); |
|
error (" r[b[* * *]: for b[svn.kde.org] to ensure the correct default user (svn)"); |
|
} |
|
|
|
croak_runtime ("SVN server has changed login scheme, see error message"); |
|
} |
|
} |
|
} |
|
|
|
# This procedure should be run before any usage of a local working copy to |
|
# ensure it is valid. This should only be run if there's actually a local |
|
# copy. |
|
# |
|
# Any errors will be fatal, so a 'Runtime' exception would be raised. |
|
sub check_module_validity |
|
{ |
|
my $self = assert_isa(shift, 'ksb::Updater::Svn'); |
|
my $module = $self->module(); |
|
|
|
# svn 1.7 has a different working copy format that must be manually |
|
# converted. This will mess up everything else so make this our first |
|
# check. |
|
p_chdir($module->fullpath('source')); |
|
|
|
# gensym makes a symbol that can be made a filehandle by open3 |
|
use Symbol qw(gensym); |
|
|
|
# Can't use filter_program_output as that doesn't capture STDERR on |
|
# purpose. We, on the other hand, just want STDERR. |
|
my $stderrReader = gensym(); |
|
my $pid = open3(undef, undef, $stderrReader, |
|
'svn', '--non-interactive', 'status'); |
|
|
|
my @errorLines = grep { /:\s*E155036:/ } (<$stderrReader>); |
|
waitpid ($pid, 0); |
|
|
|
if (@errorLines) { |
|
warning (<<EOF); |
|
y[*] A new version of svn has been installed which requires a b[one-time] update |
|
y[*] Currently running b[svn upgrade], this may take some time but should only |
|
y[*] be needed once. |
|
EOF |
|
|
|
if (0 != log_command($module, 'svn-upgrade', ['svn', '--non-interactive', 'upgrade'])) { |
|
error (<<EOF); |
|
r[*] Unable to run b[svn upgrade] for b[r[$module]! |
|
r[*] If you have no local changes you should try deleting the $module |
|
r[*] source directory, and re-run b[kdesrc-build], which will re-download. |
|
r[*] |
|
r[*] There is no way for kdesrc-build to safely make this check for you as |
|
r[*] the old version of b[svn] is required to read the current repository! |
|
EOF |
|
croak_runtime("Unable to run svn upgrade for $module"); |
|
} |
|
|
|
# By this point svn-upgrade should have run successfully, unless |
|
# we're in pretend mode. |
|
if (pretending()) { |
|
croak_runtime("Unable to use --pretend for svn module $module until svn-upgrade is run"); |
|
} |
|
} |
|
|
|
# Ensure the URLs are correct. |
|
$self->_verifyCorrectServerURL(); |
|
} |
|
|
|
# Subroutine used to handle the checkout-only option. It handles updating |
|
# subdirectories of an already-checked-out module. |
|
# |
|
# This function can throw an exception in the event of a update failure. |
|
# |
|
# First parameter is the module. |
|
# All remaining parameters are subdirectories to check out. |
|
# |
|
# Returns the number of files changed by the update, or undef if unable to |
|
# be determined. |
|
sub update_module_subdirectories |
|
{ |
|
my $self = assert_isa(shift, 'ksb::Updater::Svn'); |
|
my $module = $self->module(); |
|
my $numChanged = 0; |
|
|
|
# If we have elements in @path, download them now |
|
for my $dir (@_) |
|
{ |
|
info ("\tUpdating g[$dir]"); |
|
|
|
my $logname = $dir; |
|
$logname =~ tr{/}{-}; |
|
|
|
my $count = $self->run_svn("svn-up-$logname", [ 'svn', 'up', $dir ]); |
|
$numChanged = undef unless defined $count; |
|
$numChanged += $count if defined $numChanged; |
|
} |
|
|
|
return $numChanged; |
|
} |
|
|
|
# Checkout a module that has not been checked out before, along with any |
|
# subdirectories the user desires. |
|
# |
|
# This function will throw an exception in the event of a failure to update. |
|
# |
|
# The first parameter is the module to checkout (including extragear and |
|
# playground modules). |
|
# All remaining parameters are subdirectories of the module to checkout. |
|
# |
|
# Returns number of files affected, or undef. |
|
sub checkout_module_path |
|
{ |
|
my $self = assert_isa(shift, 'ksb::Updater::Svn'); |
|
my $module = $self->module(); |
|
my @path = @_; |
|
my %pathinfo = $module->getInstallPathComponents('source'); |
|
my @args; |
|
|
|
if (not -e $pathinfo{'path'} and not super_mkdir($pathinfo{'path'})) |
|
{ |
|
croak_runtime ("Unable to create path r[$pathinfo{path}]!"); |
|
} |
|
|
|
p_chdir ($pathinfo{'path'}); |
|
|
|
my $svn_url = $self->svn_module_url(); |
|
my $modulename = $pathinfo{'module'}; # i.e. kdelibs for KDE/kdelibs as $module |
|
|
|
push @args, ('svn', 'co', '--non-interactive'); |
|
push @args, '-N' if scalar @path; # Tells svn to only update the base dir |
|
push @args, $svn_url; |
|
push @args, $modulename; |
|
|
|
note ("Checking out g[$module]"); |
|
|
|
my $count = $self->run_svn('svn-co', \@args); |
|
|
|
p_chdir ($pathinfo{'module'}) if scalar @path; |
|
|
|
my $count2 = $self->update_module_subdirectories(@path); |
|
|
|
return $count + $count2 if defined $count and defined $count2; |
|
return undef; |
|
} |
|
|
|
# Update a module that has already been checked out, along with any |
|
# subdirectories the user desires. |
|
# |
|
# This function will throw an exception in the event of an update failure. |
|
# |
|
# The first parameter is the module to checkout (including extragear and |
|
# playground modules). |
|
# All remaining parameters are subdirectories of the module to checkout. |
|
sub update_module_path |
|
{ |
|
my ($self, @path) = @_; |
|
assert_isa($self, 'ksb::Updater::Svn'); |
|
my $module = $self->module(); |
|
my $fullpath = $module->fullpath('source'); |
|
my @args; |
|
|
|
p_chdir ($fullpath); |
|
|
|
push @args, ('svn', 'up', '--non-interactive'); |
|
push @args, '-N' if scalar @path; |
|
|
|
note ("Updating g[$module]"); |
|
|
|
my $count = eval { $self->run_svn('svn-up', \@args); }; |
|
|
|
# Update failed, try svn cleanup. |
|
if (had_an_exception() && $@->{exception_type} ne 'ConflictPresent') |
|
{ |
|
info ("\tUpdate failed, trying a cleanup."); |
|
my $result = safe_system('svn', 'cleanup'); |
|
$result == 0 or croak_runtime ("Unable to update $module, " . |
|
"svn cleanup failed with exit code $result"); |
|
|
|
info ("\tCleanup complete."); |
|
|
|
# Now try again (allow exception to bubble up this time). |
|
$count = $self->run_svn('svn-up-2', \@args); |
|
} |
|
|
|
my $count2 = $self->update_module_subdirectories(@path); |
|
|
|
return $count + $count2 if defined $count and defined $count2; |
|
return undef; |
|
} |
|
|
|
# Run the svn command. This is a special subroutine so that we can munge |
|
# the generated output to see what files have been added, and adjust the |
|
# build according. |
|
# |
|
# This function will throw an exception in the event of a build failure. |
|
# |
|
# First parameter is the ksb::Module object we're building. |
|
# Second parameter is the filename to use for the log file. |
|
# Third parameter is a reference to a list, which is the command ('svn') |
|
# and all of its arguments. |
|
# Return value is the number of files update (may be undef if unable to tell) |
|
sub run_svn |
|
{ |
|
my ($self, $logfilename, $arg_ref) = @_; |
|
assert_isa($self, 'ksb::Updater::Svn'); |
|
my $module = $self->module(); |
|
|
|
my $revision = $module->getOption('revision'); |
|
if ($revision && $revision ne '0') |
|
{ |
|
my @tmp = @{$arg_ref}; |
|
|
|
# Insert after first two entries, deleting 0 entries from the |
|
# list. |
|
splice @tmp, 2, 0, '-r', $revision; |
|
$arg_ref = \@tmp; |
|
} |
|
|
|
my $count = 0; |
|
my $conflict = 0; |
|
|
|
my $callback = sub { |
|
return unless $_; |
|
|
|
# The check for capitalized letters in the second column is because |
|
# svn can use the first six columns for updates (the characters will |
|
# all be uppercase), which makes it hard to tell apart from normal |
|
# sentences (like "At Revision foo" |
|
$count++ if /^[UPDARGMC][ A-Z]/; |
|
$conflict = 1 if /^C[ A-Z]/; |
|
}; |
|
|
|
# Do svn update. |
|
my $result = log_command($module, $logfilename, $arg_ref, { callback => $callback }); |
|
|
|
return 0 if pretending(); |
|
|
|
croak_runtime("Error updating $module!") unless $result == 0; |
|
|
|
if ($conflict) |
|
{ |
|
warning ("Source code conflict exists in r[$module], this module will not"); |
|
warning ("build until it is resolved."); |
|
|
|
die make_exception('ConflictPresent', "Source conflicts exist in $module"); |
|
} |
|
|
|
return $count; |
|
} |
|
|
|
# Subroutine to check for subversion conflicts in a module. Basically just |
|
# runs svn st and looks for "^C". |
|
# |
|
# First parameter is the module to check for conflicts on. |
|
# Returns 0 if a conflict exists, non-zero otherwise. |
|
sub module_has_conflict |
|
{ |
|
my $module = assert_isa(shift, 'ksb::Module'); |
|
my $srcdir = $module->fullpath('source'); |
|
|
|
if ($module->getOption('no-svn')) |
|
{ |
|
whisper ("\tSource code conflict check skipped."); |
|
return 1; |
|
} |
|
else |
|
{ |
|
info ("\tChecking for source conflicts... "); |
|
} |
|
|
|
my $pid = open my $svnProcess, "-|"; |
|
if (!$pid) |
|
{ |
|
error ("\tUnable to open check source conflict status: b[r[$!]"); |
|
return 0; # false allows the build to proceed anyways. |
|
}; |
|
|
|
if (0 == $pid) |
|
{ |
|
close STDERR; # No broken pipe warnings |
|
|
|
disable_locale_message_translation(); |
|
exec {'svn'} (qw/svn --non-interactive st/, $srcdir) or |
|
croak_runtime("Cannot execute 'svn' program: $!"); |
|
# Not reached |
|
} |
|
|
|
while (<$svnProcess>) |
|
{ |
|
if (/^C/) |
|
{ |
|
error (<<EOF); |
|
The $module module has source code conflicts present. This can occur |
|
when you have made changes to the source code in the local copy |
|
at $srcdir |
|
that interfere with a change introduced in the source repository. |
|
EOF |
|
|
|
error (<<EOF); |
|
To fix this, y[if you have made no source changes that you haven't committed], |
|
run y[svn revert -R $srcdir] |
|
to bring the source directory back to a pristine state and trying building the |
|
module again. |
|
|
|
NOTE: Again, if you have uncommitted source code changes, running this command |
|
will delete your changes in favor of the version in the source repository. |
|
EOF |
|
|
|
kill "TERM", $pid; # Kill svn |
|
waitpid ($pid, 0); |
|
close $svnProcess; |
|
return 0; |
|
} |
|
} |
|
|
|
# conflicts cleared apparently. |
|
waitpid ($pid, 0); |
|
close $svnProcess; |
|
return 1; |
|
} |
|
|
|
# scm-specific update procedure. |
|
# May change the current directory as necessary. |
|
# Assumes called as part of a ksb::Module (i.e. $self->isa('ksb::Module') should be true. |
|
sub updateInternal |
|
{ |
|
my $self = assert_isa(shift, 'ksb::Updater::Svn'); |
|
my $module = $self->module(); |
|
my $fullpath = $module->fullpath('source'); |
|
my @options = split(' ', $module->getOption('checkout-only')); |
|
|
|
if (-e "$fullpath/.svn") { |
|
$self->check_module_validity(); |
|
my $updateCount = $self->update_module_path(@options); |
|
|
|
my $log_filter = sub { |
|
return unless defined $_; |
|
print $_ if /^C/; |
|
print $_ if /Checking for/; |
|
return; |
|
}; |
|
|
|
# Use log_command as the check so that an error file gets created. |
|
if (0 != log_command($module, 'conflict-check', |
|
['kdesrc-build', 'ksb::Updater::Svn::module_has_conflict', |
|
$module], |
|
{ callback => $log_filter, no_translate => 1 }) |
|
) |
|
{ |
|
croak_runtime (" * Conflicts present in module $module"); |
|
} |
|
|
|
return $updateCount; |
|
} |
|
else { |
|
return $self->checkout_module_path(@options); |
|
} |
|
} |
|
|
|
sub name |
|
{ |
|
return 'svn'; |
|
} |
|
|
|
sub currentRevisionInternal |
|
{ |
|
my $self = assert_isa(shift, 'ksb::Updater::Svn'); |
|
return $self->svnInfo('Revision'); |
|
} |
|
|
|
# Returns a requested parameter from 'svn info'. |
|
# |
|
# First parameter is a string with the name of the parameter to retrieve (e.g. URL). |
|
# Each line of output from svn info is searched for the requested string. |
|
# Returns the string value of the parameter or undef if an error occurred. |
|
sub svnInfo |
|
{ |
|
my $self = assert_isa(shift, 'ksb::Updater::Svn'); |
|
my $module = $self->module(); |
|
|
|
my $param = shift; |
|
my $srcdir = $module->fullpath('source'); |
|
my $result; # Predeclare to outscope upcoming eval |
|
|
|
if (pretending() && ! -e $srcdir) { |
|
return 'Unknown'; |
|
} |
|
|
|
# Search each line of output, ignore stderr. |
|
# eval since filter_program_output uses exceptions. |
|
eval |
|
{ |
|
# Need to chdir into the srcdir, in case srcdir is a symlink. |
|
# svn info /path/to/symlink barfs otherwise. |
|
p_chdir ($srcdir); |
|
|
|
my @lines = filter_program_output( |
|
sub { /^$param:/ }, |
|
'svn', 'info', '--non-interactive', '.' |
|
); |
|
|
|
croak_runtime ("No svn info output!") unless @lines; |
|
chomp ($result = $lines[0]); |
|
$result =~ s/^$param:\s*//; |
|
}; |
|
|
|
if($@) |
|
{ |
|
error ("Unable to run r[b[svn], is the Subversion program installed?"); |
|
error (" -- Error was: r[$@]"); |
|
return undef; |
|
} |
|
|
|
return $result; |
|
} |
|
|
|
1; |
|
|
|
|