1 module utils.processutils; 2 3 import dsh; 4 5 /** 6 * Helper for more easily creating processes. 7 */ 8 public class ProcessBuilder { 9 private File stdin; 10 private File stdout; 11 private File stderr; 12 private string[string] env; 13 private string dir; 14 15 public this() { 16 this.stdin = std.stdio.stdin; 17 this.stdout = std.stdio.stdout; 18 this.stderr = std.stdio.stderr; 19 this.dir = getcwd(); 20 } 21 22 public ProcessBuilder inputFrom(string filename) { 23 this.stdin = File(filename, "r"); 24 return this; 25 } 26 27 public ProcessBuilder outputTo(string filename) { 28 this.stdout = File(filename, "w"); 29 return this; 30 } 31 32 public ProcessBuilder errorTo(string filename) { 33 this.stderr = File(filename, "w"); 34 return this; 35 } 36 37 public ProcessBuilder withEnv(string key, string value) { 38 this.env[key] = value; 39 return this; 40 } 41 42 public ProcessBuilder workingDir(string dir) { 43 this.dir = dir; 44 return this; 45 } 46 47 public int run(string command) { 48 import std.process; 49 import std.regex; 50 try { 51 auto r = regex("\\s+"); 52 auto s = split(command, r); 53 auto pid = spawnProcess(s, this.stdin, this.stdout, this.stderr, this.env, Config.none, this.dir); 54 return wait(pid); 55 } catch (ProcessException e) { 56 error("Could not start process \"%s\": %s", command, e.msg); 57 return -1; 58 } 59 } 60 } 61 62 unittest { 63 auto p = new ProcessBuilder() 64 .outputTo(".test.txt") 65 .workingDir("source"); 66 p.run("ls"); 67 68 } 69 70 /** 71 * Runs the given command using the user's shell. 72 * Params: 73 * cmd = The command to execute. 74 * Returns: The exit code from the command. Generally, 0 indicates success. If 75 * the process could not be started, -1 is returned. 76 */ 77 public int run(string cmd) { 78 return new ProcessBuilder().run(cmd); 79 } 80 81 unittest { 82 print("Running test of run(cmd). Expect some test output!"); 83 version(Posix) { 84 assert(run("ls") == 0); 85 } 86 version(Windows) { 87 assert(run("dir") == 0); 88 } 89 print("Testing run(cmd) with a non-existent command. Expect an error message."); 90 assert(run("kjafhdflkuahlkefuhfahlfeuhaf") == -1); 91 } 92 93 /** 94 * Convenience method to run a command and pipe output to a file. 95 * Params: 96 * cmd = The command to run. 97 * outputFile = The file to send output to. 98 * Returns: The exit code from the command. 99 */ 100 public int run(string cmd, string outputFile) { 101 return new ProcessBuilder() 102 .outputTo(outputFile) 103 .errorTo(outputFile) 104 .run(cmd); 105 } 106 107 /** 108 * Runs the given command, and exits the program if the return code is not 0. 109 * Params: 110 * cmd = The command to run. 111 */ 112 public void runOrQuit(string cmd) { 113 import core.stdc.stdlib : exit; 114 int r = run(cmd); 115 if (r != 0) { 116 error("Process \"%s\" exited with code %d", cmd, r); 117 exit(r); 118 } 119 } 120 121 public void runOrQuit(string cmd, string outputFile) { 122 import core.stdc.stdlib : exit; 123 int r = run(cmd, outputFile); 124 if (r != 0) { 125 error("Process \"%s\" exited with code %d", cmd, r); 126 exit(r); 127 } 128 } 129 130 /** 131 * Gets an environment variable. 132 * Params: 133 * key = The name of the environment variable. 134 * Returns: The value of the environment variable, or null. 135 */ 136 public string getEnv(string key) { 137 import std.process : environment; 138 try { 139 return environment[key]; 140 } catch (Exception e) { 141 return null; 142 } 143 } 144 145 unittest { 146 assert(getEnv("PATH") !is null); 147 assert(getEnv("flkahelkuhfalukfehlakuefhl") is null); 148 } 149 150 /** 151 * Sets an environment variable. 152 * Params: 153 * key = The name of the environment variable. 154 * value = The value to set. 155 */ 156 public void setEnv(string key, string value) { 157 import std.process : environment; 158 try { 159 environment[key] = value; 160 } catch (Exception e) { 161 error("Could not set environment variable \"%s\": %s", key, e.msg); 162 } 163 } 164 165 unittest { 166 assert(getEnv("dsh_test_env_1") is null); 167 setEnv("dsh_test_env_1", "yes"); 168 assert(getEnv("dsh_test_env_1") == "yes"); 169 }