1 /** 2 Code to make the executable do what it does at runtime. 3 */ 4 module dpp.runtime.app; 5 6 import dpp.from; 7 8 /** 9 The "real" main 10 */ 11 void run(in from!"dpp.runtime.options".Options options) @safe { 12 import std.stdio: File; 13 import std.exception: enforce; 14 import std.process: execute; 15 import std.array: join; 16 import std.file: remove; 17 18 foreach(dppFileName; options.dppFileNames) 19 preprocess!File(options, dppFileName, options.toDFileName(dppFileName)); 20 21 if(options.preprocessOnly) return; 22 23 const args = options.dlangCompiler ~ options.dlangCompilerArgs; 24 const res = execute(args); 25 enforce(res.status == 0, "Could not execute `" ~ args.join(" ") ~ "`:\n" ~ res.output); 26 27 if(!options.keepDlangFiles) { 28 foreach(fileName; options.dFileNames) 29 remove(fileName); 30 } 31 } 32 33 /** 34 Preprocesses a quasi-D file, expanding #include directives inline while 35 translating all definitions, and redefines any macros defined therein. 36 37 The output is a valid D file that can be compiled. 38 39 Params: 40 options = The runtime options. 41 */ 42 void preprocess(File)(in from!"dpp.runtime.options".Options options, 43 in string inputFileName, 44 in string outputFileName) 45 { 46 import std.file: remove; 47 48 const tmpFileName = outputFileName ~ ".tmp"; 49 scope(exit) if(!options.keepPreCppFiles) remove(tmpFileName); 50 51 { 52 auto outputFile = File(tmpFileName, "w"); 53 54 const translationText = translationText!File(options, inputFileName); 55 56 outputFile.writeln(translationText.moduleDeclaration); 57 outputFile.writeln(preamble); 58 outputFile.writeln(translationText.dlangDeclarations); 59 60 // write original D code 61 writeDlangLines(inputFileName, outputFile); 62 } 63 64 runCPreProcessor(tmpFileName, outputFileName); 65 } 66 67 private struct TranslationText { 68 string moduleDeclaration; 69 string dlangDeclarations; 70 } 71 72 // the translated D code from all #included files 73 private TranslationText translationText(File)(in from!"dpp.runtime.options".Options options, 74 in string inputFileName) 75 { 76 77 import dpp.runtime.context: Context, Language; 78 import dpp.expansion: expand, isCppHeader, getHeaderName; 79 import std.algorithm: map, filter; 80 import std..string: fromStringz; 81 import std.path: dirName; 82 import std.array: array, join; 83 import core.stdc.stdio: tmpnam; 84 85 auto inputFile = File(inputFileName); 86 const lines = () @trusted { return inputFile.byLine.map!(a => a.idup).array; }(); 87 auto moduleLines = () @trusted { return lines.filter!isModuleLine.array; }(); 88 auto nonModuleLines = lines.filter!(a => !isModuleLine(a)); 89 const includePaths = options.includePaths ~ inputFileName.dirName; 90 auto includes = nonModuleLines.map!(a => getHeaderName(a, includePaths)).filter!(a => a != ""); 91 char[1024] tmpnamBuf; 92 const includesFileName = () @trusted { return cast(string) tmpnam(&tmpnamBuf[0]).fromStringz; }(); 93 auto language = Language.C; 94 // write a temporary file with all #included files in it 95 () @trusted { 96 auto includesFile = File(includesFileName, "w"); 97 foreach(include; includes) { 98 includesFile.writeln(`#include "`, include, `"`); 99 if(isCppHeader(options, include)) language = Language.Cpp; 100 } 101 }(); 102 103 /** 104 We remember the cursors already seen so as to not try and define 105 something twice (legal in C, illegal in D). 106 */ 107 auto context = Context(options.indent, language); 108 109 // parse all #includes at once and populate context with 110 // D definitions 111 expand(includesFileName, context, includePaths); 112 113 context.fixNames; 114 115 return TranslationText(moduleLines.join("\n"), context.translation); 116 } 117 118 // write the original D code that doesn't need translating 119 private void writeDlangLines(in string inputFileName, ref from!"std.stdio".File outputFile) 120 @trusted 121 { 122 123 import dpp.expansion: getHeaderName; 124 import std.stdio: File; 125 import std.algorithm: filter; 126 127 foreach(line; File(inputFileName).byLine.filter!(a => !isModuleLine(a))) { 128 if(getHeaderName(line) == "") 129 // not an #include directive, just pass through 130 outputFile.writeln(line); 131 // otherwise do nothing 132 } 133 } 134 135 bool isModuleLine(in const(char)[] line) @safe pure { 136 import std..string: stripLeft; 137 import std.algorithm: startsWith; 138 return line.stripLeft.startsWith("module "); 139 } 140 141 142 private void runCPreProcessor(in string tmpFileName, in string outputFileName) @safe { 143 144 import std.exception: enforce; 145 import std.process: execute; 146 import std.conv: text; 147 import std..string: join, splitLines; 148 import std.stdio: File; 149 import std.algorithm: filter, startsWith; 150 151 const cppArgs = ["cpp", tmpFileName]; 152 const ret = execute(cppArgs); 153 enforce(ret.status == 0, text("Could not run `", cppArgs.join(" "), "`:\n", ret.output)); 154 155 { 156 auto outputFile = File(outputFileName, "w"); 157 auto lines = ret. 158 output 159 .splitLines 160 .filter!(a => !a.startsWith("#")) 161 ; 162 163 foreach(line; lines) { 164 outputFile.writeln(line); 165 } 166 } 167 168 } 169 170 171 private string preamble() @safe pure { 172 import std.array: replace, join; 173 import std.algorithm: map, filter; 174 import std..string: splitLines; 175 176 return q{ 177 178 import core.stdc.config; 179 import core.stdc.stdarg: va_list; 180 static import core.simd; 181 182 struct Int128 { long lower; long upper; } 183 struct UInt128 { ulong lower; ulong upper; } 184 185 struct __locale_data { int dummy; } // FIXME 186 } ~ 187 ` #define __gnuc_va_list va_list` ~ "\n" ~ 188 189 q{ 190 alias _Bool = bool; 191 192 struct dpp { 193 194 static struct Move(T) { 195 T* ptr; 196 } 197 198 // FIXME - crashes if T is passed by value (which we want) 199 static auto move(T)(ref T value) { 200 return Move!T(&value); 201 } 202 203 204 mixin template EnumD(string name, T, string prefix) if(is(T == enum)) { 205 206 private static string _memberMixinStr(string member) { 207 import std.conv: text; 208 import std.array: replace; 209 return text(` `, member.replace(prefix, ""), ` = `, T.stringof, `.`, member, `,`); 210 } 211 212 private static string _enumMixinStr() { 213 import std.array: join; 214 215 string[] ret; 216 217 ret ~= "enum " ~ name ~ "{"; 218 219 static foreach(member; __traits(allMembers, T)) { 220 ret ~= _memberMixinStr(member); 221 } 222 223 ret ~= "}"; 224 225 return ret.join("\n"); 226 } 227 228 mixin(_enumMixinStr()); 229 } 230 } 231 } 232 .splitLines 233 .filter!(a => a != "") 234 .map!(a => a.length >= 8 ? a[8 .. $] : a) // get rid of leading spaces 235 .join("\n"); 236 }