@ -25,13 +25,12 @@ use Fcntl; # For sysopen constants
use Carp;
use POSIX qw(strftime :sys_wait_h);
use File::Find; # For our lndir reimplementation.
use File::Basename;
use File::Glob ':glob';
use File::Basename; # fileparse
use LWP::UserAgent;
use Sys::Hostname;
use Storable 'dclone';
use IO::Handle;
use IO::File;
use IPC::Open3;
use Errno qw(:POSIX);
use Data::Dumper;
@ -962,6 +961,8 @@ my $LOG_DATE; # Used by logging subsystem to create logs in same dir.
package ksb::BuildContext;
use Carp 'confess';
use File::Basename; # dirname
use IO::File;
# We derive from Module so that BuildContext acts like the 'global'
# Module, with some extra functionality.
@ -1288,6 +1289,123 @@ HOME
}
}
#
# Persistent option handling
#
# Reads in all persistent options from the file where they are kept
# (.kdesrc-build-data) for use in the program.
#
# The directory used is the same directory that contains the rc file in use.
sub loadPersistentOptions
{
my $self = assert_isa(shift, 'ksb::BuildContext');
my $rcfile = $self->rcFile();
my $dir = dirname($rcfile ? $rcfile : "");
my $fh = IO::File->new("<$dir/.kdesrc-build-data");
return unless $fh;
my $persistent_data;
{
local $/ = undef; # Read in whole file with <> operator.
$persistent_data = <$fh>;
}
# $persistent_data should be Perl code which, when evaluated will give us
# a hash called persistent_options which we can then merge into our
# package_opts.
my $persistent_options;
# eval must appear after declaration of $persistent_options
eval $persistent_data;
if ($@)
{
# Failed.
error ("Failed to read persistent module data: r[b[$@]");
return;
}
# We need to keep persistent data with the context instead of with the
# applicable modules since otherwise we might forget to write out
# persistent data for modules we didn't build in this run. So, we just
# store it all.
# Layout of this data:
# $self->persistent_options = {
# 'module-name' => {
# option => value,
# # foreach option/value pair
# },
# # foreach module
# }
$self->{persistent_options} = $persistent_options;
}
# Writes out the persistent options to the file .kdesrc-build-data.
#
# The directory used is the same directory that contains the rc file in use.
sub storePersistentOptions
{
my $self = assert_isa(shift, 'ksb::BuildContext');
return if pretending();
my $rcfile = $self->rcFile() // "";
my $dir = dirname($rcfile);
my $fh = IO::File->new("> $dir/.kdesrc-build-data");
if (!$fh)
{
error ("Unable to save persistent module data: b[r[$!]");
return;
}
print $fh "# AUTOGENERATED BY kdesrc-build $versionNum\n";
$Data::Dumper::Indent = 1;
print $fh Data::Dumper->Dump([$self->{persistent_options}], ["persistent_options"]);
undef $fh; # Closes the file
}
# Returns the value of a "persistent" option (normally read in as part of
# startup), or undef if there is no value stored.
#
# First parameter is the module name to get the option for, or 'global' if
# not for a module.
# Note that unlike setOption/getOption, no inheritance is done at this
# point so if an option is present globally but not for a module you
# must check both if that's what you want.
# Second parameter is the name of the value to retrieve (i.e. the key)
sub getPersistentOption
{
my ($self, $moduleName, $key) = @_;
my $persistent_opts = $self->{persistent_options};
# We must check at each level of indirection to avoid
# "autovivification"
return unless exists $persistent_opts->{$moduleName};
return unless exists $persistent_opts->{$moduleName}{$key};
return $persistent_opts->{$moduleName}{$key};
}
# Sets a "persistent" option which will be read in for a module when
# kdesrc-build starts up and written back out at (normal) program exit.
#
# First parameter is the module name to set the option for, or 'global'.
# Second parameter is the name of the value to set (i.e. key)
# Third parameter is the value to store, which must be a scalar.
sub setPersistentOption
{
my ($self, $moduleName, $key, $value) = @_;
my $persistent_opts = $self->{persistent_options};
# Initialize empty hash ref if nothing defined for this module.
$persistent_opts->{$moduleName} //= { };
$persistent_opts->{$moduleName}{$key} = $value;
}
1;
}
# }}}
@ -1516,6 +1634,29 @@ EOF
}
}
# Gets persistent options set for this module. First parameter is the name
# of the option to lookup. Undef is returned if the option is not set,
# although even if the option is set, the value returned might be empty.
# Note that ksb::BuildContext also has this function, with a slightly
# different signature, which OVERRIDEs this function since Perl does not
# have parameter-based method overloading.
sub getPersistentOption
{
my ($self, $key) = @_;
return $self->buildContext()->getPersistentOption($self->name(), $key);
}
# Sets a persistent option (i.e. survives between processes) for this module.
# First parameter is the name of the persistent option.
# Second parameter is its actual value.
# See the warning for getPersistentOption above, it also applies for this
# method vs. ksb::BuildContext::setPersistentOption
sub setPersistentOption
{
my ($self, $key, $value) = @_;
return $self->buildContext()->setPersistentOption($self->name(), $key, $value);
}
# Clones the options from the given Module (as handled by
# hasOption/setOption/getOption). Options on this module will then be able
# to be set independently from the other module.
@ -2308,7 +2449,7 @@ sub git_clone_module
my $result = log_command($module, 'git-clone', ['git', 'clone', @args]);
if ($result == 0) {
set_persistent_option($module->name(), 'git-cloned-repository', $git_repo);
$module->setPersistentOption( 'git-cloned-repository', $git_repo);
my $branch = get_git_branch($module);
@ -2596,7 +2737,7 @@ sub git_update_module
{
my $module = assert_isa(shift, 'Module');
my $srcdir = $module->fullpath('source');
my $old_repo = get_persistent_option($module->name(), 'git-cloned-repository');
my $old_repo = $module->getPersistentOption( 'git-cloned-repository');
my $cur_repo = $module->getOption('repository');
my $branch = get_git_branch($module);
my $remoteName = GIT_REMOTE_ALIAS;
@ -2637,7 +2778,7 @@ sub git_update_module
note " y[b[*]\tAttempting to perform the switch";
# Update what we think is the current repository on-disk.
set_persistent_option($module->name(), 'git-cloned-repository', $cur_repo);
$module->setPersistentOption( 'git-cloned-repository', $cur_repo);
}
# Download updated objects
@ -3994,7 +4135,7 @@ sub output_failed_module_lists
# See if any modules fail continuously and warn specifically for them.
my @super_fail = grep {
(get_persistent_option($_->name(), 'failure-count') // 0) > 3
($_->getPersistentOption( 'failure-count') // 0) > 3
} (@{$ctx->moduleList()});
if (@super_fail)
@ -4067,127 +4208,6 @@ sub handle_set_env
return 1;
}
# Sets a "persistent" option which will be read in for a module when kdesrc-build starts
# up and written back out at (normal) program exit.
#
# First parameter is the module to set the option for, or 'global'.
# Second parameter is the name of the value to set (i.e. key)
# Third parameter is the value to store, which must be a scalar.
sub set_persistent_option
{
my ($module, $key, $value) = @_;
# A reference to a hash is used in the normal package_opts hash table with the
# special key persistent-options.
if (not exists $package_opts{$module}{'persistent-options'})
{
$package_opts{$module}{'persistent-options'} = { };
}
$package_opts{$module}{'persistent-options'}{$key} = $value;
}
# Returns the value of a "persistent" option (normally read in as part of startup), or
# undef if there is no value stored.
#
# First parameter is the module to get the option for, or 'global' if not for a module.
# Note that unlike setOption/getOption, no inheritance is done at this point so if
# an option is present globally but not for a module you must check both if that's what
# you want.
# Second parameter is the name of the value to retrieve (i.e. the key)
# A scalar is always the return value.
sub get_persistent_option
{
my ($module, $key) = @_;
# Don't auto-vivify the value if it's not there.
return undef unless exists $package_opts{$module}{'persistent-options'};
return undef unless exists $package_opts{$module}{'persistent-options'}{$key};
return $package_opts{$module}{'persistent-options'}{$key};
}
# Writes out the persistent options to the file .kdesrc-build-data. Note: If
# the file already exists, it is only overwritten if the file contains
# "AUTOGENERATED BY kdesrc-build" in the first line in case someone actually
# used this file name before this feature was added.
#
# The directory used is the same directory that contains the rc file in use.
sub write_persistent_options
{
my $ctx = assert_isa(shift, 'ksb::BuildContext');
return if pretending;
my $rcfile = $ctx->rcFile();
my $dir = dirname($rcfile ? $rcfile : "");
my $fh = IO::File->new("> $dir/.kdesrc-build-data");
if (not defined $fh)
{
error "Unable to save persistent module data: b[r[$!]";
return;
}
print $fh "# AUTOGENERATED BY kdesrc-build $versionNum\n";
# Modules with persistent options
my @names = grep { exists $package_opts{$_}{'persistent-options'} } keys %package_opts;
# References to the persistent hash tables.
my @refs = map { $package_opts{$_}{'persistent-options'} } @names;
my %output;
# This is a hash slice, it basically does $output{$names[i]} = $refs[i] for
# all entries in the lists.
@output{@names} = @refs;
$Data::Dumper::Indent = 1;
print $fh Data::Dumper->Dump([\%output], ["persistent_options"]);
}
# Reads in all persistent options from the file where they are kept
# (.kdesrc-build-data) for use in the program.
#
# The directory used is the same directory that contains the rc file in use.
sub read_persistent_options
{
my $ctx = assert_isa(shift, 'ksb::BuildContext');
my $rcfile = $ctx->rcFile();
my $dir = dirname($rcfile ? $rcfile : "");
my $fh = IO::File->new("<$dir/.kdesrc-build-data");
# Don't penalize user for name change.
$fh = IO::File->new("<$dir/.kdesvn-build-data") unless defined $fh;
return unless defined $fh;
my $persistent_data;
{
local $/ = undef; # Read in whole file with <> operator.
$persistent_data = <$fh>;
}
# $persistent_data should be Perl code which, when evaluated will give us
# a hash called persistent-options which we can then merge into our
# package_opts.
my $persistent_options;
# eval must appear after declaration of $persistent_options
eval $persistent_data;
if ($@)
{
# Failed.
error "Failed to read persistent module data: r[b[$@]";
return;
}
for my $key (keys %{$persistent_options})
{
$package_opts{$key}{'persistent-options'} = ${$persistent_options}{$key};
}
}
# Returns an array of lines output from a program. Use this only if you
# expect that the output will be short.
#
@ -4929,7 +4949,7 @@ sub dont_build
if ($module->getOption('#conflict-found'))
{
# Record now for posterity
set_persistent_option($module->name(), "conflicts-present", 1);
$module->setPersistentOption( "conflicts-present", 1);
}
}
@ -5260,7 +5280,7 @@ EOF
# We use a special script for qt-copy to auto-accept the license, it is created
# just before running it (see below).
my $builddir = $module->fullpath('build');
my $old_flags = get_persistent_option($module->name(), 'last-configure-flags') || '';
my $old_flags = $module->getPersistentOption( 'last-configure-flags') || '';
unshift @commands, $script;
@ -5273,7 +5293,7 @@ EOF
info "\tRunning g[configure]...";
set_persistent_option($module->name(), 'last-configure-flags', get_list_digest(@commands));
$module->setPersistentOption( 'last-configure-flags', get_list_digest(@commands));
return log_command($module, "configure", \@commands);
}
@ -5345,7 +5365,7 @@ sub safe_run_cmake
unshift @commands, 'cmake', $srcdir; # Add to beginning of list.
my $old_options =
get_persistent_option($module->name(), 'last-cmake-options') || '';
$module->getPersistentOption( 'last-cmake-options') || '';
my $builddir = $module->fullpath('build');
if (($old_options ne get_list_digest(@commands)) ||
@ -5359,7 +5379,7 @@ sub safe_run_cmake
safe_unlink "$srcdir/CMakeCache.txt" if -e "$srcdir/CMakeCache.txt";
safe_unlink "$builddir/CMakeCache.txt" if -e "$builddir/CMakeCache.txt";
set_persistent_option($module->name(), 'last-cmake-options', get_list_digest(@commands));
$module->setPersistentOption( 'last-cmake-options', get_list_digest(@commands));
return log_command($module, "cmake", \@commands);
}
@ -6381,14 +6401,13 @@ EOF
# Increment failed count to track when to start bugging the
# user to fix stuff.
my $fail_count = get_persistent_option($buffer, 'failure-count');
$fail_count = 0 unless defined $fail_count;
my $fail_count = $module->getPersistentOption('failure-count') // 0;
++$fail_count;
set_persistent_option($buffer, 'failure-count', $fail_count);
$module->setPersistentOption( 'failure-count', $fail_count);
if ($ipcType == IPC::MODULE_CONFLICT)
{
set_persistent_option($buffer, 'conflicts-present', 1);
$module->setPersistentOption( 'conflicts-present', 1);
}
}
elsif ($ipcType == IPC::MODULE_UPTODATE)
@ -6429,7 +6448,7 @@ EOF
# if we didn't successfully build last time.
if (!$module->getOption('build-when-unchanged') &&
$svn_status{$moduleName} eq 'skipped' &&
get_persistent_option($moduleName, 'failure-count' ) == 0)
($module->getPersistentOption('failure-count') // 0 ) == 0)
{
note "\tSkipping g[$module], its source code has not changed.";
$i++;
@ -6440,8 +6459,8 @@ EOF
{
my $elapsed = prettify_seconds(time - $start_time);
print STATUS_FILE "$module: Succeeded after $elapsed.\n" if $outfile;
set_persistent_option($moduleName, 'last-build-rev', current_module_revision($module));
set_persistent_option($moduleName, 'failure-count', 0);
$module->setPersistentOption( 'last-build-rev', current_module_revision($module));
$module->setPersistentOption( 'failure-count', 0);
info "\tOverall time for g[$module] was g[$elapsed].";
push @build_done, $moduleName;
@ -6458,10 +6477,9 @@ EOF
# Increment failed count to track when to start bugging the
# user to fix stuff.
my $fail_count = get_persistent_option($moduleName, 'failure-count');
$fail_count = 0 unless defined $fail_count;
my $fail_count = $module->getPersistentOption('failure-count') // 0;
++$fail_count;
set_persistent_option($moduleName, 'failure-count', $fail_count);
$module->setPersistentOption( 'failure-count', $fail_count);
if ($module->getOption('stop-on-failure'))
{
@ -6526,7 +6544,7 @@ sub finish
my $logdir = get_log_dir($ctx);
$exitcode = 0 unless $exitcode;
write_persistent_options($ctx );
$ctx->storePersistentOptions( );
exit $exitcode if pretending; # Abort early when pretending.
@ -6611,7 +6629,7 @@ sub handle_install
# We can optionally uninstall prior to installing
# to weed out old unused files.
if ($module->getOption('use-clean-install') &&
get_persistent_option ($moduleName, 'last-install-rev') &&
$module->getPersistentOption( 'last-install-rev') &&
safe_make ($module, 'uninstall'))
{
warning "\tUnable to uninstall r[$module] before installing the new build.";
@ -6642,7 +6660,7 @@ sub handle_install
next if $result != 0; # Don't delete anything if the build failed.
set_persistent_option($moduleName, 'last-install-rev', current_module_revision($module));
$module->setPersistentOption( 'last-install-rev', current_module_revision($module));
my $remove_setting = $module->getOption('remove-after-install');
@ -7230,9 +7248,7 @@ eval
my $time = localtime;
info "Script started processing at g[$time]" unless pretending;
# Read in persistent data for i.e. tracking the revision of the last
# successful svn update or build.
read_persistent_options($ctx);
$ctx->loadPersistentOptions();
my $result;
my @update_list = map { $_->name() } ($ctx->modulesInPhase('update'));