A tool that tries to build every Dub package against a wide range of DMD versions.

core.d 5.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. module backporter.core;
  2. import core.time;
  3. import d2sqlite3;
  4. import std.stdio : File;
  5. import std.datetime.systime : SysTime;
  6. // TODO: rename this class 'God'
  7. class Config
  8. {
  9. this()
  10. {
  11. import std.path : absolutePath;
  12. outpath = "results.sqlite".absolutePath;
  13. dataDir = "projects".absolutePath;
  14. }
  15. string outpath;
  16. string dataDir;
  17. string compilerRelease = "2.080.0";
  18. string dubLoc;
  19. string dmdLoc;
  20. string fallbackDub;
  21. string[] blacklistedProjects;
  22. Duration buildTimeout = 5.minutes;
  23. bool useDocker = true;
  24. ulong revisionCount = 3;
  25. void init()
  26. {
  27. import std.path : absolutePath;
  28. db = Database(outpath.absolutePath);
  29. db.run(`CREATE TABLE IF NOT EXISTS builds
  30. (
  31. time TEXT PRIMARY KEY,
  32. compilerRelease TEXT NOT NULL,
  33. packageName TEXT NOT NULL,
  34. revisionId TEXT NOT NULL,
  35. skipped BOOLEAN NOT NULL,
  36. canCheckOut BOOLEAN NOT NULL,
  37. canBuild BOOLEAN NOT NULL,
  38. canTest BOOLEAN NOT NULL,
  39. hasDeprecations BOOLEAN NOT NULL
  40. )`);
  41. db.run(`CREATE TABLE IF NOT EXISTS revisions
  42. (
  43. packageName TEXT NOT NULL,
  44. revisionId TEXT NOT NULL,
  45. gitUrl TEXT NOT NULL,
  46. commitId TEXT NOT NULL,
  47. lastBuilt TEXT NULL,
  48. PRIMARY KEY (packageName, revisionId)
  49. )`);
  50. addRecord = db.prepare(`
  51. INSERT INTO builds
  52. (time, compilerRelease, packageName, revisionId, skipped, canCheckOut, canBuild,
  53. canTest, hasDeprecations)
  54. VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
  55. `);
  56. updateBuildTime = db.prepare(`
  57. UPDATE revisions
  58. SET lastBuilt = $3
  59. WHERE packageName = $1 AND revisionId = $2
  60. `);
  61. nextPackages = db.prepare(`
  62. SELECT * FROM revisions
  63. ORDER BY lastBuilt ASC, packageName DESC, revisionId DESC
  64. LIMIT ?
  65. `);
  66. addPackage = db.prepare(`
  67. INSERT INTO revisions
  68. (packageName, revisionId, gitUrl, commitId, lastBuilt)
  69. VALUES
  70. (?, ?, ?, ?, ?)
  71. `);
  72. nextPackageSingle = db.prepare(`
  73. SELECT * FROM revisions
  74. WHERE NOT EXISTS
  75. (
  76. SELECT * FROM builds
  77. WHERE compilerRelease = ?
  78. AND packageName = revisions.packageName
  79. AND revisionId = revisions.revisionId
  80. )
  81. ORDER BY revisionId DESC
  82. LIMIT 1
  83. `);
  84. remainingPackageCount = db.prepare(`
  85. SELECT COUNT(*) FROM revisions
  86. WHERE NOT EXISTS
  87. (
  88. SELECT * FROM builds
  89. WHERE compilerRelease = ?
  90. AND packageName = revisions.packageName
  91. AND revisionId = revisions.revisionId
  92. )
  93. `);
  94. {
  95. import scriptlike;
  96. fallbackDub = runCollect("which dub").strip;
  97. }
  98. }
  99. ~this()
  100. {
  101. db.close;
  102. }
  103. void record(Build result)
  104. {
  105. addRecord.inject(result);
  106. updateBuildTime.inject(result.packageName, result.revisionId, result.time);
  107. }
  108. void importPackages(PackageRevision[] revisions)
  109. {
  110. db.run("begin");
  111. scope (exit) db.run("commit");
  112. db.run("delete from revisions");
  113. foreach (rev; revisions)
  114. {
  115. addPackage.inject(rev);
  116. }
  117. }
  118. PackageRevision nextPackage(string compilerRelease)
  119. {
  120. nextPackageSingle.reset;
  121. nextPackageSingle.bind(1, compilerRelease);
  122. auto result = nextPackageSingle.execute;
  123. if (result.empty) return PackageRevision.init;
  124. return result.front.as!PackageRevision;
  125. }
  126. PackageRevision[] fetchPackages(uint count)
  127. {
  128. import std.algorithm.iteration : map;
  129. import std.array : array;
  130. nextPackages.reset;
  131. nextPackages.bind(1, count);
  132. auto result = nextPackages.execute;
  133. return result.map!(x => x.as!PackageRevision).array;
  134. }
  135. ulong countRemainingPackages(string compilerRelease)
  136. {
  137. remainingPackageCount.reset;
  138. remainingPackageCount.bind(1, compilerRelease);
  139. return remainingPackageCount.execute.oneValue!ulong;
  140. }
  141. private:
  142. File _outfile;
  143. Database db;
  144. Statement addRecord;
  145. Statement nextPackages;
  146. Statement updateBuildTime;
  147. Statement addPackage;
  148. Statement nextPackageSingle;
  149. Statement remainingPackageCount;
  150. }
  151. struct PackageRevision
  152. {
  153. string packageName;
  154. string revisionId;
  155. string gitUrl;
  156. string commitId;
  157. string lastBuilt; // ISO timestamp
  158. }
  159. struct Build
  160. {
  161. /// When this build happened.
  162. string time;
  163. /// The compiler version we used.
  164. string compilerRelease;
  165. /// The package that we built.
  166. string packageName;
  167. /// The ID of the revision that we built.
  168. string revisionId;
  169. /// Whether we skipped this for some reason.
  170. bool skipped;
  171. /// Whether we could check this package out.
  172. bool canCheckOut;
  173. /// Whether we could build this package.
  174. bool canBuild;
  175. /// Whether the tests passed.
  176. bool canTest;
  177. /// Whether there's any deprecated code in this.
  178. bool hasDeprecations;
  179. }