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 }