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