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 }