From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from lists.gentoo.org (pigeon.gentoo.org [208.92.234.80]) by finch.gentoo.org (Postfix) with ESMTP id 003591387C2 for ; Fri, 1 Feb 2013 10:49:51 +0000 (UTC) Received: from pigeon.gentoo.org (localhost [127.0.0.1]) by pigeon.gentoo.org (Postfix) with SMTP id 7754821C029; Fri, 1 Feb 2013 10:49:51 +0000 (UTC) Received: from smtp.gentoo.org (smtp.gentoo.org [140.211.166.183]) (using TLSv1 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by pigeon.gentoo.org (Postfix) with ESMTPS id B97A621C031 for ; Fri, 1 Feb 2013 10:49:50 +0000 (UTC) Received: from hornbill.gentoo.org (hornbill.gentoo.org [94.100.119.163]) (using TLSv1 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by smtp.gentoo.org (Postfix) with ESMTPS id 95AFA33DCA5 for ; Fri, 1 Feb 2013 10:49:49 +0000 (UTC) Received: from localhost.localdomain (localhost [127.0.0.1]) by hornbill.gentoo.org (Postfix) with ESMTP id 2CD77E4073 for ; Fri, 1 Feb 2013 10:49:48 +0000 (UTC) From: "Sven Eden" To: gentoo-commits@lists.gentoo.org Content-Transfer-Encoding: 8bit Content-type: text/plain; charset=UTF-8 Reply-To: gentoo-dev@lists.gentoo.org, "Sven Eden" Message-ID: <1359300671.87db82c8424435b99effd98a4a12f39c8b6796d8.yamakuzure@gentoo> Subject: [gentoo-commits] proj/ufed:master commit in: / X-VCS-Repository: proj/ufed X-VCS-Files: Portage.pm ufed.pl.in X-VCS-Directories: / X-VCS-Committer: yamakuzure X-VCS-Committer-Name: Sven Eden X-VCS-Revision: 87db82c8424435b99effd98a4a12f39c8b6796d8 X-VCS-Branch: master Date: Fri, 1 Feb 2013 10:49:48 +0000 (UTC) Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-Id: Gentoo Linux mail X-BeenThere: gentoo-commits@lists.gentoo.org X-Archives-Salt: d2071717-6ef3-4958-b179-a5a635b3e368 X-Archives-Hash: c7c375f927a723782c137881b94e2f47 commit: 87db82c8424435b99effd98a4a12f39c8b6796d8 Author: Sven Eden gmx de> AuthorDate: Sun Jan 27 15:31:11 2013 +0000 Commit: Sven Eden gmx de> CommitDate: Sun Jan 27 15:31:11 2013 +0000 URL: http://git.overlays.gentoo.org/gitweb/?p=proj/ufed.git;a=commit;h=87db82c8 Merged the functionality to build the global use flag hash into Portage.pm. Portage.pm has been reorganized and is a "real" modules now as described in perlmod and perlmodlib. The data gathering is now done in INIT, thus ufed never executes unless the data gathering was successful. Further the data layout has been rewritten to allow to gather more information about packages and use flag states. This data is not fully processed, as the internal data handling of the ncurses interface has to be rewritten to handle this data. These changes were neccessary to allow the addition of more filters and to allow the addition of a per-package view, triggered by command line arguments. As this is much like a rewrite, this commit will get me either a big clap on the back or I'll be tarred and feathered soon... --- Portage.pm | 778 ++++++++++++++++++++++++++++++++++++++++++++++-------------- ufed.pl.in | 229 +++++++++++-------- 2 files changed, 731 insertions(+), 276 deletions(-) diff --git a/Portage.pm b/Portage.pm index 92f5495..8b91e4b 100644 --- a/Portage.pm +++ b/Portage.pm @@ -7,69 +7,213 @@ package Portage; use strict; use warnings; -my %environment; -$environment{$_}={} for qw(USE); # INCREMENTALS, except we only need USE - -our @portagedirs; -our %packages; -our @profiles; -our %use_masked_flags; -our %make_defaults_flags; -our %default_flags; -our %make_conf_flags; -our %archs; -our %all_flags; -our $eprefix; - -sub get_eprefix; -sub have_package; -sub merge; -sub merge_env; -sub noncomments; -sub norm_path; -sub read_archs; -sub read_make_conf; -sub read_make_defaults; -sub read_make_globals; -sub read_packages; -sub read_profiles; -sub read_sh; -sub read_use_mask; - -get_eprefix; -read_packages; -read_profiles; -read_use_mask; -read_make_globals; -read_make_defaults; -read_make_conf; -read_archs; - -my $lastorder; -for(reverse split /:/, $environment{USE_ORDER} || "env:pkg:conf:defaults:pkginternal:env.d") { - if($_ eq 'defaults') { - merge(\%default_flags, \%make_defaults_flags); - merge(\%all_flags, \%make_defaults_flags); - } elsif($_ eq 'conf') { - merge(\%all_flags, \%make_conf_flags); +BEGIN { + use Exporter (); + + our @ISA = qw(Exporter); + our %EXPORT_TAGS = (); + our @EXPORT_OK = (); +} + + +# --- public members --- + +# $use_flags - hashref that represents the combined and +# consolidated data about all valid use flags +# Layout of $use_flags->{flag_name}: +# {count} = number of different description lines +# Note: +1 for the list of affected packages, and +1 for each descriptionless package with settings differing from global. +# {global} = hashref for the global paramters if the flag has a description in use.desc, otherwise undefined +# ->{affected} = List of packages that are affected but have no own description +# ->{conf} = The flag is disabled (-1), enabled (1) or not set (0) in make.conf +# ->{default} = The flag is disabled (-1), enabled (1) or not set (0) by default +# ->{descr} = Global description +# ->{forced} = The flag is globally force enabled (and masked) (0,1) +# ->{installed} = At least one affected package is installed (0,1) +# ->{masked} = The flag is globally masked (0,1) +# Note: When a flag is forced, {masked} is set to one, but can be reset to 0 by any later use.mask file. +# {"local"}->{package} = hashref for per package settings +# ->{descr} = Description from use.local.desc or empty if there is no individual description +# Note: Packages without description are only listed here if their settings differ from the global +# ->{forced} = The flag is explicitly unforced (-1), default (0) or explicitly force enabled (1) for this package +# ->{installed} = This package is installed +# ->{masked} = The flag is explicitly unmasked (-1), default (0) or explicitly masked (1) for this package +# ->{package} = The flag is explicitly disabled (-1), default (0) or explicitly enabled (1) for this package. +# Note: This is a combination of the ebuilds IUSE and the installation PKGUSE and only set for installed packages. +our $use_flags; + +# $used_make_conf - path of the used make.conf +our $used_make_conf = ""; + +# --- private members --- +my %_environment = (); +my $_EPREFIX = ""; +my @_profiles = (); +# $_use_temp - hashref that represents the current state of +# all known flags. This is for data gathering, the public +# $use_flags is generated out of this by _gen_use_flags() +# Layout of $_use_temp->{flag_name}: +# {global} = conf hash for global settings +# {"local"}-> {package} = conf hash for per package settings +# global and per package settings: +# ->{conf} Is either disabled, left alone or enabled by make.conf (-1, 0, 1) +# ->{default} Is either disabled, left alone or enabled by make.defaults (-1, 0, 1) +# ->{descr} Description from use.desc ({global}) or use.local.desc {cat/pkg} (string) +# ->{forced} Is force enabled (implies {masked}=1) in any *use.force +# For packages this is only set to -1 (explicitly unforced) or +1 (explicitly forced). 0 means "left alone". +# ->{installed} Has one installed package ({global}) or is installed {cat/pkg} (0,1) +# ->{masked} Is masked by any *use.mask (0,1) +# For packages this is only set to -1 (explicitly unmasked) or +1 (explicitly masked). 0 means "left alone". +# ->{package} Is either disabled, left alone or enabled by its ebuild (IUSE) or by user package.use (PKGUSE) (-1, 0, 1) + +my $_use_temp = undef; +my $_use_template = { + conf => 0, + "default" => 0, + descr => "", + forced => 0, + installed => 0, + masked => 0, + "package" => 0 +}; + +# --- public methods --- + +# --- private methods --- +sub _add_flag; +sub _add_temp; +sub _determine_eprefix; +sub _determine_make_conf; +sub _determine_profiles; +sub _final_cleaning; +sub _gen_use_flags; +sub _merge; +sub _merge_env; +sub _noncomments; +sub _norm_path; +sub _read_archs; +sub _read_descriptions; +sub _read_make_conf; +sub _read_make_defaults; +sub _read_make_globals; +sub _read_packages; +sub _read_sh; +sub _read_use_force; +sub _read_use_mask; + +# --- Package initialization --- +INIT { + $_environment{$_} = {} for qw{USE}; + _determine_eprefix; + _determine_make_conf; + _determine_profiles; + _read_make_globals; + _read_make_defaults; + _read_make_conf; + + # Now with the defaults loaded, a check is in order + # whether the set USE_ORDER is supported: + defined($_environment{USE_ORDER}) + or die("Unable to determine USE_ORDER!\nSomething is seriously broken!\n"); + my $lastorder = ""; + my @use_order = reverse split /:/, $_environment{USE_ORDER}; + for my $order(@use_order) { + if($order eq 'defaults') { + _read_make_defaults + } elsif($order eq 'conf') { + _read_make_conf + } else { + next; + } + $lastorder = $order; + } + $lastorder eq 'conf' + or die("Sorry, USE_ORDER without make.conf overriding global" + . " USE flags are not currently supported by ufed.\n"); + + _read_packages; + _read_use_force; ## Must be before _read_use_mask to not + _read_use_mask; ## unintentionally unmask explicitly masked flags. + _read_archs; + _read_descriptions; + _final_cleaning; + _gen_use_flags; +} + +# --- public methods implementations --- + + +# --- private methods implementations --- + +# Add a flag to $use_flags and intialize it with the given +# description hash key. +# Parameter 1: flag +# Parameter 2: Keyword "global" or the package name +# Parameter 3: description hash key +sub _add_flag +{ + my ($flag, $pkg, $descKey) = @_; + + if ($descKey =~ /^\[(.*)\](-?\d+):(-?\d+):(-?\d+):(-?\d+):(-?\d+):(-?\d+)$/ ) { + my ($descr, $conf, $default, $forced, $installed, $masked, $package) + = ($1, $2, $3, $4, $5, $6, $7); + my %data = (); + + $data{descr} = $descr; + $data{forced} = $forced; + $data{installed} = $installed; + $data{masked} = $masked; + $data{"package"} = $package; + if ("global" eq "$pkg") { + $data{affected} = []; + $data{conf} = $conf; + $data{"default"} = $default; + %{$use_flags->{$flag}{global}} = %data; + } else { + %{$use_flags->{$flag}{"local"}{$pkg}} = %data; + } + ++$use_flags->{$flag}{count}; + + } else { - next; + die ("\n\"$flag\" ($pkg) Description Hash Key\n \"$descKey\"\nis illegal!\n"); } - $lastorder = $_; + + return; } -if($lastorder ne 'conf') { - die "Sorry, USE_ORDER without make.conf overriding global USE flags are not currently supported by ufed.\n"; + + +# Add a flag to $_use_temp if it does not exist +# Parameter 1: flag +# Parameter 2: Keyword "global" or "category/package" +sub _add_temp +{ + my ($flag, $pkg) = @_; + my $isAdded = 0; + + (defined($flag) && length($flag)) + or return; + + if ("global" eq $pkg) { + defined ($_use_temp->{$flag}{global}) + or %{$_use_temp->{$flag}{global}} = %$_use_template; + } else { + defined ($_use_temp->{$flag}{"local"}{$pkg}) + or %{$_use_temp->{$flag}{"local"}{$pkg}} = %$_use_template; + } + + return; } # Determine the value for EPREFIX and save it -# in $eprefix. This is done using 'portageq'. +# in $_EPREFIX. This is done using 'portageq'. # Other output from portageq is printed on # STDERR. # No parameters accepted. -sub get_eprefix { +sub _determine_eprefix { my $tmp = "/tmp/ufed_$$.tmp"; - $eprefix = qx{portageq envvar EPREFIX 2>$tmp}; + $_EPREFIX = qx{portageq envvar EPREFIX 2>$tmp}; die "Couldn't determine EPREFIX from Portage" if $? != 0; if ( -s $tmp ) { @@ -80,24 +224,163 @@ sub get_eprefix { } -e $tmp and unlink $tmp; - chomp($eprefix); + chomp($_EPREFIX); + return; +} + + +# determine which make.conf to use +# and save the result in $used_make_conf +sub _determine_make_conf +{ + $used_make_conf = "${_EPREFIX}/etc/portage/make.conf"; + (! -r $used_make_conf) + and $used_make_conf = "${_EPREFIX}/etc/make.conf"; + return; +} + + +# read /etc/make.profile and /etc/portage/make.profile and +# analyze the complete profile tree using the found parent +# files. Add all found paths to @profiles. +# No parameters accepted. +sub _determine_profiles +{ + my $mp = readlink "${_EPREFIX}/etc/portage/make.profile"; + defined($mp) + or $mp = readlink "${_EPREFIX}/etc/make.profile"; + + # make.profile is mandatory and must be a link + defined($mp) + or die "${_EPREFIX}/etc\{,/portage\}/make.profile is not a symlink\n"; + + # Start with the linked path, it is the deepest profile child. + @_profiles = _norm_path('/etc', $mp); + for (my $i = -1; $i >= -@_profiles; $i--) { + for(_noncomments("${_profiles[$i]}/parent")) { + splice(@_profiles, $i, 0, _norm_path(${_profiles[$i]}, $_)); + } + } + return; +} + + +# This method does a final cleanup of $_use_temp +# Everything that is to be done _after_ all +# configs are parsed goes in here. +# No parameters accepted +sub _final_cleaning +{ + # The "disable all" flag is truncated to '*' by the parsing, but it + # has to read '-*'. + _add_temp("-*", "global"); + + $_use_temp->{'-*'}{global}{descr} = "{Never enable any flags other than those specified in make.conf}"; + $_use_temp->{'-*'}{global}{conf} = 0; ## Can never be -1 + + # Set it from the truncated config: + if (defined($_use_temp->{'*'}{global})) { + $_use_temp->{'*'}{global}{conf} > 0 + and $_use_temp->{'-*'}{global}{conf} = 1; + } + + # The following use flags are dangerous and must no be + # available using ufed: + for my $flag ("*", "boostrap", "build") { + defined($_use_temp->{"$flag"}) and delete($_use_temp->{"$flag"}); + } + return; } -# returns the content of %packages for the given -# scalar or undef. -# Parameter 1: Package to test of the form / -sub have_package { - my ($cp) = @_; - return $packages{$cp}; +# Once $_use_temp is ready, this method builds +# the final $use_flags hashref. +# No parameters accepted +sub _gen_use_flags +{ + for my $flag (keys %$_use_temp) { + my %descCons = (); + my $flagRef = $_use_temp->{$flag}; ## Shortcut + my $hasGlobal= (defined($flagRef->{global}) && length($flagRef->{global}{descr})) ? 1 : 0; + my $lCount = $hasGlobal; + my $gDesc = ""; + my $gKey = ""; + my $gRef = $flagRef->{global}; + my $pDesc = ""; + my $pKey = ""; + my $pRef = undef; + + # Build the description consolidation hash + if ($hasGlobal) { + $gDesc = $gRef->{descr}; + $gKey = sprintf("[%s]%d:%d:%d:%d:%d:%d", $gDesc, $gRef->{conf}, $gRef->{"default"}, + $gRef->{forced}, $gRef->{installed}, $gRef->{masked}, $gRef->{"package"}); + $descCons{$gKey}{global} = 1; + } + for my $pkg (sort keys %{$flagRef->{"local"}}) { + $pRef = $flagRef->{"local"}{$pkg}; + + if (length($pRef->{descr})) { + ## This package has an individual description: + $pDesc = "$pRef->{descr}"; + $pKey = sprintf("[%s]%d:%d:%d:%d:%d:%d", $pDesc, $pRef->{conf}, $pRef->{"default"}, + $pRef->{forced}, $pRef->{installed}, $pRef->{masked}, $pRef->{"package"}); + $descCons{$pKey}{$pkg} = 1; + ++$lCount; + } + ## TODO : Add affected packages that have no own description + # once the interface can handle them. These can be used for + # the package filtering per command line arguments later. + } ## End of walking through a flags package list + + # Skip if there was nothing consolidated + next unless($lCount); + + # Add the content of $descCons to $use_flags: + $use_flags->{$flag}{count} = 0; + + # The global data has to be added first: + if (length($gKey)) { + _add_flag($flag, "global", $gKey); + + # TODO : Add affected packages once they can be hanlded. See above TODO. + } + + # Then the "local" flag descriptions + # TODO : Add affected packages that have diffeent settings once they can be handled. + for my $key (sort keys %descCons) { + next if ($key eq $gKey); + + # Generate the package list with same description and flags, + # but not for more than 5 packages + my $packages = ""; + my $pkgCount = 0; + for my $pkg (sort keys %{$descCons{$key}}) { + length($packages) and $packages .= ", "; + $packages .= $pkg; + if (++$pkgCount == 5) { + _add_flag($flag, $packages, $key); + $packages = ""; + $pkgCount = 0; + } + } + + # Add the last result if there is something left + $pkgCount and _add_flag($flag, $packages, $key); + } + + delete $_use_temp->{$flag}; + } ## End of walking through $_use_temp flags + + return; } # merges two hashes into the first. # Parameter 1: reference of the destination hash # Parameter 2: reference of the source hash -sub merge { +sub _merge { my ($dst, $src) = @_; %{$dst} = () if(exists $src->{'*'}); $dst->{$_} = $src->{$_} for(keys %$src); @@ -106,12 +389,12 @@ sub merge { # Splits content of the source hash at spaces and -# merges its contents into %environment. +# merges its contents into %_environment. # Parameter 1: reference of the hash to merge -sub merge_env { +sub _merge_env { my ($src) = @_; - for(keys %environment) { - if(ref $environment{$_} eq 'HASH') { + for(keys %_environment) { + if(ref $_environment{$_} eq 'HASH') { if(exists $src->{$_}) { my %split; for(split ' ', $src->{$_}) { @@ -120,13 +403,13 @@ sub merge_env { $split{$_} = !$off; } $src->{$_} = { %split }; - merge(\%{$environment{$_}}, \%{$src->{$_}}); + _merge(\%{$_environment{$_}}, \%{$src->{$_}}); } } } for(keys %$src) { - if(ref $environment{$_} ne 'HASH') { - $environment{$_} = $src->{$_}; + if(ref $_environment{$_} ne 'HASH') { + $_environment{$_} = $src->{$_}; } } return; @@ -136,7 +419,7 @@ sub merge_env { # returns a list of all lines of a given file # that are no pure comments # Parameter 1: filename -sub noncomments { +sub _noncomments { my ($fname) = @_; my @result; local $/; @@ -154,7 +437,7 @@ sub noncomments { # Parameter 1: base path # Parameter 2: sub path # return: normalized base path / normalized sub path -sub norm_path { +sub _norm_path { my ($base, $path) = @_; my @pathcomp = ($path !~ m!^/! && split(m!/!, $base), split(m!/!, $path)); for(my $i=0;;$i++) { @@ -176,118 +459,217 @@ sub norm_path { } -# reads all arch.list in all profiles sub directories -# of found @portagedirs and saves the found architectures -# in %arch +# reads all found arch.list and erase all found archs +# from $_use_temp. Archs are not setable. # No parameters accepted -sub read_archs { - for my $dir(@portagedirs) { - for(noncomments "$dir/profiles/arch.list") { - $archs{$_} = 1; +sub _read_archs { + for my $dir(@_profiles) { + next unless (-r "$dir/arch.list"); + for my $arch (_noncomments("$dir/arch.list")) { + defined($_use_temp->{$arch}) + and delete($_use_temp->{$arch}); } } return; } -# read /etc/make.conf and/or /etc/portage/make.conf and -# merge set USE flags into %make_conf_flags. Additionally -# all set portage directories (plus overlays) are recorded -# in @portagedirs. -# No parameters accepted. -sub read_make_conf { - my %oldEnv = read_sh("$eprefix/etc/make.conf"); - my %newEnv = read_sh("$eprefix/etc/portage/make.conf"); - merge (\%oldEnv, \%newEnv); - merge (\%make_conf_flags,\ %{$oldEnv{USE}}) if exists $oldEnv{USE}; - @portagedirs = $environment{PORTDIR}; - push @portagedirs, split ' ', $environment{PORTDIR_OVERLAY} if defined $environment{PORTDIR_OVERLAY}; +# reads all use.desc and use.local.desc and updates +# $_use_temp accordingly. +# No parameters accepted +sub _read_descriptions +{ + for my $dir(@_profiles) { + if (-r "$dir/use.desc") { + for(_noncomments("$dir/use.desc")) { + my ($flag, $desc) = /^(.*?)\s+-\s+(.*)$/ or next; + + _add_temp($flag, "global"); + + $_use_temp->{$flag}{global}{descr} = $desc; + } + } ## End of having a use.desc file + + if (-r "$dir/use.local.desc") { + for(_noncomments("$dir/use.local.desc")) { + my ($pkg, $flag, $desc) = /^(.*?):(.*?)\s+-\s+(.*)$/ or next; + + # Here we do not explicitly add a {global} part, + # some flags are local only. + _add_temp($flag, $pkg); + + $_use_temp->{$flag}{"local"}{$pkg}{descr} = $desc; + } + } ## End of having a use.local.desc file + } ## End of looping the profiles return; } -# read all found make.defaults files and merge their flags -# into %make_default_flags. +# read make.conf and record the state of all set use +# flags. +# Additionally add all set portage directories (plus +# overlays) to @_profiles. +# The last added profile directory, if it exists, is +# /etc/portage/profile to allow recognition of user +# overrides. # No parameters accepted. -sub read_make_defaults { - for my $dir(@profiles) { - my %env = read_sh "$dir/make.defaults"; - merge (\%make_defaults_flags, \%{$env{USE}}) if exists $env{USE}; +sub _read_make_conf { + my %oldEnv = _read_sh("${_EPREFIX}/etc/make.conf"); + my %newEnv = _read_sh("${_EPREFIX}/etc/portage/make.conf"); + _merge (\%oldEnv, \%newEnv); + + # Note the conf state of the read flags: + for my $flag ( keys %{$oldEnv{USE}}) { + + _add_temp($flag, "global"); + + $oldEnv{USE}{$flag} + and $_use_temp->{$flag}{global}{conf} = 1 + or $_use_temp->{$flag}{global}{conf} = -1; } + + # Add PORTDIR and overlays to @_profiles + defined ($_environment{PORTDIR}) + and push @_profiles, "$_environment{PORTDIR}/profiles" + or die("Unable to determine PORTDIR!\nSomething is seriously broken here!\n"); + defined ($_environment{PORTDIR_OVERLAY}) + and push @_profiles, + map { my $x=$_; $x =~ s/^\s*(\S+)\s*$/$1\/profiles/mg ; $x } + split('\n', $_environment{PORTDIR_OVERLAY}); + -e "${_EPREFIX}/etc/portage/profile" + and push @_profiles, "${_EPREFIX}/etc/portage/profile"; + return; +} + + +# read all found make.defaults and non-user package.use files +# and merge their values into env. +# TODO : use USE_EXPAND to add Expansion parsing. The most +# important of these are set with sane defaults here, +# too. +# No parameters accepted. +sub _read_make_defaults { + for my $dir(@_profiles) { + if (-r "$dir/make.defaults") { + my %env = _read_sh("$dir/make.defaults"); + + # Note the conf state of the read flags: + for my $flag ( keys %{$env{USE}}) { + _add_temp($flag, "global"); + + $env{USE}{$flag} + and $_use_temp->{$flag}{global}{"default"} = 1 + or $_use_temp->{$flag}{global}{"default"} = -1; + } + } + } ## End of looping through the profiles return } # read all found make.globals and merge their -# settings into %environment. +# settings into %environment. This is done to +# get the final "PORTDIR" and "USE_ORDER" # No parameters accepted -sub read_make_globals { - for my $dir(@profiles, "$eprefix/usr/share/portage/config") { - read_sh "$dir/make.globals"; +sub _read_make_globals { + for my $dir(@_profiles, "${_EPREFIX}/usr/share/portage/config") { + _read_sh("$dir/make.globals"); } return; } -# Analyze EPREFIX/var/db/pkg and note all installed -# packages in %packages. +# Analyze EPREFIX/var/db/pkg and analyze all installed +# packages. We need to know what they use (IUSE) and what +# has been set when they where emerged (PKGUSE). # No parameters accepted. -sub read_packages { - die "Couldn't read $eprefix/var/db/pkg\n" unless opendir my $pkgdir, "$eprefix/var/db/pkg"; +sub _read_packages { + my $pkgdir = undef; + opendir($pkgdir, "${_EPREFIX}/var/db/pkg") + or die "Couldn't read ${_EPREFIX}/var/db/pkg\n"; + + # loop through all categories in pkgdir while(my $cat = readdir $pkgdir) { next if $cat eq '.' or $cat eq '..'; - next unless opendir my $catdir, "$eprefix/var/db/pkg/$cat"; + my $catdir = undef; + opendir($catdir, "${_EPREFIX}/var/db/pkg/$cat") + or next; + + # loop through all openable directories in cat while(my $pkg = readdir $catdir) { next if $pkg eq '.' or $pkg eq '..'; - my @provide = (); - my @use = (); + my @puse = (); + my @iuse = (); # Load PROVIDE - ## FIXME: There is no file "PROVIDE" anywhere, at least on my system! - if(open my $provide, '<', "$eprefix/var/db/pkg/$cat/$pkg/PROVIDE") { - local $/; - @provide = split ' ', <$provide>; - close $provide; - } + #Update: Deprecated, this file is gone + #if(open my $provide, '<', "$eprefix/var/db/pkg/$cat/$pkg/PROVIDE") { + # local $/; + # @provide = split ' ', <$provide>; + # close $provide; + #} # Load USE - if(open my $use, '<', "$eprefix/var/db/pkg/$cat/$pkg/USE") { + # Update: deprecated, this file is no longer useful. read IUSE and PKGUSE instead + #if(open my $use, '<', "$eprefix/var/db/pkg/$cat/$pkg/USE") { + # local $/; + # @use = split ' ', <$use>; + # close $use; + #} + + # Load IUSE to learn which use flags the package in this version knows + my $fiuse = "${_EPREFIX}/var/db/pkg/$cat/$pkg/IUSE"; + if(open my $use, '<', $fiuse) { + local $/; + @iuse = split ' ', <$use>; + close $use; + } + + # Load PKGUSE to learn which use flags have been set when this package was emerged + my $fpuse = "${_EPREFIX}/var/db/pkg/$cat/$pkg/PKGUSE"; + if(open my $use, '<', $fpuse) { local $/; - @use = split ' ', <$use>; + @puse = split ' ', <$use>; close $use; } # could be shortened, but make sure not to strip off part of the name $pkg =~ s/-\d+(?:\.\d+)*\w?(?:_(?:alpha|beta|pre|rc|p)\d*)?(?:-r\d+)?$//; - $packages{"$cat/$pkg"} = 1; + $pkg = $cat . "/" . $pkg; + + # Now save the knowledge gained (if any) in $_use_temp: + for my $flag (@iuse) { + my $eState = $flag =~ s/^\+// || 0; + my $dState = $flag =~ s/^-// || 0; + + _add_temp($flag, "global"); + _add_temp($flag, $pkg); + + $_use_temp->{$flag}{"local"}{$pkg}{"package"} = $eState ? 1 : $dState ? -1 : 0; + $_use_temp->{$flag}{"local"}{$pkg}{installed} = 1; + $_use_temp->{$flag}{global}{installed} = 1; + } ## End of looping IUSE + for my $flag (@puse) { + my $state = $flag =~ s/^-// || 0; + + if ( defined($_use_temp->{$flag}{global}) + && defined($_use_temp->{$flag}{$pkg})) { + $state and $_use_temp->{$flag}{"local"}{$pkg}{"package"} = -1 + or $_use_temp->{$flag}{"local"}{$pkg}{"package"} = 0; + + } # enable if output is wanted! + #else { + # ## This can happen if a package was installed with a flag + # ## that is gone. (Seen with sys-fs/ntfs3g-2012.1.15-r1 to + # ## sys-fs/ntfs3g-2012.1.15-r2 and use flag "extras". Gone, + # ## but still listed in PKGUSE) + # printf STDERR "DEBUG: '%s' found in\n '%s'\n but not in\n '%s'\n", + # $flag, $fpuse, $fiuse; + # ## No need to break, though... + #} + } ## End of looping PKGUSE - # FIXME: What is this supposed to achieve? - # Currently this does nothing as there is no PROVIDE anywhere, - # but even if it were, there is nothing really done at all with - # @use. - for(my $i=0; $i<@provide; $i++) { - my $pkg = $provide[$i]; - next if $pkg eq '(' || $pkg eq ')'; - if($pkg !~ s/\?$//) { - $pkg =~ s/-\d+(?:\.\d+)*\w?(?:_(?:alpha|beta|pre|rc|p)\d*)?(?:-r\d+)?$//; - $packages{$pkg} = 1; - } else { - my $musthave = $pkg !~ s/^!//; - my $have = 0; - for(@use) { - if($pkg eq $_) - { $have = 1; last } - } - if($musthave != $have) { - my $level = 0; - for($i++;$i<@provide;$i++) { - $level++ if $provide[$i] eq '('; - $level-- if $provide[$i] eq ')'; - last if $level==0; - } - } - } - } } closedir $catdir; } @@ -296,32 +678,13 @@ sub read_packages { } -# read /etc/make.profile and /etc/portage/make.profile -# and analyze the complete profile tree using the found -# parent files. Add all found paths to @profiles. -# No parameters accepted. -sub read_profiles { - $_ = readlink "$eprefix/etc/make.profile"; - $_ = readlink "$eprefix/etc/portage/make.profile" if not defined $_; - die "$eprefix/etc\{,/portage\}/make.profile is not a symlink\n" if not defined $_; - @profiles = norm_path '/etc', $_; - for (my $i = -1; $i >= -@profiles; $i--) { - for(noncomments "$profiles[$i]/parent") { - splice @profiles, $i, 0, norm_path $profiles[$i], $_; - } - } - push @profiles, "$eprefix/etc/portage/profile"; - return; -} - - # reads the given file and parses it for key=value pairs. # "source" entries are added to the file and parsed as # well. The results of the parsing are merged into # %environment. # Parameter 1: The path of the file to parse. # In a non-scalar context the function returns the found values. -sub read_sh { +sub _read_sh { my ($fname) = @_; my $BLANK = qr{(?:[ \n\t]+|#.*)+}; # whitespace and comments my $IDENT = qr{([^ \\\n\t'"{}=#]+)}; # identifiers @@ -337,7 +700,7 @@ sub read_sh { eval { for(;;) { /\G$BLANK/gc; - last if pos == length; + last if ((pos || 0) == (length || 0)); /\G$IDENT/gc or die; my $name = $1; /\G$BLANK/gc; @@ -386,33 +749,88 @@ sub read_sh { }; die "Parse error in $fname\n" if $@; } - merge_env(\%env); + _merge_env(\%env); return %env if wantarray; return; } +# read all enforced flags from all found use.force +# and package.use.force files. Save the found +# masks in %use_flags. +# No parameters accepted. +sub _read_use_force { + for my $dir(@_profiles) { + if (-r "$dir/use.force") { + # use.force can enforce and mask specific flags + for my $flag (_noncomments("$dir/use.force") ) { + my $state = $flag =~ s/^-// || 0; + + _add_temp($flag, "global"); + + $_use_temp->{$flag}{global}{masked} = !$state; + $_use_temp->{$flag}{global}{forced} = !$state; + } + } ## End of having a use.force file + + if (-r "$dir/package.use.force") { + # package.use.force can enforce or unforce flags per package + for(_noncomments("$dir/package.use.force") ) { + my($pkg, @flags) = split; + for my $flag (@flags) { + my $state = $flag =~ s/^-// || 0; + + _add_temp($flag, "global"); + _add_temp($flag, $pkg); + + if ($state) { + $_use_temp->{$flag}{"local"}{$pkg}{masked} = -1; ## explicitly unmasked and + $_use_temp->{$flag}{"local"}{$pkg}{forced} = -1; ## explicitly unforced + } else { + $_use_temp->{$flag}{"local"}{$pkg}{masked} = 1; ## explicitly masked and + $_use_temp->{$flag}{"local"}{$pkg}{forced} = 1; ## explicitly enforced + } + } + } + } ## End of having a package.use.force file + } ## End of looping through the profiles + return; +} + + # read all masked flags from all found use.mask # and package.use.mask files. Save the found -# masks in %use_masked_flags. +# masks in %use_flags. # No parameters accepted. -sub read_use_mask { - for my $dir(@profiles) { - -r "$dir/use.mask" or next; - for(noncomments "$dir/use.mask") { - my $off = s/^-//; - $use_masked_flags{$_} = { '' => !$off }; - } - for(noncomments "$dir/package.use.mask") { - my($pkg, @flags) = split; - for(@flags) { - my $off = s/^-//; - - $use_masked_flags{$_}{''} ||= 0; - $use_masked_flags{$_}{$pkg} = !$off; +sub _read_use_mask { + for my $dir(@_profiles) { + if (-r "$dir/use.mask") { + # use.mask can enable or disable masks + for my $flag (_noncomments("$dir/use.mask") ) { + my $state = $flag =~ s/^-// || 0; + + _add_temp($flag, "global"); + + $_use_temp->{$flag}{global}{masked} = !$state; } - } - } + } ## End of having a use.mask file + + if (-r "$dir/package.use.mask") { + # package.use.mask can enable or disable masks per package + for(_noncomments("$dir/package.use.mask") ) { + my($pkg, @flags) = split; + for my $flag (@flags) { + my $state = $flag =~ s/^-// || 0; + + _add_temp($flag, "global"); + _add_temp($flag, $pkg); + + $state and $_use_temp->{$flag}{"local"}{$pkg}{masked} = -1; ## explicitly unmasked + $state or $_use_temp->{$flag}{"local"}{$pkg}{masked} = 1; ## explicitly masked + } + } + } ## End of having a package.use.mask file + } ## End of looping through the profiles return; } diff --git a/ufed.pl.in b/ufed.pl.in index 9b072b7..299c79d 100644 --- a/ufed.pl.in +++ b/ufed.pl.in @@ -17,52 +17,51 @@ my $interface = 'ufed-curses'; # . " --read-var-info=yes" # . " XX_libexecdir@/ufed-curses 2>/tmp/ufed_memcheck.log"; -my %use_descriptions; +# no longer needed +# my %use_descriptions; sub finalise; sub flags_dialog; -sub read_use_descs; sub save_flags; -delete $Portage::all_flags{'*'}; - -read_use_descs; - -delete @use_descriptions{qw(bootstrap build)}; - -$Portage::make_conf_flags{'-*'} = 1 - if defined $Portage::make_conf_flags{'*'} - && !$Portage::make_conf_flags{'*'}; - -for(keys %Portage::all_flags) { - @{$use_descriptions{$_}} = "[(Unknown)] g" - if not exists $use_descriptions{$_}; -} -@{$use_descriptions{'-*'}} = '[Never enable any flags other than those specified in make.conf] g'; - -for(@Portage::archs) { - delete $Portage::default_flags{$_}; - delete $Portage::all_flags{$_}; - delete $use_descriptions{$_}; -} -for my $flag (keys %Portage::use_masked_flags) { - my $masked = 1; - for my $mask (values %{$Portage::use_masked_flags{$flag}}) { - last if not($masked &&= $mask); - } - if($masked) { - if (defined($use_descriptions{$flag})) { - for (my $i = 0; $i < scalar @{$use_descriptions{$flag}}; ++$i) { - $use_descriptions{$flag}->[$i] =~ s/ [lg]$/ m/ ; - $use_descriptions{$flag}->[$i] =~ s/ L$/ M/ ; - } - } else { - delete $use_descriptions{$flag}; - delete $Portage::default_flags{$flag}; - delete $Portage::all_flags{$flag}; - } - } -} +# deprecated, the functionality is merged into Portage.pm +# delete $Portage::all_flags{'*'}; +# read_use_descs; +# @use_descriptions{qw(bootstrap build)}; +# +#$Portage::make_conf_flags{'-*'} = 1 +# if defined $Portage::make_conf_flags{'*'} +# && !$Portage::make_conf_flags{'*'}; +# +#for(keys %Portage::all_flags) { +# @{$use_descriptions{$_}} = "[(Unknown)] g" +# if not exists $use_descriptions{$_}; +#} +#@{$use_descriptions{'-*'}} = '[Never enable any flags other than those specified in make.conf] g'; +# +#for(@Portage::archs) { +# delete $Portage::default_flags{$_}; +# delete $Portage::all_flags{$_}; +# delete $use_descriptions{$_}; +#} +#for my $flag (keys %Portage::use_masked_flags) { +# my $masked = 1; +# for my $mask (values %{$Portage::use_masked_flags{$flag}}) { +# last if not($masked &&= $mask); +# } +# if($masked) { +# if (defined($use_descriptions{$flag})) { +# for (my $i = 0; $i < scalar @{$use_descriptions{$flag}}; ++$i) { +# $use_descriptions{$flag}->[$i] =~ s/ [lg]$/ m/ ; +# $use_descriptions{$flag}->[$i] =~ s/ L$/ M/ ; +# } +# } else { +# delete $use_descriptions{$flag}; +# delete $Portage::default_flags{$flag}; +# delete $Portage::all_flags{$flag}; +# } +# } +#} flags_dialog; @@ -111,16 +110,52 @@ sub flags_dialog { my $outTxt = ""; # Write out flags - for my $flag (sort { uc $a cmp uc $b } keys %use_descriptions) { + for my $flag (sort { uc $a cmp uc $b } keys %$Portage::use_flags) { + my $conf = $Portage::use_flags->{$flag}; ## Shortcut + my $state = "g"; $outTxt .= sprintf ("%s %s (%s%s) %d\n", $flag, - defined($Portage::make_conf_flags{$flag}) - ? $Portage::make_conf_flags{$flag} ? 'on' : 'off' : 'def', - exists($Portage::make_defaults_flags{$flag}) - ? $Portage::make_defaults_flags{$flag} ? '+' : '-' : ' ', - exists($Portage::make_conf_flags{$flag}) - ? $Portage::make_conf_flags{$flag} ? '+' : '-' : ' ', - scalar @{$use_descriptions{$flag}} ); - $outTxt .= sprintf ("%s\n", $_) for(@{$use_descriptions{$flag}}); + defined($conf->{global}{conf}) ? + $conf->{global}{conf} > 0 ? 'on' : + $conf->{global}{conf} < 0 ? 'off' : 'def' : 'def', + defined($conf->{global}{"default"}) ? + $conf->{global}{"default"} > 0 ? '+' : + $conf->{global}{"default"} < 0 ? '-' : ' ' : ' ', + defined($conf->{global}{conf}) ? + $conf->{global}{conf} > 0 ? '+' : + $conf->{global}{conf} < 0 ? '-' : ' ' : ' ', + $conf->{count}); + + # Print global description first (if available) + if (defined($conf->{global}) && length($conf->{global}{descr})) { + if ($conf->{global}{installed}) { + $conf->{global}{masked} and $state = "M"; + $conf->{global}{masked} or $state = "G"; + } else { + $conf->{global}{masked} and $state = "m"; + } + $outTxt .= "[" . $conf->{global}{descr} . "] $state\n"; + + # Then print the list of affected packages that have no own entry + for my $afLst (@{$conf->{global}{affected}}) { + (defined($afLst) && length($afLst)) + and $outTxt .= "($afLst) [Affected by global flag setting] $state\n"; + } + } + + # Finally print the local description lines + for my $pkg (sort keys %{$conf->{"local"}}) { + $state = "l"; + if ($conf->{"local"}{$pkg}{installed}) { + $state = "L"; + if ($conf->{global}{masked}) { + $conf->{"local"}{$pkg}{masked} > -1 and $state = "M"; + $conf->{"local"}{$pkg}{masked} < 0 and $state = "L"; + } + } elsif ($conf->{global}{masked}) { + $conf->{"local"}{$pkg}{masked} > -1 and $state = "m"; + } + $outTxt .= sprintf("(%s) [%s] %s\n", $pkg, $conf->{"local"}{$pkg}{descr}, $state); + } } # Some overlays (like sunrise) use UTF-8 characters in their @@ -155,52 +190,54 @@ sub flags_dialog { return; } -# Build the global %use_descriptions hash. -# Parsed files are: -# 1. PORTDIR/profiles/use.desc -# 2. PORTDIR/profiles/use.local.desc -# No parameters accepted. -# @todo : The local descriptions must be written in a different -# way (how?) to allow the wanted filtering/distinction -# between global/local flags. -sub read_use_descs { - my %_use_descriptions; - for my $dir(@Portage::portagedirs) { - for(Portage::noncomments "$dir/profiles/use.desc") { - my ($flag, $desc) = /^(.*?)\s+-\s+(.*)$/ or next; - $_use_descriptions{$flag}{$desc} = 1; - } - } - my %_use_local_descriptions; - for my $dir(@Portage::portagedirs) { - for(Portage::noncomments "$dir/profiles/use.local.desc") { - my ($pkg, $flag, $desc) = /^(.*?):(.*?)\s+-\s+(.*)$/ or next; - $_use_local_descriptions{$flag}{$desc}{$pkg} = 1; - } - } - # Record all global flags first, their description is printed first - # in the ncurses interface as well. - for my $key (sort keys %_use_descriptions) { - for my $desc (sort keys %{$_use_descriptions{$key}}) { - push @{$use_descriptions{$key}}, "[" . $desc . "] g"; - } - } - - # Add local flags - for my $key (sort keys %_use_local_descriptions) { - for my $desc (sort keys %{$_use_local_descriptions{$key}}) { - my $flagPrefix = "l"; - my @pkgs = (); - for my $pkg (sort keys %{$_use_local_descriptions{$key}{$desc}}) { - $flagPrefix = "L" if (Portage::have_package($pkg)); - push @pkgs, $pkg; - } - local $"=", "; - push @{$use_descriptions{$key}}, sprintf("(%s) [%s] %s", "@pkgs", $desc, $flagPrefix); - } - } - return; -} +# Deprecated, the funcionality is merged into Portage.pm. +# +## Build the global %use_descriptions hash. +## Parsed files are: +## 1. PORTDIR/profiles/use.desc +## 2. PORTDIR/profiles/use.local.desc +## No parameters accepted. +## @todo : The local descriptions must be written in a different +## way (how?) to allow the wanted filtering/distinction +## between global/local flags. +#sub read_use_descs { +# my %_use_descriptions; +# for my $dir(@Portage::portagedirs) { +# for(Portage::noncomments "$dir/profiles/use.desc") { +# my ($flag, $desc) = /^(.*?)\s+-\s+(.*)$/ or next; +# $_use_descriptions{$flag}{$desc} = 1; +# } +# } +# my %_use_local_descriptions; +# for my $dir(@Portage::portagedirs) { +# for(Portage::noncomments "$dir/profiles/use.local.desc") { +# my ($pkg, $flag, $desc) = /^(.*?):(.*?)\s+-\s+(.*)$/ or next; +# $_use_local_descriptions{$flag}{$desc}{$pkg} = 1; +# } +# } +# # Record all global flags first, their description is printed first +# # in the ncurses interface as well. +# for my $key (sort keys %_use_descriptions) { +# for my $desc (sort keys %{$_use_descriptions{$key}}) { +# push @{$use_descriptions{$key}}, "[" . $desc . "] g"; +# } +# } +# +# # Add local flags +# for my $key (sort keys %_use_local_descriptions) { +# for my $desc (sort keys %{$_use_local_descriptions{$key}}) { +# my $flagPrefix = "l"; +# my @pkgs = (); +# for my $pkg (sort keys %{$_use_local_descriptions{$key}{$desc}}) { +# $flagPrefix = "L" if (Portage::have_package($pkg)); +# push @pkgs, $pkg; +# } +# local $"=", "; +# push @{$use_descriptions{$key}}, sprintf("(%s) [%s] %s", "@pkgs", $desc, $flagPrefix); +# } +# } +# return; +#} # Write given list of flags back to make.conf if # the file has not been changed since reading it.