#include "umake.h" struct UppHubNest : Moveable { int tier = -1; String name; Vector packages; String description; String repo; String website; String status = "unknown"; String category; String list_name; String readme; String branch; }; struct UppHubDlg { VectorMap upv; Index loaded; Value LoadJson(const String& url); void Load(int tier, const String& url); void Load(); void Install(const Index& ii, bool update = false); }; Value UppHubDlg::LoadJson(const String& url) { String s = LoadFile(url); if(IsNull(s)) { PutVerbose("Fetching UppHub metadata from " << url); #ifdef PLATFORM_POSIX s = Sys("curl --silent " + url); if (IsNull(s)) { String msg = "Failed to execute UppHub download nests request."; return ErrorValue(msg); } #else HttpRequest r(url); r.Execute(); if (!r.IsSuccess()) { String msg = "Failed to execute UppHub download nests request with error code " + IntStr(r.GetStatusCode()) + "."; return ErrorValue(msg); } s = r.GetContent(); #endif } Value v = ParseJSON(s); if(v.IsError()) { s.Replace(""", "\""); s.Replace("&", "&"); v = ParseJSON(s); if (v.IsError()) { String msg = "Failed to parse Json file."; return ErrorValue(msg); } } return v; } void UppHubDlg::Load(int tier, const String& url) { if(loaded.Find(url) >= 0) return; loaded.Add(url); Value v = LoadJson(url); if (v.IsError()) { auto error_text = GetErrorText(v); if (!error_text.IsEmpty()) { PutConsole("Failed to load nests file with error \"" + GetErrorText(v) + "\"."); } return; } try { String list_name = v["name"]; for(Value ns : v["nests"]) { String url = ns["url"]; if(url.GetCount()) ns = LoadJson(url); String name = ns["name"]; UppHubNest& n = upv.GetAdd(name); n.name = name; bool tt = tier > n.tier; if(tt || n.packages.GetCount() == 0) for(Value p : ns["packages"]) n.packages.Add(p); auto Attr = [&](String& a, const char *id) { if(tt || IsNull(a)) a = ns[id]; }; Attr(n.description, "description"); Attr(n.repo, "repository"); Attr(n.website, "website"); if(IsNull(n.website)) n.website = TrimRight(".git", n.repo); Attr(n.category, "category"); Attr(n.status, "status"); Attr(n.readme, "readme"); Attr(n.branch, "branch"); n.list_name = list_name; } for(Value l : v["links"]) { Load(tier + 1, l); } } catch(ValueTypeError) {} } void UppHubDlg::Load() { String url = Nvl(LoadFile(ConfigFile("upphub_root")), (String)"https://raw.githubusercontent.com/ultimatepp/UppHub/main/nests.json"); Load(0, url); } void UppHubDlg::Install(const Index& ii_, bool update) { Index ii = clone(ii_); if(ii.GetCount()) { int i = 0; while(i < ii.GetCount()) { String ns = ii[i++]; UppHubNest *n = upv.FindPtr(ns); if(n) { String dir = GetHubDir() + '/' + n->name; if(!DirectoryExists(dir)) { String cmd = GetGitPath() + " clone "; if(n->branch.GetCount()) cmd << "-b " + n->branch << ' '; cmd << n->repo; cmd << ' ' << dir; PutConsole(cmd); system(cmd); for(String p : FindAllPaths(dir, "*.upp")) { Package pkg; pkg.Load(p); for(const auto& u : pkg.uses) for(const UppHubNest& n : upv) for(const String& p : n.packages) if(u.text == p) { ii.FindAdd(n.name); break; } } } else if (update) { String cmd = GetGitPath() + " -C "; cmd << dir << " clean -fxd"; PutConsole(cmd); system(cmd); cmd = GetGitPath() + " -C "; cmd << dir << " pull"; PutConsole(cmd); system(cmd); } } } InvalidatePackageCache(); } ResetBlitz(); } bool UppHubAuto(const String& main) { Index pmissing; for(;;) { Workspace wspc; wspc.Scan(main); Index missing; for(int i = 0; i < wspc.GetCount(); i++) { String p = wspc[i]; if(!FileExists(PackageFile(p))) missing.FindAdd(p); } if(missing.GetCount() == 0) return true; UppHubDlg dlg; dlg.Load(); Index found; for(const UppHubNest& n : dlg.upv) for(const String& p : n.packages) if(missing.Find(p) >= 0) found.FindAdd(n.name); if(missing != pmissing) { dlg.Install(found); pmissing = clone(missing); continue; } return false; } return true; } void UppHubUpdate(const String& main) { UppHubDlg dlg; dlg.Load(); Workspace wspc; wspc.Scan(main); Index packages; for(int i = 0; i < wspc.GetCount(); i++) { String pkg = wspc[i]; for(const UppHubNest& n : dlg.upv) for(const String& p : n.packages) { if (pkg == p && pkg != main) packages.FindAdd(n.name); } } dlg.Install(packages, true); } void UppHubSetupDirForUmk(const String& hub_dir, const bool auto_hub) { if(!hub_dir.IsEmpty()) { OverrideHubDir(hub_dir); return; } if(auto_hub) { // NOTE: In auto_hub mode we don't want to change default directory or reuse these // belonging to umk or ide. DeleteFolderDeep(GetHubDir()); return; } String cfgdir = GetFileFolder(GetFileFolder(ConfigFile("x"))); for(const char *q : { "umk", "theide", "ide" }) { String dir = cfgdir + "/" + q + "/UppHub"; if(DirectoryExists(dir)) { for(FindFile ff(dir + "/*"); ff; ff.Next()) if(ff.IsFolder() && *ff.GetName() != '.') { OverrideHubDir(dir); return; } } } }