From 690993b1b9b4a932ca5bf5374c59e4cf88f18732 Mon Sep 17 00:00:00 2001 From: Christian Blichmann Date: Tue, 1 Feb 2022 20:59:57 +0100 Subject: Columnar output for package lists similar to 'ls' This change makes it a bit easier to quickly grasp the changes about to be performed by apt. It displays package lists in a columnar format by default, similar to what `ls` produces for files. A new long option `--no-list-columns` and an associated `APT::Get::List-Columns` config setting control the behavior. Usage example, with 60 column wide terminal: ``` $ sudo apt upgrade | Reading package lists... Done | Building dependency tree... Done | Reading state information... Done | Calculating upgrade... Done | The following packages were automatically installed and are | no longer required: | libappindicator1 libindicator7 | libdbusmenu-gtk4 linux-image-5.14.0-4-amd64 | Use 'sudo apt autoremove' to remove them. | The following packages have been kept back: | criu linux-headers-amd64 nvidia-settings | libxnvctrl0 nvidia-modprobe xwayland | 0 upgraded, 0 newly installed, 0 to remove and 6 not upgrade| d. | ``` The effect becomes more pronounced with more packages (e.g. when doing a dist-upgrade). --- apt-private/private-cmndline.cc | 3 +- apt-private/private-output.cc | 77 +++++++++++++++++++++++++++++++++++++++++ apt-private/private-output.h | 33 ++++++++++++------ completions/bash/apt | 2 +- doc/apt-get.8.xml | 6 ++++ doc/examples/configure-index | 1 + test/integration/framework | 1 + 7 files changed, 111 insertions(+), 12 deletions(-) diff --git a/apt-private/private-cmndline.cc b/apt-private/private-cmndline.cc index 79881d034..cbab22031 100644 --- a/apt-private/private-cmndline.cc +++ b/apt-private/private-cmndline.cc @@ -184,7 +184,8 @@ static bool addArgumentsAPTGet(std::vector &Args, char const addArg(0, "show-progress", "DpkgPM::Progress", 0); addArg('f', "fix-broken", "APT::Get::Fix-Broken", 0); addArg(0, "purge", "APT::Get::Purge", 0); - addArg('V',"verbose-versions","APT::Get::Show-Versions",0); + addArg('V',"verbose-versions", "APT::Get::Show-Versions",0); + addArg(0, "list-columns", "APT::Get::List-Columns", 0); addArg(0, "autoremove", "APT::Get::AutomaticRemove", 0); addArg(0, "auto-remove", "APT::Get::AutomaticRemove", 0); addArg(0, "reinstall", "APT::Get::ReInstall", 0); diff --git a/apt-private/private-output.cc b/apt-private/private-output.cc index 93b942458..f04c35b8a 100644 --- a/apt-private/private-output.cc +++ b/apt-private/private-output.cc @@ -316,6 +316,83 @@ void ListSingleVersion(pkgCacheFile &CacheFile, pkgRecords &records, /*{{{*/ out << output; } /*}}}*/ +// ShowWithColumns - Show a list in the style of ls /*{{{*/ +// --------------------------------------------------------------------- +/* This prints out a vector of strings with the given indent and in as + many columns as will fit the screen width. + + The output looks like: + abiword debootstrap gir1.2-upowerglib-1.0 + abiword-common dh-make google-chrome-beta + abiword-plugin-grammar dmeventd gstreamer1.0-clutter-3.0 + binfmt-support dmsetup hostname + console-setup evolution-data-server iproute2 + console-setup-linux evolution-data-server-common + coreutils ffmpeg + */ +struct columnInfo +{ + bool ValidLen; + size_t LineWidth; + vector RemainingWidths; +}; +void ShowWithColumns(ostream &out, vector const &List, size_t Indent, size_t ScreenWidth) +{ + constexpr size_t MinColumnWidth = 2; + constexpr size_t ColumnSpace = 1; + + size_t const ListSize = List.size(); + size_t const MaxScreenCols = (ScreenWidth - Indent) / + MinColumnWidth; + size_t const MaxNumCols = min(MaxScreenCols, ListSize); + + vector ColumnInfo(MaxNumCols); + for (size_t I = 0; I < MaxNumCols; ++I) { + ColumnInfo[I].ValidLen = true; + ColumnInfo[I].LineWidth = (I + 1) * MinColumnWidth; + ColumnInfo[I].RemainingWidths.resize(I + 1, MinColumnWidth); + } + + for (size_t I = 0; I < ListSize; ++I) { + for (size_t J = 0; J < MaxNumCols; ++J) { + auto& Col = ColumnInfo[J]; + if (!Col.ValidLen) + continue; + + size_t Idx = I / ((ListSize + J) / (J + 1)); + size_t RealColLen = List[I].size() + (Idx == J ? 0 : ColumnSpace); + if (Col.RemainingWidths[Idx] < RealColLen) { + Col.LineWidth += RealColLen - Col.RemainingWidths[Idx]; + Col.RemainingWidths[Idx] = RealColLen; + Col.ValidLen = Col.LineWidth < ScreenWidth; + } + } + } + size_t NumCols = MaxNumCols; + while (NumCols > 1 && !ColumnInfo[NumCols - 1].ValidLen) + --NumCols; + + size_t NumRows = ListSize / NumCols + (ListSize % NumCols != 0); + auto const &LineFormat = ColumnInfo[NumCols - 1]; + for (size_t Row = 0; Row < NumRows; ++Row) { + size_t Col = 0; + size_t I = Row; + out << string(Indent, ' '); + while (true) { + out << List[I]; + + size_t CurLen = List[I].size(); + size_t MaxLen = LineFormat.RemainingWidths[Col++]; + I += NumRows; + if (I >= ListSize) + break; + + out << string(MaxLen - CurLen, ' '); + } + out << endl; + } +} + /*}}}*/ // ShowBroken - Debugging aide /*{{{*/ // --------------------------------------------------------------------- /* This prints out the names of all the packages that are broken along diff --git a/apt-private/private-output.h b/apt-private/private-output.h index c3e73d592..dcffd1880 100644 --- a/apt-private/private-output.h +++ b/apt-private/private-output.h @@ -10,6 +10,7 @@ #include #include #include +#include // forward declaration class pkgCacheFile; @@ -35,6 +36,8 @@ void ListSingleVersion(pkgCacheFile &CacheFile, pkgRecords &records, APT_PUBLIC void ShowBroken(std::ostream &out, CacheFile &Cache, bool const Now); APT_PUBLIC void ShowBroken(std::ostream &out, pkgCacheFile &Cache, bool const Now); +APT_PUBLIC void ShowWithColumns(std::ostream &out, const std::vector &List, size_t Indent, size_t ScreenWidth); + template bool ShowList(std::ostream &out, std::string const &Title, Container const &cont, PredicateC Predicate, @@ -44,7 +47,9 @@ template bool size_t const ScreenWidth = (::ScreenWidth > 3) ? ::ScreenWidth - 3 : 0; int ScreenUsed = 0; bool const ShowVersions = _config->FindB("APT::Get::Show-Versions", false); + bool const ListColumns = _config->FindB("APT::Get::List-Columns", true); bool printedTitle = false; + std::vector PackageList; for (auto const &Pkg: cont) { @@ -67,24 +72,32 @@ template bool else { std::string const PkgName = PkgDisplay(Pkg); - if (ScreenUsed == 0 || (ScreenUsed + PkgName.length()) >= ScreenWidth) - { - out << std::endl << " "; - ScreenUsed = 0; - } - else if (ScreenUsed != 0) + if (ListColumns) + PackageList.push_back(PkgName); + else { - out << " "; - ++ScreenUsed; + if (ScreenUsed == 0 || (ScreenUsed + PkgName.length()) >= ScreenWidth) + { + out << std::endl + << " "; + ScreenUsed = 0; + } + else if (ScreenUsed != 0) + { + out << " "; + ++ScreenUsed; + } + out << PkgName; + ScreenUsed += PkgName.length(); } - out << PkgName; - ScreenUsed += PkgName.length(); } } if (printedTitle == true) { out << std::endl; + if (ListColumns) + ShowWithColumns(out, PackageList, 2, ScreenWidth); return false; } return true; diff --git a/completions/bash/apt b/completions/bash/apt index 59a8eaa94..7c4b44a1a 100644 --- a/completions/bash/apt +++ b/completions/bash/apt @@ -73,7 +73,7 @@ _apt() case ${command-} in install|reinstall|remove|purge|upgrade|dist-upgrade|full-upgrade|autoremove|autopurge) COMPREPLY=( $( compgen -W '--show-progress - --fix-broken --purge --verbose-versions --auto-remove + --fix-broken --purge --verbose-versions --no-list-columns --auto-remove -s --simulate --dry-run --download --fix-missing diff --git a/doc/apt-get.8.xml b/doc/apt-get.8.xml index da1e91997..807345a3d 100644 --- a/doc/apt-get.8.xml +++ b/doc/apt-get.8.xml @@ -410,6 +410,12 @@ Configuration Item: APT::Get::Show-Versions. + + Display package lists without arranging them in columns. By + default, package lists are printed in the style of the "ls" command. + Configuration Item: APT::Get::List-Columns. + + This option controls the architecture packages are built for diff --git a/doc/examples/configure-index b/doc/examples/configure-index index da4998b4d..9a2bcbf1c 100644 --- a/doc/examples/configure-index +++ b/doc/examples/configure-index @@ -104,6 +104,7 @@ APT Show-Upgraded ""; Show-Versions ""; + List-Columns ""; Upgrade ""; Only-Upgrade ""; Upgrade-Allow-New ""; diff --git a/test/integration/framework b/test/integration/framework index 0570d5c8e..57d6849da 100644 --- a/test/integration/framework +++ b/test/integration/framework @@ -444,6 +444,7 @@ _setupprojectenvironment() { echo "Dir::Etc \"etc/apt\";" >> aptconfig.conf echo "Dir::Log \"var/log/apt\";" >> aptconfig.conf echo "APT::Get::Show-User-Simulation-Note \"false\";" >> aptconfig.conf + echo "APT::Get::List-Columns \"false\";" >> aptconfig.conf echo "Dir::Bin::Methods \"${TMPWORKINGDIRECTORY}/rootdir/usr/lib/apt/methods\";" >> aptconfig.conf # either store apt-key were we can access it, even if we run it as a different user #cp "${APTCMDLINEBINDIR}/apt-key" "${TMPWORKINGDIRECTORY}/rootdir/usr/bin/" -- cgit v1.2.3-70-g09d2 From 88b0673b0dcc4c9c917b0cb9c16cc2ca0a92a987 Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Fri, 12 Apr 2024 11:44:41 +0200 Subject: columnar: Fix floating point exception (list is empty if -V) --- apt-private/private-output.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apt-private/private-output.h b/apt-private/private-output.h index dcffd1880..70f5b9726 100644 --- a/apt-private/private-output.h +++ b/apt-private/private-output.h @@ -96,7 +96,7 @@ template bool if (printedTitle == true) { out << std::endl; - if (ListColumns) + if (ListColumns && not PackageList.empty()) ShowWithColumns(out, PackageList, 2, ScreenWidth); return false; } -- cgit v1.2.3-70-g09d2 From 8ddfeb2fb65dd45267d8f7abfc540d2b8cb73a5c Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Fri, 12 Apr 2024 12:00:45 +0200 Subject: OpProgress: Erase lines when done It's interesting to the user to see the progress when it happens, but arguably once it's done it is just visual clutter, so let's not write newlines, and when we are done, instead of appending "Done", let's just empty the line. This requires some effort to keep apt-cdrom happy which just writes lines to stdout itself. Bad apt-cdrom. Maybe there is a better fix for it, but this gets us going. --- apt-pkg/contrib/progress.cc | 11 +++++++---- apt-private/private-cmndline.cc | 4 ++++ doc/examples/configure-index | 1 + 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/apt-pkg/contrib/progress.cc b/apt-pkg/contrib/progress.cc index a2c4332ca..df068e43a 100644 --- a/apt-pkg/contrib/progress.cc +++ b/apt-pkg/contrib/progress.cc @@ -153,13 +153,15 @@ void OpTextProgress::Done() { if (NoUpdate == false && OldOp.empty() == false) { - char S[300]; + char S[300] = {}; if (_error->PendingError() == true) snprintf(S,sizeof(S),_("%c%s... Error!"),'\r',OldOp.c_str()); - else + else if (not _config->FindB("APT::Internal::OpProgress::EraseLines", true)) snprintf(S,sizeof(S),_("%c%s... Done"),'\r',OldOp.c_str()); Write(S); - cout << endl; + // FIXME: apt-cdrom relies on this end of line being printed + if (_error->PendingError() || not _config->FindB("APT::Internal::OpProgress::EraseLines", true)) + cout << endl; OldOp = string(); } @@ -200,7 +202,8 @@ void OpTextProgress::Update() { snprintf(S,sizeof(S),"\r%s",OldOp.c_str()); Write(S); - cout << endl; + if (_config->FindB("APT::Internal::OpProgress::EraseLines", true)) + cout << endl; } // Print the spinner. Absolute progress shows us a time progress. diff --git a/apt-private/private-cmndline.cc b/apt-private/private-cmndline.cc index cbab22031..505f1f2b8 100644 --- a/apt-private/private-cmndline.cc +++ b/apt-private/private-cmndline.cc @@ -481,6 +481,10 @@ static bool ShowCommonHelp(APT_CMD const Binary, CommandLine &CmdL, std::vector< static void BinarySpecificConfiguration(char const * const Binary) /*{{{*/ { std::string const binary = flNotDir(Binary); + if (binary == "apt-cdrom" || binary == "apt-config") + { + _config->CndSet("Binary::apt-cdrom::APT::Internal::OpProgress::EraseLines", false); + } if (binary == "apt" || binary == "apt-config") { if (getenv("NO_COLOR") == nullptr) diff --git a/doc/examples/configure-index b/doc/examples/configure-index index 9a2bcbf1c..a8fc98daa 100644 --- a/doc/examples/configure-index +++ b/doc/examples/configure-index @@ -881,6 +881,7 @@ Rred::f ""; Rred::Compress ""; APT::Internal::OpProgress::Absolute ""; +APT::Internal::OpProgress::EraseLines ""; APT::Color ""; update-manager::always-include-phased-updates ""; -- cgit v1.2.3-70-g09d2 From abfae1aec588c1b4ae46f229d8312a3c5e6b8b7a Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Thu, 11 Apr 2024 23:04:43 +0200 Subject: apt: Introduce the new terse apt output format 3.0 The key talking points here are: 1. Instead of long sentences, we use short concise messages, e.g. "The following NEW packages will be installed" becomes "Installing". 2. Dependencies are only listed once. We removed the "The following additional packages will be installed" section in favor of splitting up the "Installing" section into "Installing" and "Installing dependencies" (like dnf) 3. The order of the output is different: 1. Packages to be installed manually 2. Packages to be installed automatically 4. Weak dependencies of new packages not installed 3. Packages to be upgraded 4. Packages to be downgraded 5. Packages that have been kept back / are on hold 6. Removals 7. Essential removals i.e. we logically show you the action that is being done, followed by lists related to the action. 4. As requested by popey, we have colorful UI, with green for packages being installed and red for packages being removed. Caveats: - The list of recommends and suggests has not been updated yet, it should move to after the packages being installed (as they are what triggers them) This also introduces output format versioning, configured by the APT::Output-Format option. The default value is 0, except for the apt(8) binary where it is 30 - which enables the new style. --- apt-private/private-cmndline.cc | 1 + apt-private/private-install.cc | 29 +++++++++++----- apt-private/private-output.cc | 76 +++++++++++++++++++++++++++++------------ apt-private/private-output.h | 19 ++++++++--- doc/examples/configure-index | 1 + test/integration/framework | 2 +- 6 files changed, 92 insertions(+), 36 deletions(-) diff --git a/apt-private/private-cmndline.cc b/apt-private/private-cmndline.cc index 505f1f2b8..3d6816d57 100644 --- a/apt-private/private-cmndline.cc +++ b/apt-private/private-cmndline.cc @@ -489,6 +489,7 @@ static void BinarySpecificConfiguration(char const * const Binary) /*{{{*/ { if (getenv("NO_COLOR") == nullptr) _config->CndSet("Binary::apt::APT::Color", true); + _config->CndSet("Binary::apt::APT::Output-Version", 30); _config->CndSet("Binary::apt::APT::Cache::Show::Version", 2); _config->CndSet("Binary::apt::APT::Cache::AllVersions", false); _config->CndSet("Binary::apt::APT::Cache::ShowVirtuals", true); diff --git a/apt-private/private-install.cc b/apt-private/private-install.cc index 254d93436..70356ea28 100644 --- a/apt-private/private-install.cc +++ b/apt-private/private-install.cc @@ -141,6 +141,7 @@ struct WarnUsrMerge { #endif bool InstallPackages(CacheFile &Cache, APT::PackageVector &HeldBackPackages, bool ShwKept, bool Ask, bool Safety, std::string const &Hook, CommandLine const &CmdL) { + auto outVer = _config->FindI("APT::Output-Version"); #ifdef REQUIRE_MERGED_USR WarnUsrMerge warnUsrMerge(Cache); #endif @@ -221,8 +222,11 @@ bool InstallPackages(CacheFile &Cache, APT::PackageVector &HeldBackPackages, boo } // Show all the various warning indicators - ShowDel(c1out,Cache); + if (_config->FindI("APT::Output-Version") < 30) + ShowDel(c1out,Cache); ShowNew(c1out,Cache); + if (_config->FindI("APT::Output-Version") >= 30 && _config->FindB("APT::Get::Show-Upgraded",true) == true) + ShowUpgraded(c1out,Cache); if (ShwKept == true) { ShowPhasing(c1out, Cache, PhasingPackages); @@ -231,10 +235,13 @@ bool InstallPackages(CacheFile &Cache, APT::PackageVector &HeldBackPackages, boo _error->Notice("Some packages may have been kept back due to phasing."); } bool const Hold = not ShowHold(c1out,Cache); - if (_config->FindB("APT::Get::Show-Upgraded",true) == true) + if (_config->FindI("APT::Output-Version") < 30 && _config->FindB("APT::Get::Show-Upgraded",true) == true) ShowUpgraded(c1out,Cache); bool const Downgrade = !ShowDowngraded(c1out,Cache); + // Show removed packages last + if (_config->FindI("APT::Output-Version") >= 30) + ShowDel(c1out,Cache); bool Essential = false; if (_config->FindB("APT::Get::Download-Only",false) == false) Essential = !ShowEssential(c1out,Cache); @@ -312,12 +319,12 @@ bool InstallPackages(CacheFile &Cache, APT::PackageVector &HeldBackPackages, boo if (DebBytes != FetchBytes) //TRANSLATOR: The required space between number and unit is already included // in the replacement strings, so %sB will be correctly translate in e.g. 1,5 MB - ioprintf(c1out,_("Need to get %sB/%sB of archives.\n"), + ioprintf(c1out,outVer < 30 ? _("Need to get %sB/%sB of archives.\n") : _(" Download size: %sB/%sB\n"), SizeToStr(FetchBytes).c_str(),SizeToStr(DebBytes).c_str()); else if (DebBytes != 0) //TRANSLATOR: The required space between number and unit is already included // in the replacement string, so %sB will be correctly translate in e.g. 1,5 MB - ioprintf(c1out,_("Need to get %sB of archives.\n"), + ioprintf(c1out,outVer < 30 ? _("Need to get %sB of archives.\n") : _(" Download size: %sB\n"), SizeToStr(DebBytes).c_str()); } @@ -325,14 +332,16 @@ bool InstallPackages(CacheFile &Cache, APT::PackageVector &HeldBackPackages, boo if (Cache->UsrSize() >= 0) //TRANSLATOR: The required space between number and unit is already included // in the replacement string, so %sB will be correctly translate in e.g. 1,5 MB - ioprintf(c1out,_("After this operation, %sB of additional disk space will be used.\n"), + ioprintf(c1out,outVer < 30 ? _("After this operation, %sB of additional disk space will be used.\n") : _(" Installed size: %sB\n"), SizeToStr(Cache->UsrSize()).c_str()); else //TRANSLATOR: The required space between number and unit is already included // in the replacement string, so %sB will be correctly translate in e.g. 1,5 MB - ioprintf(c1out,_("After this operation, %sB disk space will be freed.\n"), + ioprintf(c1out,outVer < 30 ? _("After this operation, %sB disk space will be freed.\n") : _(" Freed space: %sB\n"), SizeToStr(-1*Cache->UsrSize()).c_str()); + if (outVer >= 30) + ioprintf(c1out,"\n"); if (DownloadAllowed) if (CheckFreeSpaceBeforeDownload(_config->FindDir("Dir::Cache::Archives"), (FetchBytes - FetchPBytes)) == false) return false; @@ -368,7 +377,7 @@ bool InstallPackages(CacheFile &Cache, APT::PackageVector &HeldBackPackages, boo if (_config->FindI("quiet",0) < 2 && _config->FindB("APT::Get::Assume-Yes",false) == false) { - if (YnPrompt(_("Do you want to continue?")) == false) + if (YnPrompt(outVer < 30 ? _("Do you want to continue?") : _("Continue?")) == false) { c2out << _("Abort.") << std::endl; exit(1); @@ -933,14 +942,16 @@ bool DoInstall(CommandLine &CmdL) /* Print out a list of packages that are going to be installed extra to what the user asked */ SortedPackageUniverse Universe(Cache); - if (Cache->InstCount() != verset[MOD_INSTALL].size()) + if (_config->FindI("APT::Output-Version") < 30 && Cache->InstCount() != verset[MOD_INSTALL].size()) ShowList(c1out, _("The following additional packages will be installed:"), Universe, PkgIsExtraInstalled(&Cache, &verset[MOD_INSTALL]), - &PrettyFullName, CandidateVersion(&Cache)); + &PrettyFullName, CandidateVersion(&Cache), "APT::Color::Green"); + /* Print out a list of suggested and recommended packages */ { std::list Recommends, Suggests, SingleRecommends, SingleSuggests; + SortedPackageUniverse Universe(Cache); for (auto const &Pkg: Universe) { /* Just look at the ones we want to install */ diff --git a/apt-private/private-output.cc b/apt-private/private-output.cc index f04c35b8a..bffdd6f67 100644 --- a/apt-private/private-output.cc +++ b/apt-private/private-output.cc @@ -524,8 +524,10 @@ void ShowBroken(ostream &out, CacheFile &Cache, bool const Now) { if (Cache->BrokenCount() == 0) return; - - out << _("The following packages have unmet dependencies:") << endl; + if (_config->FindI("APT::Output-Version") < 30) + out << _("The following packages have unmet dependencies:") << endl; + else + out << _("Unsatisfied dependencies:") << endl; SortedPackageUniverse Universe(Cache); for (auto const &Pkg: Universe) ShowBrokenPackage(out, &Cache, Pkg, Now); @@ -535,7 +537,10 @@ void ShowBroken(ostream &out, pkgCacheFile &Cache, bool const Now) if (Cache->BrokenCount() == 0) return; - out << _("The following packages have unmet dependencies:") << endl; + if (_config->FindI("APT::Output-Version") < 30) + out << _("The following packages have unmet dependencies:") << endl; + else + out << _("Unsatisfied dependencies:") << endl; APT::PackageUniverse Universe(Cache); for (auto const &Pkg: Universe) ShowBrokenPackage(out, &Cache, Pkg, Now); @@ -545,17 +550,33 @@ void ShowBroken(ostream &out, pkgCacheFile &Cache, bool const Now) void ShowNew(ostream &out,CacheFile &Cache) { SortedPackageUniverse Universe(Cache); - ShowList(out,_("The following NEW packages will be installed:"), Universe, - [&Cache](pkgCache::PkgIterator const &Pkg) { return Cache[Pkg].NewInstall(); }, + if (_config->FindI("APT::Output-Version") < 30) { + ShowList(out,_("The following NEW packages will be installed:"), Universe, + [&Cache](pkgCache::PkgIterator const &Pkg) { return Cache[Pkg].NewInstall(); }, + &PrettyFullName, + CandidateVersion(&Cache), + "APT::Color::Green"); + return; + } + + ShowList(out,_("Installing:"), Universe, + [&Cache](pkgCache::PkgIterator const &Pkg) { return Cache[Pkg].NewInstall() && (Cache[Pkg].Flags & pkgCache::Flag::Auto) == 0; }, &PrettyFullName, - CandidateVersion(&Cache)); + CandidateVersion(&Cache), + "APT::Color::Green"); + ShowList(out,_("Installing dependencies:"), Universe, + [&Cache](pkgCache::PkgIterator const &Pkg) { return Cache[Pkg].NewInstall() && Cache[Pkg].Flags & pkgCache::Flag::Auto;}, + &PrettyFullName, + CandidateVersion(&Cache), + "APT::Color::Green"); } /*}}}*/ // ShowDel - Show packages to delete /*{{{*/ void ShowDel(ostream &out,CacheFile &Cache) { SortedPackageUniverse Universe(Cache); - ShowList(out,_("The following packages will be REMOVED:"), Universe, + auto title = _config->FindI("APT::Output-Version") < 30 ? _("The following packages will be REMOVED:") : _("REMOVING:"); + ShowList(out,title, Universe, [&Cache](pkgCache::PkgIterator const &Pkg) { return Cache[Pkg].Delete(); }, [&Cache](pkgCache::PkgIterator const &Pkg) { @@ -564,14 +585,18 @@ void ShowDel(ostream &out,CacheFile &Cache) str.append("*"); return str; }, - CandidateVersion(&Cache)); + CandidateVersion(&Cache), + "APT::Color::Red"); } /*}}}*/ // ShowPhasing - Show packages kept due to phasing /*{{{*/ void ShowPhasing(ostream &out, CacheFile &Cache, APT::PackageVector const &HeldBackPackages) { SortedPackageUniverse Universe(Cache); - ShowList(out, _("The following upgrades have been deferred due to phasing:"), HeldBackPackages, + auto title = _config->FindI("APT::Output-Version") < 30 + ? _("The following upgrades have been deferred due to phasing:") + : _("Not upgrading yet due to phasing:"); + ShowList(out, title, HeldBackPackages, &AlwaysTrue, &PrettyFullName, CurrentToCandidateVersion(&Cache)); @@ -581,7 +606,8 @@ void ShowPhasing(ostream &out, CacheFile &Cache, APT::PackageVector const &HeldB void ShowKept(ostream &out,CacheFile &Cache, APT::PackageVector const &HeldBackPackages) { SortedPackageUniverse Universe(Cache); - ShowList(out,_("The following packages have been kept back:"), HeldBackPackages, + auto title = _config->FindI("APT::Output-Version") < 30 ? _("The following packages have been kept back:") : _("Not upgrading:"); + ShowList(out, title, HeldBackPackages, &AlwaysTrue, &PrettyFullName, CurrentToCandidateVersion(&Cache)); @@ -591,13 +617,15 @@ void ShowKept(ostream &out,CacheFile &Cache, APT::PackageVector const &HeldBackP void ShowUpgraded(ostream &out,CacheFile &Cache) { SortedPackageUniverse Universe(Cache); - ShowList(out,_("The following packages will be upgraded:"), Universe, + auto title = _config->FindI("APT::Output-Version") < 30 ? _("The following packages will be upgraded:") : _("Upgrading:"); + ShowList(out, title, Universe, [&Cache](pkgCache::PkgIterator const &Pkg) { return Cache[Pkg].Upgrade() == true && Cache[Pkg].NewInstall() == false; }, &PrettyFullName, - CurrentToCandidateVersion(&Cache)); + CurrentToCandidateVersion(&Cache), + "APT::Color::Green"); } /*}}}*/ // ShowDowngraded - Show downgraded packages /*{{{*/ @@ -606,20 +634,23 @@ void ShowUpgraded(ostream &out,CacheFile &Cache) bool ShowDowngraded(ostream &out,CacheFile &Cache) { SortedPackageUniverse Universe(Cache); - return ShowList(out,_("The following packages will be DOWNGRADED:"), Universe, + auto title = _config->FindI("APT::Output-Version") < 30 ? _("The following packages will be DOWNGRADED:") : _("DOWNGRADING:"); + return ShowList(out, title, Universe, [&Cache](pkgCache::PkgIterator const &Pkg) { return Cache[Pkg].Downgrade() == true && Cache[Pkg].NewInstall() == false; }, &PrettyFullName, - CurrentToCandidateVersion(&Cache)); + CurrentToCandidateVersion(&Cache), + "APT::Color::Green"); } /*}}}*/ // ShowHold - Show held but changed packages /*{{{*/ bool ShowHold(ostream &out,CacheFile &Cache) { SortedPackageUniverse Universe(Cache); - return ShowList(out,_("The following held packages will be changed:"), Universe, + auto title = _config->FindI("APT::Output-Version") < 30 ? _("The following held packages will be changed:") : _("Changing held packages:Changing held packages:"); + return ShowList(out, title, Universe, [&Cache](pkgCache::PkgIterator const &Pkg) { return Pkg->SelectedState == pkgCache::State::Hold && @@ -710,6 +741,7 @@ void Stats(ostream &out, pkgDepCache &Dep, APT::PackageVector const &HeldBackPac unsigned long Downgrade = 0; unsigned long Install = 0; unsigned long ReInstall = 0; + auto outVer = _config->FindI("APT::Output-Version"); for (pkgCache::PkgIterator I = Dep.PkgBegin(); I.end() == false; ++I) { if (Dep[I].NewInstall() == true) @@ -726,18 +758,20 @@ void Stats(ostream &out, pkgDepCache &Dep, APT::PackageVector const &HeldBackPac if (Dep[I].Delete() == false && (Dep[I].iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall) ReInstall++; } - - ioprintf(out,_("%lu upgraded, %lu newly installed, "), + if (outVer >= 30) + ioprintf(out, _("Summary:\n")); + ioprintf(out,outVer < 30 ? _("%lu upgraded, %lu newly installed, ") : _(" Upgrades: %lu, Installs: %lu, "), Upgrade,Install); if (ReInstall != 0) - ioprintf(out,_("%lu reinstalled, "),ReInstall); + ioprintf(out,outVer < 30 ? _("%lu reinstalled, ") : _("Reinstalls: %lu, "),ReInstall); if (Downgrade != 0) - ioprintf(out,_("%lu downgraded, "),Downgrade); + ioprintf(out,outVer < 30 ? _("%lu downgraded, ") : _("Downgrades: %lu, "),Downgrade); - ioprintf(out,_("%lu to remove and %lu not upgraded.\n"), + ioprintf(out, outVer < 30 ? _("%lu to remove and %lu not upgraded.\n") : _("Removals: %lu, Not Upgrading: %lu\n"), Dep.DelCount(), HeldBackPackages.size()); - + + // FIXME: outVer if (Dep.BadCount() != 0) ioprintf(out,_("%lu not fully installed or removed.\n"), Dep.BadCount()); diff --git a/apt-private/private-output.h b/apt-private/private-output.h index 70f5b9726..4cc7c01b4 100644 --- a/apt-private/private-output.h +++ b/apt-private/private-output.h @@ -42,15 +42,19 @@ template bool Container const &cont, PredicateC Predicate, DisplayP PkgDisplay, - DisplayV VerboseDisplay) + DisplayV VerboseDisplay, + std::string colorName = "APT::Color::Neutral") { size_t const ScreenWidth = (::ScreenWidth > 3) ? ::ScreenWidth - 3 : 0; int ScreenUsed = 0; bool const ShowVersions = _config->FindB("APT::Get::Show-Versions", false); - bool const ListColumns = _config->FindB("APT::Get::List-Columns", true); + bool const ListColumns = _config->FindB("APT::Get::List-Columns", _config->FindI("APT::Output-Version") >= 30); bool printedTitle = false; std::vector PackageList; + auto setColor = _config->FindI("APT::Output-Version") >= 30 ? _config->Find(colorName) : ""; + auto resetColor = _config->FindI("APT::Output-Version") >= 30 ? _config->Find("APT::Color::Neutral") : ""; + for (auto const &Pkg: cont) { if (Predicate(Pkg) == false) @@ -64,7 +68,7 @@ template bool if (ShowVersions == true) { - out << std::endl << " " << PkgDisplay(Pkg); + out << std::endl << " " << setColor << PkgDisplay(Pkg) << resetColor; std::string const verbose = VerboseDisplay(Pkg); if (verbose.empty() == false) out << " (" << verbose << ")"; @@ -87,7 +91,7 @@ template bool out << " "; ++ScreenUsed; } - out << PkgName; + out << setColor << PkgName << resetColor; ScreenUsed += PkgName.length(); } } @@ -96,8 +100,13 @@ template bool if (printedTitle == true) { out << std::endl; - if (ListColumns && not PackageList.empty()) + if (ListColumns && not PackageList.empty()) { + out << setColor; ShowWithColumns(out, PackageList, 2, ScreenWidth); + out << resetColor; + } + if (_config->FindI("APT::Output-Version") >= 30) + out << std::endl; return false; } return true; diff --git a/doc/examples/configure-index b/doc/examples/configure-index index a8fc98daa..cc9a2e765 100644 --- a/doc/examples/configure-index +++ b/doc/examples/configure-index @@ -723,6 +723,7 @@ apt::system ""; apt::acquire::translation ""; // deprecated in favor of Acquire::Languages apt::color::highlight ""; apt::color::neutral ""; +apt::output-version ""; dpkgpm::reporting-steps ""; diff --git a/test/integration/framework b/test/integration/framework index 57d6849da..147de9808 100644 --- a/test/integration/framework +++ b/test/integration/framework @@ -444,7 +444,7 @@ _setupprojectenvironment() { echo "Dir::Etc \"etc/apt\";" >> aptconfig.conf echo "Dir::Log \"var/log/apt\";" >> aptconfig.conf echo "APT::Get::Show-User-Simulation-Note \"false\";" >> aptconfig.conf - echo "APT::Get::List-Columns \"false\";" >> aptconfig.conf + echo "Binary::apt::APT::Output-Version \"0\";" >> aptconfig.conf echo "Dir::Bin::Methods \"${TMPWORKINGDIRECTORY}/rootdir/usr/lib/apt/methods\";" >> aptconfig.conf # either store apt-key were we can access it, even if we run it as a different user #cp "${APTCMDLINEBINDIR}/apt-key" "${TMPWORKINGDIRECTORY}/rootdir/usr/bin/" -- cgit v1.2.3-70-g09d2 From d3274a11d9e153cd996b89436b01d00b218a4985 Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Fri, 12 Apr 2024 14:24:33 +0200 Subject: Show Recommends/Suggests for upgrades too, move them down Extract the code to show them into its own function and call it in the old place for output version < 30, or after the dependencies being installed for new output format. --- apt-private/private-install.cc | 189 +++++++++++++++++++++-------------------- 1 file changed, 98 insertions(+), 91 deletions(-) diff --git a/apt-private/private-install.cc b/apt-private/private-install.cc index 70356ea28..26b81b052 100644 --- a/apt-private/private-install.cc +++ b/apt-private/private-install.cc @@ -139,6 +139,7 @@ struct WarnUsrMerge { } }; #endif +static void ShowWeakDependencies(CacheFile &Cache); bool InstallPackages(CacheFile &Cache, APT::PackageVector &HeldBackPackages, bool ShwKept, bool Ask, bool Safety, std::string const &Hook, CommandLine const &CmdL) { auto outVer = _config->FindI("APT::Output-Version"); @@ -225,6 +226,8 @@ bool InstallPackages(CacheFile &Cache, APT::PackageVector &HeldBackPackages, boo if (_config->FindI("APT::Output-Version") < 30) ShowDel(c1out,Cache); ShowNew(c1out,Cache); + if (_config->FindI("APT::Output-Version") >= 30) + ShowWeakDependencies(Cache); if (_config->FindI("APT::Output-Version") >= 30 && _config->FindB("APT::Get::Show-Upgraded",true) == true) ShowUpgraded(c1out,Cache); if (ShwKept == true) @@ -912,6 +915,99 @@ struct PkgIsExtraInstalled { return std::find(verset->begin(), verset->end(), Cand) == verset->end(); } }; +/* Print out a list of suggested and recommended packages */ +static void ShowWeakDependencies(CacheFile &Cache) +{ + std::list Recommends, Suggests, SingleRecommends, SingleSuggests; + SortedPackageUniverse Universe(Cache); + for (auto const &Pkg: Universe) + { + /* Just look at the ones we want to install */ + if ((*Cache)[Pkg].Install() == false) + continue; + + // get the recommends/suggests for the candidate ver + pkgCache::VerIterator CV = (*Cache)[Pkg].CandidateVerIter(*Cache); + for (pkgCache::DepIterator D = CV.DependsList(); D.end() == false; ) + { + pkgCache::DepIterator Start; + pkgCache::DepIterator End; + D.GlobOr(Start,End); // advances D + if (Start->Type != pkgCache::Dep::Recommends && Start->Type != pkgCache::Dep::Suggests) + continue; + + { + // Skip if we already saw this + std::string target; + for (pkgCache::DepIterator I = Start; I != D; ++I) + { + if (target.empty() == false) + target.append(" | "); + target.append(I.TargetPkg().FullName(true)); + } + std::list &Type = Start->Type == pkgCache::Dep::Recommends ? SingleRecommends : SingleSuggests; + if (std::find(Type.begin(), Type.end(), target) != Type.end()) + continue; + Type.push_back(target); + } + + std::list OrList; + bool foundInstalledInOrGroup = false; + for (pkgCache::DepIterator I = Start; I != D; ++I) + { + { + // satisfying package is installed and not marked for deletion + APT::VersionList installed = APT::VersionList::FromDependency(Cache, I, APT::CacheSetHelper::INSTALLED); + if (std::find_if(installed.begin(), installed.end(), + [&Cache](pkgCache::VerIterator const &Ver) { return Cache[Ver.ParentPkg()].Delete() == false; }) != installed.end()) + { + foundInstalledInOrGroup = true; + break; + } + } + + { + // satisfying package is upgraded to/new install + APT::VersionList upgrades = APT::VersionList::FromDependency(Cache, I, APT::CacheSetHelper::CANDIDATE); + if (std::find_if(upgrades.begin(), upgrades.end(), + [&Cache](pkgCache::VerIterator const &Ver) { return Cache[Ver.ParentPkg()].Upgrade(); }) != upgrades.end()) + { + foundInstalledInOrGroup = true; + break; + } + } + + if (OrList.empty()) + OrList.push_back(I.TargetPkg().FullName(true)); + else + OrList.push_back("| " + I.TargetPkg().FullName(true)); + } + + if(foundInstalledInOrGroup == false) + { + std::list &Type = Start->Type == pkgCache::Dep::Recommends ? Recommends : Suggests; + std::move(OrList.begin(), OrList.end(), std::back_inserter(Type)); + } + } + } + auto always_true = [](std::string const&) { return true; }; + auto string_ident = [](std::string const&str) { return str; }; + auto verbose_show_candidate = + [&Cache](std::string str) + { + if (APT::String::Startswith(str, "| ")) + str.erase(0, 2); + pkgCache::PkgIterator const Pkg = Cache->FindPkg(str); + if (Pkg.end() == true) + return ""; + return (*Cache)[Pkg].CandVersion; + }; + ShowList(c1out,_("Suggested packages:"), Suggests, + always_true, string_ident, verbose_show_candidate); + ShowList(c1out,_("Recommended packages:"), Recommends, + always_true, string_ident, verbose_show_candidate); +} + bool DoInstall(CommandLine &CmdL) { CacheFile Cache; @@ -947,98 +1043,9 @@ bool DoInstall(CommandLine &CmdL) PkgIsExtraInstalled(&Cache, &verset[MOD_INSTALL]), &PrettyFullName, CandidateVersion(&Cache), "APT::Color::Green"); - /* Print out a list of suggested and recommended packages */ - { - std::list Recommends, Suggests, SingleRecommends, SingleSuggests; - SortedPackageUniverse Universe(Cache); - for (auto const &Pkg: Universe) - { - /* Just look at the ones we want to install */ - if ((*Cache)[Pkg].Install() == false) - continue; - - // get the recommends/suggests for the candidate ver - pkgCache::VerIterator CV = (*Cache)[Pkg].CandidateVerIter(*Cache); - for (pkgCache::DepIterator D = CV.DependsList(); D.end() == false; ) - { - pkgCache::DepIterator Start; - pkgCache::DepIterator End; - D.GlobOr(Start,End); // advances D - if (Start->Type != pkgCache::Dep::Recommends && Start->Type != pkgCache::Dep::Suggests) - continue; - - { - // Skip if we already saw this - std::string target; - for (pkgCache::DepIterator I = Start; I != D; ++I) - { - if (target.empty() == false) - target.append(" | "); - target.append(I.TargetPkg().FullName(true)); - } - std::list &Type = Start->Type == pkgCache::Dep::Recommends ? SingleRecommends : SingleSuggests; - if (std::find(Type.begin(), Type.end(), target) != Type.end()) - continue; - Type.push_back(target); - } - - std::list OrList; - bool foundInstalledInOrGroup = false; - for (pkgCache::DepIterator I = Start; I != D; ++I) - { - { - // satisfying package is installed and not marked for deletion - APT::VersionList installed = APT::VersionList::FromDependency(Cache, I, APT::CacheSetHelper::INSTALLED); - if (std::find_if(installed.begin(), installed.end(), - [&Cache](pkgCache::VerIterator const &Ver) { return Cache[Ver.ParentPkg()].Delete() == false; }) != installed.end()) - { - foundInstalledInOrGroup = true; - break; - } - } - - { - // satisfying package is upgraded to/new install - APT::VersionList upgrades = APT::VersionList::FromDependency(Cache, I, APT::CacheSetHelper::CANDIDATE); - if (std::find_if(upgrades.begin(), upgrades.end(), - [&Cache](pkgCache::VerIterator const &Ver) { return Cache[Ver.ParentPkg()].Upgrade(); }) != upgrades.end()) - { - foundInstalledInOrGroup = true; - break; - } - } - - if (OrList.empty()) - OrList.push_back(I.TargetPkg().FullName(true)); - else - OrList.push_back("| " + I.TargetPkg().FullName(true)); - } - - if(foundInstalledInOrGroup == false) - { - std::list &Type = Start->Type == pkgCache::Dep::Recommends ? Recommends : Suggests; - std::move(OrList.begin(), OrList.end(), std::back_inserter(Type)); - } - } - } - auto always_true = [](std::string const&) { return true; }; - auto string_ident = [](std::string const&str) { return str; }; - auto verbose_show_candidate = - [&Cache](std::string str) - { - if (APT::String::Startswith(str, "| ")) - str.erase(0, 2); - pkgCache::PkgIterator const Pkg = Cache->FindPkg(str); - if (Pkg.end() == true) - return ""; - return (*Cache)[Pkg].CandVersion; - }; - ShowList(c1out,_("Suggested packages:"), Suggests, - always_true, string_ident, verbose_show_candidate); - ShowList(c1out,_("Recommended packages:"), Recommends, - always_true, string_ident, verbose_show_candidate); - } + if (_config->FindI("APT::Output-Version") < 30) + ShowWeakDependencies(Cache); RunJsonHook("AptCli::Hooks::Install", "org.debian.apt.hooks.install.pre-prompt", CmdL.FileList, Cache); -- cgit v1.2.3-70-g09d2 From 275ee5afe968e77d8379f0f3b6dc54c6239aa4f6 Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Fri, 12 Apr 2024 15:10:20 +0200 Subject: Only show Recommends/Suggests for new installs, not upgrades This makes things more useful in combination with the upgrade command, but introduces a subtle change seen in the test suite when you use the install command to upgrade packages. --- apt-private/private-install.cc | 2 +- test/integration/test-bug-470115-new-and-tighten-recommends | 2 -- test/integration/test-ubuntu-bug-614993 | 4 ---- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/apt-private/private-install.cc b/apt-private/private-install.cc index 26b81b052..3efae0112 100644 --- a/apt-private/private-install.cc +++ b/apt-private/private-install.cc @@ -923,7 +923,7 @@ static void ShowWeakDependencies(CacheFile &Cache) for (auto const &Pkg: Universe) { /* Just look at the ones we want to install */ - if ((*Cache)[Pkg].Install() == false) + if ((*Cache)[Pkg].NewInstall() == false) continue; // get the recommends/suggests for the candidate ver diff --git a/test/integration/test-bug-470115-new-and-tighten-recommends b/test/integration/test-bug-470115-new-and-tighten-recommends index f27990e3e..e1c272b18 100755 --- a/test/integration/test-bug-470115-new-and-tighten-recommends +++ b/test/integration/test-bug-470115-new-and-tighten-recommends @@ -165,8 +165,6 @@ Conf upgrade-over-new (2 unstable [all])' aptget install upgrade-over-new -s # the user doesn't seem to need it so avoid upgrading it testsuccessequal 'Reading package lists... Building dependency tree... -Recommended packages: - cool The following packages will be upgraded: now-satisfiable 1 upgraded, 0 newly installed, 0 to remove and 12 not upgraded. diff --git a/test/integration/test-ubuntu-bug-614993 b/test/integration/test-ubuntu-bug-614993 index 542e55105..fe830b499 100755 --- a/test/integration/test-ubuntu-bug-614993 +++ b/test/integration/test-ubuntu-bug-614993 @@ -28,10 +28,6 @@ The following additional packages will be installed: xserver-xorg-video-sisusb xserver-xorg-video-tdfx xserver-xorg-video-trident xserver-xorg-video-tseng xserver-xorg-video-vesa xserver-xorg-video-vmware xserver-xorg-video-voodoo -Suggested packages: - xfonts-scalable gpointing-device-settings touchfreeze firmware-linux -Recommended packages: - xauth intel-gpu-tools The following packages will be REMOVED: xserver-xorg-video-v4l The following NEW packages will be installed: -- cgit v1.2.3-70-g09d2 From f725565ae69a06d0ad5747881b6f54a19c0c1763 Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Fri, 12 Apr 2024 18:08:51 +0200 Subject: Use the same words for the summary Instead of using Upgrades, Installs, and so on, just use Upgrading, Installing, etc. This solves the problem of not having a nice noun for "Not upgrading". --- apt-private/private-output.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apt-private/private-output.cc b/apt-private/private-output.cc index bffdd6f67..bbf50397f 100644 --- a/apt-private/private-output.cc +++ b/apt-private/private-output.cc @@ -760,15 +760,15 @@ void Stats(ostream &out, pkgDepCache &Dep, APT::PackageVector const &HeldBackPac } if (outVer >= 30) ioprintf(out, _("Summary:\n")); - ioprintf(out,outVer < 30 ? _("%lu upgraded, %lu newly installed, ") : _(" Upgrades: %lu, Installs: %lu, "), + ioprintf(out,outVer < 30 ? _("%lu upgraded, %lu newly installed, ") : _(" Upgrading: %lu, Installing: %lu, "), Upgrade,Install); if (ReInstall != 0) - ioprintf(out,outVer < 30 ? _("%lu reinstalled, ") : _("Reinstalls: %lu, "),ReInstall); + ioprintf(out,outVer < 30 ? _("%lu reinstalled, ") : _("Reinstalling: %lu, "),ReInstall); if (Downgrade != 0) - ioprintf(out,outVer < 30 ? _("%lu downgraded, ") : _("Downgrades: %lu, "),Downgrade); + ioprintf(out,outVer < 30 ? _("%lu downgraded, ") : _("Downgrading: %lu, "),Downgrade); - ioprintf(out, outVer < 30 ? _("%lu to remove and %lu not upgraded.\n") : _("Removals: %lu, Not Upgrading: %lu\n"), + ioprintf(out, outVer < 30 ? _("%lu to remove and %lu not upgraded.\n") : _("Removing: %lu, Not Upgrading: %lu\n"), Dep.DelCount(), HeldBackPackages.size()); // FIXME: outVer -- cgit v1.2.3-70-g09d2