1 /**
2    Command-line options
3  */
4 module dpp.runtime.options;
5 
6 @safe:
7 
8 version(Windows)
9     enum exeExtension = ".exe";
10 else
11     enum exeExtension = "";
12 
13 
14 struct Options {
15 
16     enum usage = "Usage: d++ [options] [D compiler options] <filename.dpp> [D compiler args]";
17 
18     string[] dppFileNames;
19     string indentation;
20     bool debugOutput;
21     string[] includePaths;
22     bool keepPreCppFiles;
23     bool keepDlangFiles;
24     bool parseAsCpp;
25     bool preprocessOnly;
26     string dlangCompiler = "dmd";
27     string[] dlangCompilerArgs;
28     string[] defines;
29     bool earlyExit;
30     bool hardFail;
31 
32     this(string[] args) {
33 
34         import clang: systemPaths;
35         import std.exception: enforce;
36         import std.path: stripExtension, extension, buildPath, absolutePath;
37         import std.file: tempDir;
38         import std.algorithm: map, filter, canFind, startsWith;
39         import std.array: array;
40         import std.conv: text;
41 
42         parseArgs(args);
43         if(earlyExit) return;
44 
45         if(preprocessOnly)
46             enforce(args.length == 2,
47                     text("Wrong argument length. Expected 2, got ", args.length,
48                          " for preprocessing only.\n", usage));
49         else
50             enforce(args.length >= 2, "Not enough arguments\n" ~ usage);
51 
52         dppFileNames = args.filter!(a => a.extension == ".dpp").array;
53         enforce(dppFileNames.length != 0, "No .dpp input file specified\n" ~ usage);
54 
55         // Remove the name of this binary and the name of the .dpp input file from args
56         // so that a D compiler can use the remaining entries.
57         dlangCompilerArgs =
58             args[1..$].filter!(a => a.extension != ".dpp").array ~
59             dFileNames;
60 
61         // if no -of option is given, default to the name of the .dpp file
62         if(!dlangCompilerArgs.canFind!(a => a.startsWith("-of")) && !dlangCompilerArgs.canFind("-c"))
63             dlangCompilerArgs ~= "-of" ~
64                 args.
65                 filter!(a => a.extension == ".dpp" || a.extension == ".d")
66                 .front
67                 .stripExtension
68                 ~ exeExtension;
69 
70         includePaths = systemPaths ~ includePaths;
71     }
72 
73     string[] dFileNames() @safe pure const {
74         import std.algorithm: map;
75         import std.array: array;
76         return dppFileNames.map!toDFileName.array;
77     }
78 
79     static string toDFileName(in string dppFileName) @safe pure nothrow {
80         import std.path: stripExtension;
81         return dppFileName.stripExtension ~ ".d";
82     }
83 
84     private void parseArgs(ref string[] args) {
85         import std.getopt: getopt, defaultGetoptPrinter, config;
86         auto helpInfo =
87             getopt(
88                 args,
89                 config.passThrough,
90                 "print-cursors", "Print debug information", &debugOutput,
91                 "include-path", "Include paths", &includePaths,
92                 "keep-pre-cpp-files", "Do not delete the temporary pre-preprocessed file", &keepPreCppFiles,
93                 "keep-d-files", "Do not delete the temporary D file to be compiled", &keepDlangFiles,
94                 "preprocess-only", "Only transform the .dpp file into a .d file, don't compile", &preprocessOnly,
95                 "compiler", "D compiler to use", &dlangCompiler,
96                 "parse-as-cpp", "Parse header as C++", &parseAsCpp,
97                 "define", "C Preprocessor macro", &defines,
98                 "hard-fail", "Translate nothing if any part fails", &hardFail,
99             );
100 
101         if(helpInfo.helpWanted) {
102             () @trusted {
103                 defaultGetoptPrinter(usage, helpInfo.options);
104             }();
105             earlyExit = true;
106         }
107     }
108 
109     Options indent() pure nothrow const {
110         Options ret;
111         foreach(i, ref elt; ret.tupleof) {
112             static if(__traits(compiles, this.tupleof[i].dup))
113                 elt = this.tupleof[i].dup;
114             else
115                 elt = this.tupleof[i];
116         }
117 
118         ret.includePaths = includePaths.dup;
119         ret.defines = defines.dup;
120         ret.indentation = indentation ~ "    ";
121 
122         return ret;
123     }
124 
125     void log(T...)(auto ref T args) @trusted const {
126         version(unittest) import unit_threaded.io: writeln = writelnUt;
127         else import std.stdio: writeln;
128 
129         version(unittest) enum shouldLog = true;
130         else             const shouldLog = debugOutput;
131 
132         if(shouldLog)
133             debug writeln(indentation, args);
134     }
135 }