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