diff --git a/source/dub/commandline.d b/source/dub/commandline.d index cc26a77..a23f786 100644 --- a/source/dub/commandline.d +++ b/source/dub/commandline.d @@ -194,7 +194,10 @@ } // execute the command - try return cmd.execute(dub, remaining_args, app_args); + int rc; + try { + rc = cmd.execute(dub, remaining_args, app_args); + } catch (UsageException e) { logError("%s", e.msg); logDiagnostic("Full exception: %s", e.toString().sanitize); @@ -206,6 +209,10 @@ logDiagnostic("Full exception: %s", e.toString().sanitize); return 2; } + + if (!cmd.skipDubInitialization) + dub.shutdown(); + return rc; } class CommandArgs { diff --git a/source/dub/dub.d b/source/dub/dub.d index a976cf8..272ae21 100644 --- a/source/dub/dub.d +++ b/source/dub/dub.d @@ -105,6 +105,10 @@ .array; ps ~= defaultPackageSuppliers(); + auto cacheDir = m_userDubPath ~ "cache/"; + foreach (p; ps) + p.loadCache(cacheDir); + m_packageSuppliers = ps; m_packageManager = new PackageManager(m_userDubPath, m_systemDubPath); updatePackageSearchPath(); @@ -118,6 +122,14 @@ updatePackageSearchPath(); } + /// Perform cleanup and persist caches to disk + void shutdown() + { + auto cacheDir = m_userDubPath ~ "cache/"; + foreach (p; m_packageSuppliers) + p.storeCache(cacheDir); + } + @property void dryRun(bool v) { m_dryRun = v; } /** Returns the root path (usually the current working directory). diff --git a/source/dub/packagesupplier.d b/source/dub/packagesupplier.d index fe65b32..2f6ae81 100644 --- a/source/dub/packagesupplier.d +++ b/source/dub/packagesupplier.d @@ -38,6 +38,12 @@ /// returns the metadata for the package Json getPackageDescription(string packageId, Dependency dep, bool pre_release); + + /// load caches to disk + void loadCache(Path cacheDir); + + /// persist caches to disk + void storeCache(Path cacheDir); } class FileSystemPackageSupplier : PackageSupplier { @@ -79,6 +85,12 @@ return jsonFromZip(filename, "dub.json"); } + void storeCache(Path cacheDir) { + } + + void loadCache(Path cacheDir) { + } + private Path bestPackageFile(string packageId, Dependency dep, bool pre_release) { Path toPath(Version ver) { @@ -102,6 +114,7 @@ struct CacheEntry { Json data; SysTime cacheTime; } CacheEntry[string] m_metadataCache; Duration m_maxCacheTime; + bool m_metadataCacheDirty; } this(URL registry) @@ -139,12 +152,42 @@ return getBestPackage(packageId, dep, pre_release); } + void storeCache(Path cacheDir) + { + if (!m_metadataCacheDirty) return; + + auto path = cacheDir ~ cacheFileName; + if (!cacheDir.existsFile()) + mkdirRecurse(cacheDir.toNativeString()); + // TODO: method is slow due to Json escaping + writeJsonFile(path, m_metadataCache.serializeToJson()); + m_metadataCacheDirty = false; + } + + void loadCache(Path cacheDir) + { + auto path = cacheDir ~ cacheFileName; + if (!path.existsFile()) return; + + deserializeJson(m_metadataCache, jsonFromFile(path)); + m_metadataCacheDirty = false; + } + + private @property string cacheFileName() + { + import std.digest.md; + auto hash = m_registryUrl.toString.md5Of(); + return m_registryUrl.host ~ hash[0 .. $/2].toHexString().idup ~ ".json"; + } + private Json getMetadata(string packageId) { auto now = Clock.currTime(UTC()); if (auto pentry = packageId in m_metadataCache) { if (pentry.cacheTime + m_maxCacheTime > now) return pentry.data; + m_metadataCache.remove(packageId); + m_metadataCacheDirty = true; } auto url = m_registryUrl ~ Path(PackagesPath ~ "/" ~ packageId ~ ".json"); @@ -154,7 +197,11 @@ auto jsonData = cast(string)download(url); Json json = parseJsonString(jsonData); + // strip readme data (to save size and time) + foreach (ref v; json["versions"]) + v.remove("readme"); m_metadataCache[packageId] = CacheEntry(json, now); + m_metadataCacheDirty = true; return json; }