1 // SemiTwist Library 2 // Written in the D programming language. 3 4 module semitwist.util.process; 5 6 import std.conv; 7 import std.file; 8 import std.process; 9 import std.stream; 10 import std..string; 11 12 version(Windows) 13 { 14 import core.sys.windows.windows; 15 extern(Windows) int CreatePipe( 16 HANDLE* hReadPipe, 17 HANDLE* hWritePipe, 18 SECURITY_ATTRIBUTES* lpPipeAttributes, 19 uint nSize); 20 } 21 else 22 { 23 import core.sys.posix.unistd; 24 } 25 26 import semitwist.util.all; 27 28 void createPipe(out HANDLE readHandle, out HANDLE writeHandle) 29 { 30 version(Windows) 31 { 32 auto secAttr = SECURITY_ATTRIBUTES(SECURITY_ATTRIBUTES.sizeof, null, true); 33 if(!CreatePipe(&readHandle, &writeHandle, &secAttr, 0)) 34 throw new Exception("Couldn't create pipe"); 35 } 36 else 37 { 38 int[2] pipeHandles; 39 if(pipe(pipeHandles) != 0) 40 throw new Exception("Couldn't create pipe"); 41 readHandle = pipeHandles[0]; 42 writeHandle = pipeHandles[1]; 43 } 44 } 45 46 bool evalCleanup = true; 47 48 //TODO: Support string/wstring in addition to char[]/wchar[] 49 TRet eval(TRet)(string code, string imports="", string rdmdOpts="") 50 { 51 void removeIfExists(string filename) 52 { 53 if(exists(filename)) 54 remove(filename); 55 } 56 void cleanup(string tempName) 57 { 58 if(evalCleanup) 59 { 60 removeIfExists(tempName~".d"); 61 removeIfExists(tempName~".d.deps"); 62 removeIfExists(tempName~".map"); 63 version(Windows) 64 removeIfExists(tempName~".exe"); 65 else 66 removeIfExists(tempName); 67 } 68 } 69 70 enum boilerplate = q{ 71 import std.conv; 72 import std.process; 73 import std.stream; 74 import std..string; 75 version(Windows) import std.c.windows.windows; 76 %s 77 alias %s TRet; 78 void main(string[] args) 79 { 80 static if(is(TRet==void)) 81 _main(); 82 else 83 { 84 if(args.length < 2 || !std..string.isNumeric(args[1])) 85 throw new Exception("First arg must be file handle for the return value"); 86 87 auto writeHandle = cast(HANDLE)std.conv.to!size_t(args[1]); 88 auto retValWriter = new std.stream.File(writeHandle, FileMode.Out); 89 90 auto ret = _main(); 91 retValWriter.write(ret); 92 } 93 } 94 TRet _main() 95 { 96 %s 97 } 98 }.normalize(); 99 100 code = boilerplate.format(imports, TRet.stringof, code); 101 102 HANDLE retValPipeRead; 103 HANDLE retValPipeWrite; 104 static if(!is(TRet==void)) 105 { 106 createPipe(retValPipeRead, retValPipeWrite); 107 auto retValReader = new std.stream.File(retValPipeRead, FileMode.In); 108 } 109 110 auto tempName = "eval_st_"~md5(code); 111 std.file.write(tempName~".d", code); 112 scope(exit) cleanup(tempName); 113 114 auto rdmdName = "rdmd"; 115 116 auto errlvl = system(rdmdName~" -of"~tempName~" "~rdmdOpts~" "~tempName~" "~to!string(cast(size_t)retValPipeWrite)); 117 118 if(errlvl != 0) 119 //TODO: Include failure text, and what part failed: compile or execution 120 throw new Exception("eval failed"); 121 122 static if(is(TRet==void)) 123 return; 124 else 125 { 126 TRet retVal; 127 retValReader.read(retVal); 128 return retVal; 129 } 130 } 131 132 mixin(unittestSemiTwistDLib(q{ 133 //enum test_eval1 = q{ eval!int(q{ writeln("Hello World!"); return 7; }, q{ import std.stdio; }) }; 134 //mixin(deferEnsure!(test_eval1, q{ _==7 })); 135 136 enum test_eval2 = q{ eval!int(q{ return 42; }) }; 137 mixin(deferEnsure!(test_eval2, q{ _==42 })); 138 139 enum test_eval3 = q{ eval!(char[])(q{ return "Test string".dup; }) }; 140 mixin(deferEnsure!(test_eval3, q{ _=="Test string" })); 141 142 enum test_eval4 = q{ eval!void(q{ return; }) }; 143 //mixin(deferEnsure!(test_eval4, q{ true })); //TODO: Fix error: "voids have no value" 144 mixin(test_eval4~";"); 145 }));