1 module reggae.range; 2 3 import reggae.build; 4 import reggae.options; 5 6 import std.range; 7 import std.algorithm; 8 import std.conv; 9 import std.exception; 10 11 @safe: 12 13 enum isTargetLike(T) = is(typeof(() { 14 auto target = T.init; 15 auto deps = target.dependencyTargets; 16 static assert(is(Unqual!(typeof(deps[0])) == Unqual!T)); 17 auto imps = target.implicitTargets; 18 static assert(is(Unqual!(typeof(imps[0])) == Unqual!T)); 19 if(target.isLeaf) {} 20 string cmd = target.shellCommand(Options()); 21 })); 22 23 24 static assert(isTargetLike!Target); 25 26 struct DepthFirst(T) if(isTargetLike!T) { 27 T[] targets; 28 29 this(T target) pure { 30 this.targets = depthFirstTargets(target); 31 } 32 33 T[] depthFirstTargets(T target) pure { 34 //if leaf, return 35 if(target.isLeaf) return target.shellCommand(Options()) is null ? [] : [target]; 36 37 //if not, add ourselves to the end to get depth-first 38 return reduce!((a, b) => a ~ depthFirstTargets(b))(typeof(return).init, target.dependencyTargets) ~ 39 reduce!((a, b) => a ~ depthFirstTargets(b))(typeof(return).init, target.implicitTargets) ~ 40 target; 41 } 42 43 T front() pure nothrow { 44 return targets.front; 45 } 46 47 void popFront() pure nothrow { 48 targets.popFront; 49 } 50 51 bool empty() pure nothrow { 52 return targets.empty; 53 } 54 55 static assert(isInputRange!DepthFirst); 56 } 57 58 auto depthFirst(T)(T target) pure { 59 return DepthFirst!T(target); 60 } 61 62 struct ByDepthLevel { 63 Target[][] targets; 64 65 this(Target target) pure { 66 this.targets = sortTargets(target); 67 } 68 69 auto front() pure nothrow { 70 return targets.front; 71 } 72 73 void popFront() pure nothrow { 74 targets.popFront; 75 } 76 77 bool empty() pure nothrow { 78 return targets.empty; 79 } 80 81 private Target[][] sortTargets(Target target) pure { 82 if(target.isLeaf) return []; 83 84 Target[][] targets = [[target]]; 85 rec(0, [target], targets); 86 return targets. 87 retro. 88 map!(a => 89 a.sort!((x, y) => x.rawOutputs < y.rawOutputs). 90 uniq!((x, y) => equal(x.rawOutputs, y.rawOutputs)).array). 91 array; 92 } 93 94 private void rec(int level, Target[] targets, ref Target[][] soFar) @trusted pure nothrow { 95 Target[] notLeaves = targets. 96 map!(a => chain(a.dependencyTargets, a.implicitTargets)). //get all dependencies 97 join. //flatten into a regular range 98 filter!(a => !a.isLeaf). //don't care about leaves 99 array; 100 if(notLeaves.empty) return; 101 102 soFar ~= notLeaves; 103 rec(level + 1, notLeaves, soFar); 104 } 105 106 static assert(isInputRange!ByDepthLevel); 107 } 108 109 struct Leaves { 110 this(in Target target) pure nothrow { 111 recurse(target); 112 } 113 114 const(Target) front() pure nothrow { 115 return targets.front; 116 } 117 118 void popFront() pure nothrow { 119 targets.popFront; 120 } 121 122 bool empty() pure nothrow { 123 return targets.empty; 124 } 125 126 127 private: 128 129 const(Target)[] targets; 130 131 void recurse(in Target target) pure nothrow { 132 if(target.isLeaf) { 133 targets ~= target; 134 return; 135 } 136 137 foreach(dep; target.dependencyTargets ~ target.implicitTargets) { 138 if(dep.isLeaf) { 139 targets ~= dep; 140 } else { 141 recurse(dep); 142 } 143 } 144 } 145 146 static assert(isInputRange!Leaves); 147 } 148 149 150 //TODO: a non-allocating version with no arrays 151 auto noSortUniq(R)(R range) if(isInputRange!R) { 152 ElementType!R[] ret; 153 foreach(elt; range) { 154 if(!ret.canFind(elt)) ret ~= elt; 155 } 156 return ret; 157 } 158 159 //removes duplicate targets from the build, presents a depth-first interface 160 //per top-level target 161 struct UniqueDepthFirst { 162 Build build; 163 private Target[] _targets; 164 165 this(Build build) pure @trusted { 166 _targets = build.targets. 167 map!depthFirst. 168 join. 169 noSortUniq. 170 array; 171 } 172 173 Target front() pure nothrow { 174 return _targets.front; 175 } 176 177 void popFront() pure nothrow { 178 _targets.popFront; 179 } 180 181 bool empty() pure nothrow { 182 return _targets.empty; 183 } 184 185 static assert(isInputRange!UniqueDepthFirst); 186 }