1 module exec.stupidlocal;
2 
3 import exec.iexecprovider;
4 
5 import vibe.core.core: sleep, runTask;
6 import core.time : msecs;
7 import vibe.core.log : logInfo;
8 
9 import std.process;
10 import std.typecons: Tuple;
11 import std.file: exists, remove, tempDir;
12 import std.stdio: File;
13 import std.random: uniform;
14 import std..string: stripRight, format;
15 
16 // searches the local system for valid D compilers
17 private string findDCompiler()
18 {
19 	string dCompiler = "dmd";
20 	foreach (compiler; ["dmd", "ldmd", "ldmd2", "gdmd"])
21 	{
22 		try {
23 			if (execute([compiler, "--version"]).status == 0)
24 			{
25 				dCompiler = compiler;
26 				break;
27 			}
28 		} catch (ProcessException) {}
29 	}
30 	return dCompiler;
31 }
32 
33 /++
34 	Stupid local executor which just runs rdmd and passes the source to it
35 	and outputs the executes binary's output.
36 
37 	Warning:
38 		UNSAFE BECUASE CODE IS RUN UNFILTERED AND NOT IN A SANDBOX
39 +/
40 class StupidLocal: IExecProvider
41 {
42 	string dCompiler = "dmd";
43 	this() {
44 		dCompiler = findDCompiler();
45 	    logInfo("Selected %s as D compiler", dCompiler);
46 	}
47 
48 	private File getTempFile()
49 	{
50 		auto tempdir = tempDir().stripRight("/");  // dub has issues with // in path
51 
52 		static string randomName()
53 		{
54 			enum Length = 10;
55 			char[Length] res;
56 			foreach (ref c; res) {
57 				c = cast(char)('a' + uniform(0, 'z'-'a'));
58 			}
59 			return res.idup;
60 		}
61 
62 		string tempname;
63 		do {
64 			tempname = "%s/temp_dlang_tour_%s.d".format(tempdir, randomName());
65 		} while (exists(tempname));
66 		return File(tempname, "wb");
67 	}
68 
69 	Tuple!(string, "output", bool, "success") compileAndExecute(RunInput input)
70 	{
71 		import std.array : join, split;
72 		import std.algorithm.searching : canFind;
73 		typeof(return) result;
74 		auto task = runTask(() {
75 			auto tmpfile = getTempFile();
76 			scope(exit) tmpfile.name.remove;
77 
78 			tmpfile.write(input.source);
79 			tmpfile.close();
80 
81 			const isDub = input.source.canFind("dub.sdl", "dub.json");
82 			string[] args;
83 
84 			typeof(args.execute) res;
85 			// support execution of dub single file packages
86 			if (isDub)
87 			{
88 				string dubCompiler;
89 				switch (dCompiler)
90 				{
91 					case "dmd":
92 						dubCompiler = "dmd";
93 						break;
94 					case "ldmd":
95 						dubCompiler = "ldc";
96 						break;
97 					case "ldmd2":
98 						dubCompiler = "ldc2";
99 						break;
100 					case "gdmd", "gdmd2":
101 						dubCompiler = "gdc";
102 						break;
103 					default:
104 						assert(0, "Unknown compiler found.");
105 				}
106 
107 				args = ["dub", "-q", "--compiler=" ~ dubCompiler, "--single", tmpfile.name];
108 				res = args.execute;
109 			}
110 			else
111 			{
112 				args = [dCompiler];
113 				args ~= input.args.split(" ");
114 				args ~= "-color=" ~ (input.color ? "on " : "off ");
115 				args ~= "-run";
116 				args ~= tmpfile.name;
117 				args ~= input.runtimeArgs.split(" ");
118 
119 				// DMD requires a TTY for colored output
120 				auto env = [
121 					"TERM": "dtour"
122 				];
123 				auto fakeTty = `
124 faketty () {	 script -qfc "$(printf "%q " "$@")" /dev/null ; }
125 faketty ` ~ 	args.join(" ") ~  ` | cat | sed 's/\r$//'`;
126 
127 				res = fakeTty.executeShell(env);
128 			}
129 			result.success = res.status == 0;
130 			result.output = res.output;
131 		});
132 
133 		while (task.running)
134 			sleep(10.msecs);
135 
136 		return result;
137 	}
138 
139 	Package[] installedPackages()
140 	{
141 		return null;
142 	}
143 }