diff --git a/kdesrc-build b/kdesrc-build index 1d7808f..1fea1c9 100755 --- a/kdesrc-build +++ b/kdesrc-build @@ -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'));