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