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 }