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