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     if(!options.keepDlangFiles) {
27         foreach(fileName; options.dFileNames)
28             remove(fileName);
29     }
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 
47     import dpp.runtime.context: Context;
48     import dpp.expansion: maybeExpand;
49     import std.algorithm: map, startsWith, filter;
50     import std.process: execute;
51     import std.exception: enforce;
52     import std.conv: text;
53     import std..string: splitLines;
54     import std.file: remove;
55     import std.array: replace;
56 
57     const tmpFileName = outputFileName ~ ".tmp";
58     scope(exit) if(!options.keepPreCppFile) remove(tmpFileName);
59 
60     {
61         auto outputFile = File(tmpFileName, "w");
62 
63         outputFile.writeln(preamble);
64 
65         /**
66            We remember the cursors already seen so as to not try and define
67            something twice (legal in C, illegal in D).
68         */
69         auto context = Context(options.indent);
70 
71         () @trusted {
72             foreach(immutable line; File(inputFileName).byLine.map!(a => cast(string)a)) {
73                 // If the line is an #include directive, expand its translations "inline"
74                 // into the context structure.
75                 line.maybeExpand(context);
76             }
77         }();
78 
79         context.fixNames;
80         outputFile.writeln(context.translation);
81     }
82 
83     const ret = execute(["cpp", tmpFileName]);
84     enforce(ret.status == 0, text("Could not run cpp on ", tmpFileName, ":\n", ret.output));
85 
86     {
87         auto outputFile = File(outputFileName, "w");
88         auto lines = ret.
89             output
90             .splitLines
91             .filter!(a => !a.startsWith("#"))
92             ;
93 
94         foreach(line; lines) {
95             outputFile.writeln(line);
96         }
97     }
98 }
99 
100 
101 private string preamble() @safe pure {
102     import std.array: replace, join;
103     import std.algorithm: filter;
104     import std..string: splitLines;
105 
106     return q{
107 
108         import core.stdc.config;
109         import core.stdc.stdarg: va_list;
110         struct __locale_data { int dummy; }  // FIXME
111         #define __gnuc_va_list va_list
112         alias _Bool = bool;
113 
114     }.replace("        ", "").splitLines.filter!(a => a != "").join("\n");
115 }