1 module wand;
2 
3 import mage;
4 import std.conv : to;
5 import std.uni : isWhite;
6 import std.algorithm : strip;
7 
8 debug = ShuffleTargets;
9 debug = LogResolveDeps;
10 
11 struct MageConfig
12 {
13   Path sourceRootPath;
14   GeneratorConfig[] genConfigs;
15 }
16 
17 struct GeneratorConfig
18 {
19   string name;
20 }
21 
22 auto readMageConfig(in Path p) {
23   MageConfig cfg;
24   auto lines = p.open().byLine;
25   cfg.sourceRootPath = Path(cast(string)lines.front);
26   lines.popFront();
27   foreach(line; lines) {
28     GeneratorConfig gen;
29     gen.name = cast(string)line.strip!(a => a.isWhite);
30     cfg.genConfigs ~= gen;
31   }
32   return cfg;
33 }
34 
35 const(TypeInfo)[] targetOrder(ITargetWrapper[] targets)
36 {
37   debug(ShuffleTargets)
38   {
39     import std.random;
40     randomShuffle(targets);
41   }
42   debug(LogResolveDeps) log.info("Original Target Order: %-(\n       %s%)", targets.map!(a => a.wrappedTypeInfo));
43 
44   const(TypeInfo)[] queue;
45   void helper(ref const(TypeInfo)[] queue, const(ITargetWrapper) wrapper)
46   {
47     auto _begin = log.Block("%s", wrapper.targetName);
48     debug(LogResolveDeps) log.info("Deps: %s", wrapper.dependencies);
49     foreach(dep; wrapper.dependencies)
50     {
51       helper(queue, targets.find!(a => a.wrappedTypeInfo == dep)[0]);
52     }
53     if(!queue.canFind!(a => a == wrapper.wrappedTypeInfo)) {
54       queue ~= wrapper.wrappedTypeInfo;
55       debug(LogResolveDeps) log.info("[add]");
56     }
57     else
58     {
59       debug(LogResolveDeps) log.info("[skip]");
60     }
61   }
62   foreach(wrapper; targets)
63   {
64     helper(queue, wrapper);
65   }
66 
67   debug(LogResolveDeps) log.info("Sorted Target Type Infos (Queue):%-(\n       %s%)", queue);
68 
69   return queue;
70 }
71 
72 // Is expected to be run in the `temp` dir that `mage` created.
73 int main(string[] args)
74 {
75   log.info("Running wand.");
76   Target[] targets;
77 
78   auto order = targetOrder(wrappedTargets);
79 
80   auto context = new MagicContext();
81   foreach(ti; order) {
82     auto wrapper = wrappedTargets.find!(a => a.wrappedTypeInfo == ti)[0];
83     auto _chdir = ScopedChdir(wrapper.filePath.parent);
84     auto _block = log.Block(`Creating target %s`, wrapper.targetName);
85 
86     auto target = wrapper.create();
87     target.properties["name"] = wrapper.targetName;
88     target.properties["mageFilePath"] = wrapper.filePath;
89     target.properties["dependencies"] = wrapper.dependencies.map!(a => targets.find!(t => a == typeid(t))[0]).array;
90     log.info("Target deps: %s", wrapper.dependencies);
91     target.context = context;
92     targets ~= target;
93   }
94 
95   with(log.forcedBlock("Set Dependency instances"))
96   {
97     foreach(wrapper; wrappedTargets) {
98       auto target = targets.find!(a => typeid(a) == wrapper.wrappedTypeInfo)[0];
99       foreach(dep; wrapper.dependencies) {
100         auto dependentTarget = targets.find!(a => typeid(a) == dep)[0];
101         wrapper.setDependencyInstance(target, dependentTarget);
102       }
103     }
104   }
105 
106   with(log.forcedBlock("Configure Targets"))
107   {
108     foreach(target; targets)
109     {
110       auto _chdir = ScopedChdir(target.properties["mageFilePath"].get!Path.parent);
111       target.configure();
112     }
113   }
114 
115   // Iterate all configured generators and pass all targets.`
116   auto mageCfg = readMageConfig(cwd() ~ "mage.cfg");
117   G["sourceRootPath"] = mageCfg.sourceRootPath;
118   foreach(cfg; mageCfg.genConfigs) {
119     log.info(`Generator "%s"`, cfg.name);
120     
121     auto path = Path(cfg.name);
122     if(!path.exists) {
123       path.mkdir(true);
124     }
125     with(ScopedChdir(path)) {
126       G["genRootPath"] = cwd();
127       generatorRegistry[cfg.name].generate(context, targets);
128     }
129   }
130 
131   return 0;
132 }