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 }