1 module mage.target; 2 import mage; 3 import std.variant; 4 import std.typecons : tuple; 5 public import std.typecons : Tuple; 6 7 8 //debug = PragmaMsg; 9 10 11 /// Base class for all targets. 12 abstract class Target 13 { 14 Properties _properties; 15 MagicContext context; 16 17 this() 18 { 19 properties.name = typeid(this).toString(); 20 21 // Useful for targets that only do some global config. 22 properties["type"] = "none"; 23 // TODO Source file properties. 24 } 25 26 @property ref inout(Properties) properties() inout { 27 return _properties; 28 } 29 30 mixin PropertiesOperators!_properties; 31 32 @property bool isExternal() const { return false; } 33 34 void configure() {} 35 36 override string toString() const 37 { 38 auto var = this["name"]; 39 if(var.hasValue()) 40 { 41 return (cast()var).get!string; 42 } 43 import std.conv : to; 44 return typeid(this).to!string(); 45 } 46 } 47 48 49 abstract class Executable : Target 50 { 51 this() { 52 properties["type"] = "executable"; 53 } 54 } 55 56 57 enum LibraryType 58 { 59 Static, 60 Shared 61 } 62 63 abstract class Library : Target 64 { 65 this(LibraryType libType) 66 { 67 this["type"] = "library"; 68 this["libType"] = libType; 69 } 70 } 71 72 abstract class StaticLibrary : Library 73 { 74 this() { super(LibraryType.Static); } 75 } 76 77 abstract class SharedLibrary : Library 78 { 79 this() { super(LibraryType.Shared); } 80 } 81 82 83 /// Used for third-party inclusion. 84 abstract class ExternalTarget : Target 85 { 86 final override @property bool isExternal() const { return true; } 87 } 88 89 90 /// Helper to add link targets. 91 void addLinkTarget(Target target, Target linkTarget) 92 { 93 if(linkTarget.tryGet("libType") is null) 94 { 95 log.error("Missing `libType' property; Cannot add `%s' as link target.", linkTarget); 96 return; 97 } 98 99 Target[] targets; 100 if(auto pValue = target.tryGet("linkTargets")) 101 { 102 targets = pValue.get!(Target[]); 103 } 104 targets ~= linkTarget; 105 log.trace("Link targets of `%s': %s", target, targets); 106 target["linkTargets"] = targets; 107 } 108 109 struct Config 110 { 111 auto properties = Properties("<anonymousConfig>"); 112 113 this(string name) { 114 this.name = name; 115 } 116 117 this(string name, string architecture) 118 { 119 this.name = name; 120 this.architecture = architecture; 121 } 122 123 @property string name() const { return (cast()this["name"]).get!string; } 124 @property void name(string theName) { this["name"] = theName; } 125 126 @property string architecture() const { return (cast()this["architecture"]).get!string; } 127 @property void architecture(string theArch) { this["architecture"] = theArch; } 128 129 mixin PropertiesOperators!properties; 130 131 string toString() const { 132 return "Config(`%s', `%s')".format(this.name, this.architecture); 133 } 134 } 135 136 bool isMatch(ref in Config cfg1, ref in Config cfg2) 137 { 138 return cfg1.name == cfg2.name && cfg1.architecture == cfg2.architecture; 139 } 140 141 auto matchingConfigurations(Config[] configs1, Config[] configs2) 142 { 143 Tuple!(Config*, Config*)[] result; 144 foreach(ref cfg; configs1) 145 { 146 foreach(ref otherCfg; configs2) 147 { 148 if(cfg.isMatch(otherCfg)) { 149 result ~= tuple(&cfg, &otherCfg); 150 } 151 } 152 } 153 return result; 154 } 155 156 auto matchingConfigurations(ref Environment env1, ref Environment env2) 157 { 158 auto configs1 = env1.first("configurations"); 159 auto configs2 = env2.first("configurations"); 160 return matchingConfigurations(configs1.get!(Config[]), configs2.get!(Config[])); 161 } 162 163 auto matchingConfigurations(Target t1, Target t2) 164 { 165 auto env1 = Environment("matchingConfigurations_t1", t1.properties); 166 auto env2 = Environment("matchingConfigurations_t2", t2.properties); 167 return matchingConfigurations(env1, env2); 168 } 169 170 unittest 171 { 172 import std.range; 173 174 Config[] l; 175 l.length = 3; 176 l[0].name = "a"; 177 l[0].architecture = "x86"; 178 l[1].name = "b"; 179 l[1].architecture = "x86"; 180 l[2].name = "c"; 181 l[2].architecture = "x86"; 182 183 Config[] r; 184 r.length = 3; 185 r[0].name = "c"; 186 r[0].architecture = "x86"; 187 r[1].name = "z"; 188 r[1].architecture = "x86"; 189 r[2].name = "a"; 190 r[2].architecture = "x86"; 191 192 auto matches = matchingConfigurations(l, r); 193 assert(matches.length == 2); 194 assert(matches.front[0].name == "a"); 195 assert(matches.front[1].name == "a"); 196 matches.popFront(); 197 assert(matches.front[0].name == "c"); 198 assert(matches.front[1].name == "c"); 199 } 200 201 202 /// User-defined Attribute (UDA) to decorate a dependency field with. 203 struct Dependency 204 { 205 } 206 207 208 interface ITargetWrapper 209 { 210 abstract @property Path filePath() const; 211 abstract @property string targetName() const; 212 abstract @property const(TypeInfo) wrappedTypeInfo() const; 213 abstract @property const(TypeInfo)[] dependencies() const; 214 abstract void setDependencyInstance(Target target, Target dependency); 215 abstract Target create(); 216 } 217 218 __gshared ITargetWrapper[] wrappedTargets; 219 220 class TargetWrapper(TargetType) : ITargetWrapper 221 { 222 import std.stdio; 223 private: 224 alias DependencySetter = void function(Target, Target); 225 226 Path _filePath; 227 DependencySetter[TypeInfo] _dependencies; 228 229 public: 230 override @property Path filePath() const { return _filePath; } 231 override @property string targetName() const { return TargetType.stringof; } 232 override @property const(TypeInfo) wrappedTypeInfo() const { return typeid(TargetType); } 233 override @property const(TypeInfo)[] dependencies() const { return _dependencies.keys; } 234 override void setDependencyInstance(Target target, Target dependency) 235 { 236 log.info("Finding %s...", typeid(dependency)); 237 _dependencies[typeid(dependency)](target, dependency); 238 } 239 240 this(Path filePath) { 241 import mage.util.reflection; 242 _filePath = filePath; 243 debug(PragmaMsg) { pragma(msg, `[mage] Scanning Target "` ~ TargetType.stringof ~ `" members for dependencies.`); } 244 foreach(m; __traits(allMembers, TargetType)) 245 { 246 debug(PragmaMsg) { pragma(msg, "[mage] Member: " ~ m); } 247 alias Member = Resolve!(__traits(getMember, TargetType, m)); 248 static if(__traits(compiles, typeof(Member))) 249 { 250 foreach(uda; __traits(getAttributes, Member)) 251 { 252 debug(PragmaMsg) { pragma(msg, "[mage] UDA: " ~ uda.stringof); } 253 if(is(uda == Dependency)) 254 { 255 static assert(is(typeof(Member) : Target), `Only "Target" types may be decorated with "@Dependency".`); 256 debug(PragmaMsg) { pragma(msg, "[mage] +++ Collecting dependency: " ~ typeof(Member).stringof); } 257 this._dependencies[typeid(typeof(Member))] = (t, d) // Setter for the member dependency instances 258 { 259 mixin("(cast(TargetType)t)." ~ m ~ " = cast(typeof(TargetType." ~ m ~ "))d;"); 260 }; 261 } 262 } 263 } 264 } 265 } 266 267 override Target create() { 268 return new TargetType(); 269 } 270 } 271 272 mixin template registerMageFile(alias T, alias filePath) 273 { 274 shared static this() 275 { 276 import mage.util.reflection; 277 debug(PragmaMsg) { pragma(msg, "[mage] Reflecting module: " ~ T.stringof); } 278 foreach(m; __traits(allMembers, T)) 279 { 280 debug(PragmaMsg) { pragma(msg, "[mage] Checking member: " ~ m); } 281 static if(!__traits(compiles, typeof(__traits(getMember, T, m)))) 282 { 283 debug(PragmaMsg) { pragma(msg, "[mage] Got one further!"); } 284 static if(__traits(compiles, ResolveType!(__traits(getMember, T, m)))) 285 { 286 debug(PragmaMsg) { pragma(msg, "[mage] One more down!"); } 287 alias Type = ResolveType!(__traits(getMember, T, m)); 288 static if(is(Type : Target)) 289 { 290 debug(PragmaMsg) { pragma(msg, "[mage] Found a target! " ~ Type.stringof); } 291 static if(!__traits(compiles, new Type())) { 292 debug(PragmaMsg) { pragma(msg, "[mage] WARNING: Target is not instantiable with `new`."); } 293 } 294 log.info("Wrapping " ~ Type.stringof); 295 wrappedTargets ~= new TargetWrapper!Type(Path(filePath)); 296 } 297 } 298 } 299 } 300 } 301 } 302 303 mixin template M_MageFileMixin() 304 { 305 class MageFileInstance{} 306 307 // The parent of MageFileInstance is the module. 308 mixin registerMageFile!(__traits(parent, MageFileInstance), M_mageFilePath); 309 }