1 /**
2  * Copyright: Copyright Jason White, 2016
3  * License:   MIT
4  * Authors:   Jason White
5  *
6  * Description:
7  * This is the root command handler. That is, this decides which command handler
8  * to use.
9  */
10 module button.handler;
11 
12 import button.resource;
13 import button.context;
14 
15 import button.handlers;
16 import button.command;
17 import button.task;
18 
19 alias Handler = void function(
20         ref BuildContext ctx,
21         const(string)[] args,
22         string workDir,
23         ref Resources inputs,
24         ref Resources outputs
25         );
26 
27 immutable Handler[string] handlers;
28 shared static this()
29 {
30     handlers = [
31         "button": &recursive,
32         "button-lua": &base,
33         "dmd": &dmd,
34         "gcc": &gcc,
35         "g++": &gcc,
36         "c++": &gcc,
37     ];
38 }
39 
40 /**
41  * Returns a handler appropriate for the given arguments.
42  *
43  * In general, this simply looks at the base name of the first argument and
44  * determines the tool based on that.
45  */
46 Handler selectHandler(const(string)[] args)
47 {
48     import std.uni : toLower;
49     import std.path : baseName, filenameCmp;
50 
51     if (args.length)
52     {
53         auto name = baseName(args[0]);
54 
55         // Need case-insensitive comparison on Windows.
56         version (Windows)
57             name = name.toLower;
58 
59         if (auto p = name in handlers)
60             return *p;
61     }
62 
63     return &tracer;
64 }
65 
66 void execute(
67         ref BuildContext ctx,
68         const(string)[] args,
69         string workDir,
70         ref Resources inputs,
71         ref Resources outputs
72         )
73 {
74     auto handler = selectHandler(args);
75 
76     handler(ctx, args, workDir, inputs, outputs);
77 }
78 
79 /**
80  * Executes the task.
81  */
82 Task.Result execute(const Task task, ref BuildContext ctx)
83 {
84     import std.array : appender;
85 
86     // FIXME: Use a set instead?
87     auto inputs  = appender!(Resource[]);
88     auto outputs = appender!(Resource[]);
89 
90     foreach (command; task.commands)
91     {
92         auto result = command.execute(ctx, task.workingDirectory);
93 
94         // FIXME: Commands may have temporary inputs and outputs. For
95         // example, if one command creates a file and a later command
96         // deletes it, it should not end up in either of the input or output
97         // sets.
98         inputs.put(result.inputs);
99         outputs.put(result.outputs);
100     }
101 
102     return Task.Result(inputs.data, outputs.data);
103 }
104 
105 /**
106  * Executes the command.
107  */
108 Command.Result execute(const Command command, ref BuildContext ctx,
109     string workDir)
110 {
111     import std.path : buildPath;
112     import std.datetime.stopwatch : StopWatch, AutoStart;
113     import button.handler : executeHandler = execute;
114 
115     auto inputs  = Resources(ctx.root, workDir);
116     auto outputs = Resources(ctx.root, workDir);
117 
118     auto sw = StopWatch(AutoStart.yes);
119 
120     executeHandler(
121             ctx,
122             command.args,
123             buildPath(ctx.root, workDir),
124             inputs, outputs
125             );
126 
127     return Command.Result(inputs.data, outputs.data, sw.peek());
128 }