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: format;
15 
16 // searches the local system for valid D compilers
17 private string findDCompiler()
18 {
19 	string dCompiler = "dmd";
20 	foreach (compiler; ["dmd", "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();
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 				args = ["dub", "-q", "--compiler=" ~ dCompiler, "--single", tmpfile.name];
89 				res = args.execute;
90 			}
91 			else
92 			{
93 				args = [dCompiler];
94 				args ~= input.args.split(" ");
95 				args ~= "-color=" ~ (input.color ? "on " : "off ");
96 				args ~= "-run";
97 				args ~= tmpfile.name;
98 
99 				// DMD requires a TTY for colored output
100 				auto env = [
101 					"TERM": "dtour"
102 				];
103 				auto fakeTty = `
104 faketty () {	 script -qfc "$(printf "%q " "$@")" /dev/null ; }
105 faketty ` ~ 	args.join(" ") ~  ` | cat | sed 's/\r$//'`;
106 
107 				res = fakeTty.executeShell(env);
108 			}
109 			result.success = res.status == 0;
110 			result.output = res.output;
111 		});
112 
113 		while (task.running)
114 			sleep(10.msecs);
115 
116 		return result;
117 	}
118 
119 	Package[] installedPackages()
120 	{
121 		return null;
122 	}
123 }