1 module sbylib.engine.tools.dscanner; 2 3 import std; 4 import sbylib.engine.util : importPath; 5 6 class DScanner { 7 static: 8 9 Tuple!(string[], SysTime)[string] importListMemo; 10 11 string[] importListRecursive(alias constraint = (string f) => true)(string file) { 12 string[] result = importList(file).filter!(constraint).array; 13 while (true) { 14 const n = result.length; 15 result = (result ~ result.map!(f => importList(f).filter!(constraint)).join).sort.uniq.array; 16 if (result.length == n) break; 17 } 18 return result; 19 } 20 21 string[] importList(string file) 22 out (r; r.all!(exists), r.to!string) 23 { 24 if (auto memo = file in importListMemo) { 25 if ((*memo)[1] == file.timeLastModified) { 26 return (*memo)[0]; 27 } 28 } 29 30 auto result = execute("-i " ~ file ~ " " ~ importPath.map!(p => "-I" ~ p).join(" ")) 31 .filter!(line => line.isValidPath) 32 .array; 33 34 importListMemo[file] = tuple(result, file.timeLastModified); 35 36 enum Head = "Could not resolve location of"; 37 auto errors = result.filter!(r => r.startsWith(Head)); 38 if (!errors.empty) { 39 throw new ResolveLocationException(format!"%s:\n%s\nimportPath=%s" 40 (Head, errors.map!(e => e.split(Head).back).join("\n"), 41 importPath.join("\n"))); 42 } 43 44 return result; 45 } 46 47 private string[] execute(string cmd) { 48 const result = executeShell("dscanner " ~ cmd); 49 if (result.status != 0) { 50 throw new Exception(result.output); 51 } 52 return result.output 53 .split("\n") 54 .filter!(s => s.length > 0) 55 .array; 56 } 57 } 58 59 class ResolveLocationException : Exception { 60 mixin basicExceptionCtors; 61 }