summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Bingner <sam@bingner.com>2019-12-30 16:31:01 -1000
committerSam Bingner <sam@bingner.com>2021-03-16 13:10:32 -1000
commitd1027179026357c0db89c7b27dd8d774e34e574b (patch)
tree46fa7e9ec6831ad383f664541586a463c4181fa8
parent9d8383bdee9f1ddc5685c36678b1ca033cbea971 (diff)
Add support for JSON output
-rw-r--r--apt-pkg/CMakeLists.txt3
-rw-r--r--apt-pkg/algorithms.h6
-rw-r--r--apt-pkg/algorithms.mm (renamed from apt-pkg/algorithms.cc)193
-rw-r--r--apt-private/CMakeLists.txt4
-rw-r--r--apt-private/private-output.mm (renamed from apt-private/private-output.cc)163
5 files changed, 316 insertions, 53 deletions
diff --git a/apt-pkg/CMakeLists.txt b/apt-pkg/CMakeLists.txt
index 318c6d798..5915a588c 100644
--- a/apt-pkg/CMakeLists.txt
+++ b/apt-pkg/CMakeLists.txt
@@ -32,7 +32,7 @@ set(APT_PKG_MAJOR ${MAJOR} PARENT_SCOPE) # exporting for methods/CMakeLists.txt
# Definition of the C++ files used to build the library - note that this
# is expanded at CMake time, so you have to rerun cmake if you add or remove
# a file (you can just run cmake . in the build directory)
-file(GLOB_RECURSE library "*.cc" "${CMAKE_CURRENT_BINARY_DIR}/tagfile-keys.cc")
+file(GLOB_RECURSE library "*.cc" "*.mm" "${CMAKE_CURRENT_BINARY_DIR}/tagfile-keys.cc")
file(GLOB_RECURSE headers "*.h")
# Create a library using the C++ files
@@ -61,6 +61,7 @@ target_link_libraries(apt-pkg
$<$<BOOL:${UDEV_FOUND}>:${UDEV_LIBRARIES}>
$<$<BOOL:${SYSTEMD_FOUND}>:${SYSTEMD_LIBRARIES}>
${ICONV_LIBRARIES}
+ "-framework Foundation"
)
set_target_properties(apt-pkg PROPERTIES VERSION ${MAJOR}.${MINOR})
set_target_properties(apt-pkg PROPERTIES SOVERSION ${MAJOR})
diff --git a/apt-pkg/algorithms.h b/apt-pkg/algorithms.h
index c5c9f4c69..5ee5e5f62 100644
--- a/apt-pkg/algorithms.h
+++ b/apt-pkg/algorithms.h
@@ -48,6 +48,9 @@ using std::ostream;
#include <apt-pkg/update.h>
#include <apt-pkg/upgrade.h>
#endif
+#ifdef __OBJC__
+#import <Foundation/Foundation.h>
+#endif
class pkgSimulatePrivate;
@@ -87,6 +90,9 @@ public:
private:
APT_HIDDEN void ShortBreaks();
APT_HIDDEN void Describe(PkgIterator iPkg,std::ostream &out,bool Current,bool Candidate);
+#ifdef __OBJC__
+ APT_HIDDEN NSMutableDictionary * DescribeJSON(PkgIterator iPkg,bool Current,bool Candidate);
+#endif
APT_HIDDEN bool RealInstall(PkgIterator Pkg,std::string File);
APT_HIDDEN bool RealConfigure(PkgIterator Pkg);
APT_HIDDEN bool RealRemove(PkgIterator Pkg,bool Purge);
diff --git a/apt-pkg/algorithms.cc b/apt-pkg/algorithms.mm
index bb0e2f873..4ac63419b 100644
--- a/apt-pkg/algorithms.cc
+++ b/apt-pkg/algorithms.mm
@@ -32,6 +32,7 @@
#include <string.h>
#include <apti18n.h>
+NSMutableArray *simulatedOperations = nil;
/*}}}*/
using namespace std;
@@ -94,6 +95,37 @@ void pkgSimulate::Describe(PkgIterator Pkg,ostream &out,bool Current,bool Candid
}
}
/*}}}*/
+// Simulate::DescribeJSON - Describe a package /*{{{*/
+// ---------------------------------------------------------------------
+/* Parameter Current == true displays the current package version,
+ Parameter Candidate == true displays the candidate package version */
+NSMutableDictionary * pkgSimulate::DescribeJSON(PkgIterator Pkg,bool Current,bool Candidate)
+{
+ VerIterator Ver(Sim);
+
+ NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
+ dictionary[@"Package"] = @(Pkg.FullName(true).c_str());
+
+ if (Current == true)
+ {
+ Ver = Pkg.CurrentVer();
+ if (Ver.end() == false){
+ dictionary[@"CurrentVersion"] = @(Ver.VerStr());
+ }
+ }
+
+ if (Candidate == true)
+ {
+ Ver = Sim[Pkg].CandidateVerIter(Sim);
+ if (Ver.end() == true)
+ return dictionary;
+ dictionary[@"CandidateVersion"] = @(Ver.VerStr());
+ dictionary[@"Release"] = @(Ver.RelStr().c_str());
+ }
+
+ return dictionary;
+}
+ /*}}}*/
// Simulate::Install - Simulate unpacking of a package /*{{{*/
// ---------------------------------------------------------------------
/* */
@@ -106,6 +138,41 @@ bool pkgSimulate::Install(PkgIterator iPkg,string File)
}
bool pkgSimulate::RealInstall(PkgIterator iPkg,string /*File*/)
{
+ if (_config->FindB("APT::Format::JSON", false)){
+ // Adapt the iterator
+ PkgIterator Pkg = Sim.FindPkg(iPkg.Name(), iPkg.Arch());
+ Flags[Pkg->ID] = 1;
+
+ NSMutableDictionary *package = DescribeJSON(Pkg, true, true);
+ package[@"Type"] = @"Inst";
+ [simulatedOperations addObject:package];
+
+ Sim.MarkInstall(Pkg,false);
+
+ // Look for broken conflicts+predepends.
+ for (PkgIterator I = Sim.PkgBegin(); I.end() == false; ++I)
+ {
+ if (Sim[I].InstallVer == 0)
+ continue;
+
+ for (DepIterator D = Sim[I].InstVerIter(Sim).DependsList(); D.end() == false;)
+ {
+ DepIterator Start;
+ DepIterator End;
+ D.GlobOr(Start,End);
+ if (Start.IsNegative() == true || End->Type == pkgCache::Dep::PreDepends)
+ {
+ if ((Sim[End] & pkgDepCache::DepGInstall) == 0)
+ {
+ if (Start->Type == pkgCache::Dep::Conflicts)
+ _error->Error("Fatal, conflicts violated %s",I.FullName(false).c_str());
+ }
+ }
+ }
+ }
+ return true;
+ }
+
// Adapt the iterator
PkgIterator Pkg = Sim.FindPkg(iPkg.Name(), iPkg.Arch());
Flags[Pkg->ID] = 1;
@@ -164,42 +231,48 @@ bool pkgSimulate::RealConfigure(PkgIterator iPkg)
Flags[Pkg->ID] = 2;
- if (Sim[Pkg].InstBroken() == true)
- {
- cout << "Conf " << Pkg.FullName(false) << " broken" << endl;
+ if (_config->FindB("APT::Format::JSON", false)){
+ NSMutableDictionary *package = DescribeJSON(Pkg, false, true);
+ package[@"Type"] = @"Conf";
+ [simulatedOperations addObject:package];
+ } else {
+ if (Sim[Pkg].InstBroken() == true)
+ {
+ cout << "Conf " << Pkg.FullName(false) << " broken" << endl;
- Sim.Update();
-
- // Print out each package and the failed dependencies
- for (pkgCache::DepIterator D = Sim[Pkg].InstVerIter(Sim).DependsList(); D.end() == false; ++D)
+ Sim.Update();
+
+ // Print out each package and the failed dependencies
+ for (pkgCache::DepIterator D = Sim[Pkg].InstVerIter(Sim).DependsList(); D.end() == false; ++D)
+ {
+ if (Sim.IsImportantDep(D) == false ||
+ (Sim[D] & pkgDepCache::DepInstall) != 0)
+ continue;
+
+ if (D->Type == pkgCache::Dep::Obsoletes)
+ cout << " Obsoletes:" << D.TargetPkg().FullName(false);
+ else if (D->Type == pkgCache::Dep::Conflicts)
+ cout << " Conflicts:" << D.TargetPkg().FullName(false);
+ else if (D->Type == pkgCache::Dep::DpkgBreaks)
+ cout << " Breaks:" << D.TargetPkg().FullName(false);
+ else
+ cout << " Depends:" << D.TargetPkg().FullName(false);
+ }
+ cout << endl;
+
+ _error->Error("Conf Broken %s",Pkg.FullName(false).c_str());
+ }
+ else
{
- if (Sim.IsImportantDep(D) == false ||
- (Sim[D] & pkgDepCache::DepInstall) != 0)
- continue;
-
- if (D->Type == pkgCache::Dep::Obsoletes)
- cout << " Obsoletes:" << D.TargetPkg().FullName(false);
- else if (D->Type == pkgCache::Dep::Conflicts)
- cout << " Conflicts:" << D.TargetPkg().FullName(false);
- else if (D->Type == pkgCache::Dep::DpkgBreaks)
- cout << " Breaks:" << D.TargetPkg().FullName(false);
- else
- cout << " Depends:" << D.TargetPkg().FullName(false);
- }
- cout << endl;
+ cout << "Conf ";
+ Describe(Pkg,cout,false,true);
+ }
- _error->Error("Conf Broken %s",Pkg.FullName(false).c_str());
- }
- else
- {
- cout << "Conf ";
- Describe(Pkg,cout,false,true);
+ if (Sim.BrokenCount() != 0)
+ ShortBreaks();
+ else
+ cout << endl;
}
-
- if (Sim.BrokenCount() != 0)
- ShortBreaks();
- else
- cout << endl;
return true;
}
@@ -227,16 +300,26 @@ bool pkgSimulate::RealRemove(PkgIterator iPkg,bool Purge)
Flags[Pkg->ID] = 3;
Sim.MarkDelete(Pkg);
- if (Purge == true)
- cout << "Purg ";
- else
- cout << "Remv ";
- Describe(Pkg,cout,true,false);
+ if (_config->FindB("APT::Format::JSON", false)){
+ NSMutableDictionary *package = DescribeJSON(Pkg, true, false);
+ if (Purge == true)
+ package[@"Type"] = @"Purg";
+ else
+ package[@"Type"] = @"Remv";
- if (Sim.BrokenCount() != 0)
- ShortBreaks();
- else
- cout << endl;
+ [simulatedOperations addObject:package];
+ } else {
+ if (Purge == true)
+ cout << "Purg ";
+ else
+ cout << "Remv ";
+ Describe(Pkg,cout,true,false);
+
+ if (Sim.BrokenCount() != 0)
+ ShortBreaks();
+ else
+ cout << endl;
+ }
return true;
}
@@ -262,26 +345,33 @@ void pkgSimulate::ShortBreaks()
/*}}}*/
bool pkgSimulate::Go2(APT::Progress::PackageManager *) /*{{{*/
{
+ bool rv = true;
if (pkgDPkgPM::ExpandPendingCalls(d->List, Cache) == false)
return false;
+ BOOL doJSON = _config->FindB("APT::Format::JSON", false);
+ if (doJSON) {
+ simulatedOperations = [NSMutableArray array];
+ } else {
+ simulatedOperations = nil;
+ }
for (auto && I : d->List)
switch (I.Op)
{
case pkgDPkgPM::Item::Install:
if (RealInstall(I.Pkg, I.File) == false)
- return false;
+ goto failout;
break;
case pkgDPkgPM::Item::Configure:
if (RealConfigure(I.Pkg) == false)
- return false;
+ goto failout;
break;
case pkgDPkgPM::Item::Remove:
if (RealRemove(I.Pkg, false) == false)
- return false;
+ goto failout;
break;
case pkgDPkgPM::Item::Purge:
if (RealRemove(I.Pkg, true) == false)
- return false;
+ goto failout;
break;
case pkgDPkgPM::Item::ConfigurePending:
case pkgDPkgPM::Item::TriggersPending:
@@ -289,7 +379,18 @@ bool pkgSimulate::Go2(APT::Progress::PackageManager *) /*{{{*/
case pkgDPkgPM::Item::PurgePending:
return _error->Error("Internal error, simulation encountered unexpected pending item");
}
- return true;
+ if (false) {
+failout:
+ rv = false;
+ }
+ if (doJSON && simulatedOperations != nil) {
+ NSData *data = [NSJSONSerialization dataWithJSONObject:@{@"operations": simulatedOperations} options:NSJSONWritingPrettyPrinted error:nil];
+ simulatedOperations = nil;
+ NSFileHandle *stdout = [NSFileHandle fileHandleWithStandardOutput];
+ [stdout writeData:data];
+ cout << endl << flush;
+ }
+ return rv;
}
/*}}}*/
// ApplyStatus - Adjust for non-ok packages /*{{{*/
diff --git a/apt-private/CMakeLists.txt b/apt-private/CMakeLists.txt
index 88a8f97fe..4a6ea51b8 100644
--- a/apt-private/CMakeLists.txt
+++ b/apt-private/CMakeLists.txt
@@ -5,14 +5,14 @@ set(MINOR 0)
# Definition of the C++ files used to build the library - note that this
# is expanded at CMake time, so you have to rerun cmake if you add or remove
# a file (you can just run cmake . in the build directory)
-file(GLOB_RECURSE library "*.cc")
+file(GLOB_RECURSE library "*.cc" "*.mm")
file(GLOB_RECURSE headers "*.h")
# Create a library using the C++ files
add_library(apt-private SHARED ${library})
# Link the library and set the SONAME
-target_link_libraries(apt-private PUBLIC apt-pkg)
+target_link_libraries(apt-private PUBLIC apt-pkg "-framework Foundation")
set_target_properties(apt-private PROPERTIES VERSION ${MAJOR}.${MINOR})
set_target_properties(apt-private PROPERTIES SOVERSION ${MAJOR})
set_target_properties(apt-private PROPERTIES CXX_VISIBILITY_PRESET hidden)
diff --git a/apt-private/private-output.cc b/apt-private/private-output.mm
index eb9a34abe..2cf3dc656 100644
--- a/apt-private/private-output.cc
+++ b/apt-private/private-output.mm
@@ -1,6 +1,8 @@
// Include files /*{{{*/
#include <config.h>
+#include <Foundation/Foundation.h>
+
#include <apt-pkg/cachefile.h>
#include <apt-pkg/configuration.h>
#include <apt-pkg/depcache.h>
@@ -443,15 +445,150 @@ static void ShowBrokenPackage(ostream &out, pkgCacheFile * const Cache, pkgCache
}
}
}
+
+static NSMutableDictionary * BrokenPackage(ostream &out, pkgCacheFile * const Cache, pkgCache::PkgIterator const &Pkg, bool const Now)
+{
+ if (Now == true)
+ {
+ if ((*Cache)[Pkg].NowBroken() == false)
+ return nil;
+ }
+ else
+ {
+ if ((*Cache)[Pkg].InstBroken() == false)
+ return nil;
+ }
+
+ NSMutableDictionary *packages = [NSMutableDictionary dictionary];
+
+ // Array of depends for package
+ NSMutableArray *deps = [NSMutableArray array];
+
+ // Print out each package and the failed dependencies
+ packages[@(Pkg.FullName(true).c_str())] = deps;
+
+ pkgCache::VerIterator Ver;
+
+ if (Now == true)
+ Ver = Pkg.CurrentVer();
+ else
+ Ver = (*Cache)[Pkg].InstVerIter(*Cache);
+
+ if (Ver.end() == true)
+ {
+ return packages;
+ }
+
+ for (pkgCache::DepIterator D = Ver.DependsList(); D.end() == false;)
+ {
+ // Compute a single dependency element (glob or)
+ pkgCache::DepIterator Start;
+ pkgCache::DepIterator End;
+ D.GlobOr(Start,End); // advances D
+
+ if ((*Cache)->IsImportantDep(End) == false)
+ continue;
+
+ if (Now == true)
+ {
+ if (((*Cache)[End] & pkgDepCache::DepGNow) == pkgDepCache::DepGNow)
+ continue;
+ }
+ else
+ {
+ if (((*Cache)[End] & pkgDepCache::DepGInstall) == pkgDepCache::DepGInstall)
+ continue;
+ }
+
+ // Array of depends that can satisfy this depend
+ NSMutableArray *possibleDepends = [NSMutableArray array];
+ [deps addObject:possibleDepends];
+
+ while (1)
+ {
+
+ NSMutableDictionary *dependency = [NSMutableDictionary dictionary];
+ dependency[@"Type"] = @(End.DepType());
+ dependency[@"Package"] = @(Start.TargetPkg().FullName(true).c_str());
+
+ // Show a quick summary of the version requirements
+ if (Start.TargetVer() != 0) {
+ dependency[@"VersionSummary"] = [NSString stringWithFormat:@"%s %s", Start.CompType(), Start.TargetVer()];
+ dependency[@"CompType"] = @(Start.CompType());
+ dependency[@"TargetVer"] = @(Start.TargetVer());
+ }
+
+ /* Show a summary of the target package if possible. In the case
+ of virtual packages we show nothing */
+ pkgCache::PkgIterator Targ = Start.TargetPkg();
+ if (Targ->ProvidesList == 0)
+ {
+ pkgCache::VerIterator Ver = (*Cache)[Targ].InstVerIter(*Cache);
+ if (Now == true)
+ Ver = Targ.CurrentVer();
+
+ if (Ver.end() == false)
+ {
+ dependency[@"VerStr"] = @(Ver.VerStr());
+ if (Now == true)
+ dependency[@"Reason"] = [NSString stringWithFormat:@"%s is installed",Ver.VerStr()];
+ else
+ dependency[@"Reason"] = [NSString stringWithFormat:@"%s is to be installed",Ver.VerStr()];
+ }
+ else
+ {
+ if ((*Cache)[Targ].CandidateVerIter(*Cache).end() == true)
+ {
+ if (Targ->ProvidesList == 0)
+ dependency[@"Reason"] = @"it is not installable";
+ else
+ dependency[@"Reason"] = @"it is a virtual package";
+ }
+ else {
+ if (Now)
+ dependency[@"Reason"] = @"it is not installed";
+ else
+ dependency[@"Reason"] = @"it is not going to be installed";
+ }
+ }
+ }
+
+ [possibleDepends addObject:dependency];
+ if (Start == End)
+ break;
+ ++Start;
+ }
+ }
+ return packages;
+}
+
void ShowBroken(ostream &out, CacheFile &Cache, bool const Now)
{
if (Cache->BrokenCount() == 0)
return;
out << _("The following packages have unmet dependencies:") << endl;
+
+ bool useJSON = _config->FindB("APT::Format::JSON", false);
+
SortedPackageUniverse Universe(Cache);
- for (auto const &Pkg: Universe)
- ShowBrokenPackage(out, &Cache, Pkg, Now);
+
+ if (useJSON){
+ NSMutableDictionary *packages = [NSMutableDictionary dictionary];
+ for (auto const &Pkg: Universe){
+ NSMutableDictionary *package = BrokenPackage(out, &Cache, Pkg, Now);
+ [packages addEntriesFromDictionary:package];
+ }
+
+ NSData *data = [NSJSONSerialization dataWithJSONObject:@{@"brokenPackages": packages} options:NSJSONWritingPrettyPrinted error:nil];
+ NSFileHandle *stdout = [NSFileHandle fileHandleWithStandardOutput];
+ [stdout writeData:data];
+
+ cout << endl << flush;
+ } else {
+ for (auto const &Pkg: Universe)
+ ShowBrokenPackage(out, &Cache, Pkg, Now);
+ }
}
void ShowBroken(ostream &out, pkgCacheFile &Cache, bool const Now)
{
@@ -459,9 +596,27 @@ void ShowBroken(ostream &out, pkgCacheFile &Cache, bool const Now)
return;
out << _("The following packages have unmet dependencies:") << endl;
+
+ bool useJSON = _config->FindB("APT::Format::JSON", false);
+
APT::PackageUniverse Universe(Cache);
- for (auto const &Pkg: Universe)
- ShowBrokenPackage(out, &Cache, Pkg, Now);
+
+ if (useJSON){
+ NSMutableDictionary *packages = [NSMutableDictionary dictionary];
+ for (auto const &Pkg: Universe){
+ NSMutableDictionary *package = BrokenPackage(out, &Cache, Pkg, Now);
+ [packages addEntriesFromDictionary:package];
+ }
+
+ NSData *data = [NSJSONSerialization dataWithJSONObject:@{@"brokenPackages": packages} options:NSJSONWritingPrettyPrinted error:nil];
+ NSFileHandle *stdout = [NSFileHandle fileHandleWithStandardOutput];
+ [stdout writeData:data];
+
+ cout << endl << flush;
+ } else {
+ for (auto const &Pkg: Universe)
+ ShowBrokenPackage(out, &Cache, Pkg, Now);
+ }
}
/*}}}*/
// ShowNew - Show packages to newly install /*{{{*/