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 int 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 bool ignoreMacros; 33 bool detailedUntranslatable; 34 string[] ignoredNamespaces; 35 string[] ignoredCursors; 36 bool ignoreSystemPaths; 37 string[] ignoredPaths; 38 string[string] prebuiltHeaders; 39 bool alwaysScopedEnums; 40 string cppStandard = "c++17"; 41 string[] clangOptions; 42 bool noSystemHeaders; 43 string cppPath; 44 45 this(string[] args) { 46 47 import clang: systemPaths; 48 import std.exception: enforce; 49 import std.path: stripExtension, extension, buildPath, absolutePath; 50 import std.file: tempDir; 51 import std.algorithm: map, filter, canFind, startsWith; 52 import std.array: array; 53 import std.conv: text; 54 55 parseArgs(args); 56 if(earlyExit) return; 57 58 if(preprocessOnly) 59 keepDlangFiles = true; 60 61 dppFileNames = args.filter!(a => a.extension == ".dpp").array; 62 enforce(dppFileNames.length != 0, "No .dpp input file specified\n" ~ usage); 63 64 // Remove the name of this binary and the name of the .dpp input file from args 65 // so that a D compiler can use the remaining entries. 66 dlangCompilerArgs = 67 args[1..$].filter!(a => a.extension != ".dpp").array ~ 68 dFileNames; 69 70 // if no -of option is given, default to the name of the .dpp file 71 if(!dlangCompilerArgs.canFind!(a => a.startsWith("-of")) && !dlangCompilerArgs.canFind("-c")) 72 dlangCompilerArgs ~= "-of" ~ 73 args. 74 filter!(a => a.extension == ".dpp" || a.extension == ".d") 75 .front 76 .stripExtension 77 ~ exeExtension; 78 79 version(Windows) 80 assert(!cppStdLib, "C++ std lib functionality not implemented yet for Windows"); 81 82 if(cppStdLib) { 83 dlangCompilerArgs ~= "-L-lstdc++"; 84 parseAsCpp = true; 85 } 86 87 if (!noSystemHeaders) 88 includePaths = systemPaths ~ includePaths; 89 } 90 91 string[] dFileNames() @safe pure const { 92 import std.algorithm: map; 93 import std.array: array; 94 return dppFileNames.map!toDFileName.array; 95 } 96 97 static string toDFileName(in string dppFileName) @safe pure nothrow { 98 import std.path: stripExtension; 99 return dppFileName.stripExtension ~ ".d"; 100 } 101 102 private void parseArgs(ref string[] args) { 103 import std.getopt: getopt, defaultGetoptPrinter, config; 104 import std.algorithm : map; 105 import std.array : split, join; 106 auto helpInfo = 107 getopt( 108 args, 109 config.passThrough, 110 "print-cursors", "Print debug information", &debugOutput, 111 "include-path", "Include paths", &includePaths, 112 "keep-pre-cpp-files", "Do not delete the temporary pre-preprocessed file", &keepPreCppFiles, 113 "keep-d-files", "Do not delete the temporary D file to be compiled", &keepDlangFiles, 114 "preprocess-only", "Only transform the .dpp file into a .d file, don't compile", &preprocessOnly, 115 "compiler", "D compiler to use", &dlangCompiler, 116 "parse-as-cpp", "Parse header as C++", &parseAsCpp, 117 "define", "C Preprocessor macro", &defines, 118 "hard-fail", "Translate nothing if any part fails", &hardFail, 119 "c++-std-lib", "Link to the C++ standard library", &cppStdLib, 120 "ignore-macros", "Ignore preprocessor macros", &ignoreMacros, 121 "ignore-ns", "Ignore a C++ namespace", &ignoredNamespaces, 122 "ignore-cursor", "Ignore a C++ cursor", &ignoredCursors, 123 "ignore-path", "Ignore a file path, note it globs so you will want to use *", &ignoredPaths, 124 "ignore-system-paths", "Adds system paths to the ignore-paths list (you can add them back individually with --include-path)", &ignoreSystemPaths, 125 "prebuilt-header", "Declare a #include can be safely replaced with import. You should also ignore-path to prevent retranslating the file", &prebuiltHeaders, 126 "detailed-untranslatables", "Show details about untranslatable cursors", &detailedUntranslatable, 127 "scoped-enums", "Don't redeclare enums to mimic C", &alwaysScopedEnums, 128 "c++-standard", "The C++ language standard (e.g. \"c++14\")", &cppStandard, 129 "clang-option", "Pass option to libclang", &clangOptions, 130 "no-sys-headers", "Don't include system headers by default", &noSystemHeaders, 131 "cpp-path", "Path to the C preprocessor executable", &cppPath, 132 ); 133 134 clangOptions = map!(e => e.split(" "))(clangOptions).join(); 135 136 if(helpInfo.helpWanted) { 137 () @trusted { 138 defaultGetoptPrinter(usage, helpInfo.options); 139 }(); 140 earlyExit = true; 141 } 142 143 if(ignoreSystemPaths) { 144 import clang: systemPaths; 145 import std.algorithm: filter, canFind; 146 foreach(sp; systemPaths.filter!(p => !includePaths.canFind(p))) 147 ignoredPaths ~= sp ~ "*"; 148 } 149 } 150 151 void indent() @safe pure nothrow { 152 indentation += 4; 153 } 154 155 Options dup() @safe pure nothrow const { 156 Options ret; 157 foreach(i, ref elt; ret.tupleof) { 158 static if(__traits(compiles, this.tupleof[i].dup)) 159 elt = this.tupleof[i].dup; 160 else static if(is(typeof(this.tupleof[i]) == const(K[V]), K, V)) 161 { 162 try // surprised looping over the AA is not nothrow but meh 163 foreach(k, v; this.tupleof[i]) 164 elt[k] = v; 165 catch(Exception) assert(0); 166 } 167 else 168 elt = this.tupleof[i]; 169 } 170 171 ret.includePaths = includePaths.dup; 172 ret.defines = defines.dup; 173 174 return ret; 175 } 176 177 void log(T...)(auto ref T args) @trusted const { 178 version(unittest) import unit_threaded.io: writeln = writelnUt; 179 else import std.stdio: writeln; 180 181 version(unittest) 182 enum shouldLog = true; 183 else 184 const shouldLog = debugOutput; 185 186 if(shouldLog) 187 writeln(indentationString, args); 188 } 189 190 private auto indentationString() @safe pure nothrow const { 191 import std.array: appender; 192 auto app = appender!(char[]); 193 app.reserve(indentation); 194 foreach(i; 0 .. indentation) app ~= " "; 195 return app.data; 196 } 197 }