1 module reggae.dub.interop.fetch; 2 3 4 import reggae.from; 5 6 7 package void dubFetch(O)( 8 auto ref O output, 9 ref from!"reggae.dub.interop.dublib".Dub dub, 10 in from!"reggae.options".Options options, 11 in string dubSelectionsJson) 12 @trusted 13 { 14 import reggae.dub.interop.exec: callDub, dubEnvArgs; 15 import reggae.io: log; 16 import std.array: replace; 17 import std.json: parseJSON, JSONType; 18 import std.file: readText; 19 import std.parallelism: parallel; 20 21 const(VersionedPackage)[] pkgsToFetch; 22 23 const json = parseJSON(readText(dubSelectionsJson)); 24 25 foreach(dubPackageName, versionJson; json["versions"].object) { 26 27 // skip the ones with a defined path 28 if(versionJson.type != JSONType..string) continue; 29 30 // versions are usually `==1.2.3`, so strip the equals sign 31 const version_ = versionJson.str.replace("==", ""); 32 const pkg = VersionedPackage(dubPackageName, version_); 33 34 if(needDubFetch(dub, pkg)) pkgsToFetch ~= pkg; 35 } 36 37 output.log("Fetching dub packages"); 38 foreach(pkg; pkgsToFetch.parallel) { 39 const cmd = ["dub", "fetch", pkg.name, "--version=" ~ pkg.version_] ~ dubEnvArgs; 40 callDub(output, options, cmd); 41 } 42 43 output.log("Reloading project"); 44 dub.reinit; 45 output.log("Project reloaded"); 46 } 47 48 49 private struct VersionedPackage { 50 string name; 51 string version_; 52 } 53 54 55 private bool needDubFetch( 56 ref from!"reggae.dub.interop.dublib".Dub dub, 57 in VersionedPackage pkg) 58 @safe 59 { 60 // first check the file system explicitly 61 if(pkgExistsOnFS(pkg)) return false; 62 // next ask dub (this is slower) 63 if(dub.getPackage(pkg.name, pkg.version_)) return false; 64 65 return true; 66 } 67 68 69 // dub fetch can sometimes take >10s (!) despite the package already being 70 // on disk 71 private bool pkgExistsOnFS(in VersionedPackage pkg) @safe { 72 import reggae.path: dubPackagesDir; 73 import std.path: buildPath; 74 import std.file: exists; 75 import std.string: replace; 76 77 // Some versions include a `+` and that becomes `_` in the path 78 const version_ = pkg.version_.replace("+", "_"); 79 80 return buildPath( 81 dubPackagesDir, 82 pkg.name ~ "-" ~ version_, 83 pkg.name ~ ".lock" 84 ).exists; 85 }