diff options
author | Sam Bingner <sam@bingner.com> | 2019-12-30 16:31:01 -1000 |
---|---|---|
committer | Sam Bingner <sam@bingner.com> | 2021-03-16 13:10:32 -1000 |
commit | d1027179026357c0db89c7b27dd8d774e34e574b (patch) | |
tree | 46fa7e9ec6831ad383f664541586a463c4181fa8 | |
parent | 9d8383bdee9f1ddc5685c36678b1ca033cbea971 (diff) |
Add support for JSON output
-rw-r--r-- | apt-pkg/CMakeLists.txt | 3 | ||||
-rw-r--r-- | apt-pkg/algorithms.h | 6 | ||||
-rw-r--r-- | apt-pkg/algorithms.mm (renamed from apt-pkg/algorithms.cc) | 193 | ||||
-rw-r--r-- | apt-private/CMakeLists.txt | 4 | ||||
-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 /*{{{*/ |