1 /** 2 Copyright: Copyright (c) 2017-2019 Andrey Penechko. 3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 module cli; 7 8 import std.stdio; 9 import std.file : exists; 10 import std.path : absolutePath, extension, baseName, setExtension; 11 import vox.all; 12 13 enum WindowsSubsystemCli : ushort { 14 CUI, 15 GUI 16 } 17 18 int runCli(string[] args) 19 { 20 import std.getopt; 21 22 auto time1 = currTime; 23 auto startInitTime = currTime; 24 25 Driver driver; 26 WindowsSubsystemCli subSystem; 27 28 bool printTime; 29 bool printMem; 30 bool printStats; 31 bool checkOnly; 32 string outputFilename; 33 string outputTarget = TARGET_OS_STRING[driver.context.hostOS]; 34 string filterFuncName; 35 string targetHelp; 36 37 version(Windows) { 38 targetHelp = "Choose target. [windows-x64(default), linux-x64, macos-x64]"; 39 } 40 else version(linux) { 41 targetHelp = "Choose target. [windows-x64, linux-x64(default), macos-x64]"; 42 } 43 else version(OSX) { 44 targetHelp = "Choose target. [windows-x64, linux-x64, macos-x64(default)]"; 45 } 46 else static assert(false, "Unnhandled OS"); 47 48 bool printHelp; 49 GetoptResult optResult; 50 51 // Look for tool usage 52 if (args.length > 1) 53 { 54 switch(args[1]) 55 { 56 case "pdb-dump": 57 if (args.length == 1) { 58 writeln("Usage: vox pdb-dump file.pdb"); 59 return 1; 60 } 61 string filename = absolutePath(args[2]); 62 if (!exists(filename)) 63 { 64 writefln("File `%s` not found", absolutePath(filename)); 65 return 1; 66 } 67 import vox.be.debug_info.pdb; 68 PdbReader.fromFile(filename); 69 return 0; 70 71 default: 72 break; // regular compiler invocation 73 } 74 } 75 76 // Regular compiler invocation 77 retry_parse_opts: 78 try 79 { 80 // GC 81 optResult = getopt( 82 args, 83 "of", "Write output to file.", &outputFilename, 84 "target", targetHelp, &outputTarget, 85 "subsystem", "Select windows subsystem. [CUI(default), GUI]", &subSystem, 86 "check-only", "Disable backend passes, leaving only error checking", &checkOnly, 87 "bundle", "Emit .har file containing all of the input files and CLI arguments. No other output will be generated", &driver.context.bundleInputs, 88 89 "no-dce", "Disable Dead Code Elimination", &driver.context.disableDCE, 90 "no-inline", "Disable Inlining", &driver.context.disableInline, 91 92 "print-time", "Print time of compilation.", &printTime, 93 "print-mem", "Print memory consumtion.", &printMem, 94 "print-stats", "Print general statistics", &printStats, 95 96 "print-source", "Print source code.", &driver.context.printSource, 97 "print-lexemes", "Print lexemes.", &driver.context.printLexemes, 98 "print-ast-fresh", "Print AST after parsing.", &driver.context.printAstFresh, 99 "print-ast-sema", "Print AST after semantic analisys.", &driver.context.printAstSema, 100 "print-ir", "Print IR after AST to IR pass.", &driver.context.printIr, 101 "print-ir-opt-each", "Print IR after each optimization pass.", &driver.context.printIrOptEach, 102 "print-ir-opt", "Print IR after all optimization passes.", &driver.context.printIrOpt, 103 "print-ir-lower-each", "Print IR after each lowering pass.", &driver.context.printIrLowerEach, 104 "print-ir-lower", "Print IR after all lowering passes.", &driver.context.printIrLower, 105 "print-lir", "Print Print LIR after IR to LIR pass.", &driver.context.printLir, 106 "print-liveness", "Print liveness analisys info.", &driver.context.printLiveIntervals, 107 "print-lir-ra", "Print LIR after register allocation pass.", &driver.context.printLirRA, 108 "print-stack-layout", "Print stack layout.", &driver.context.printStackLayout, 109 "print-code-hex", "Print code hex.", &driver.context.printCodeHex, 110 "print-symbols", "Print symbols.", &driver.context.printSymbols, 111 "print-filter", "Print only info about <function name>.", &filterFuncName, 112 "print-error-trace", "Print stack trace for every error", &driver.context.printTraceOnError, 113 ); 114 } 115 catch(GetOptException e) 116 { 117 import std.algorithm.mutation : remove; 118 119 writeln(e.msg); 120 printHelp = true; 121 args = args.remove(1); 122 goto retry_parse_opts; 123 } 124 125 switch(outputTarget) { 126 case "windows-x64": driver.context.targetOs = TargetOs.windows; break; 127 case "linux-x64": driver.context.targetOs = TargetOs.linux; break; 128 case "macos-x64": driver.context.targetOs = TargetOs.macos; break; 129 //case "linux-arm64": driver.context.targetOs = TargetOs.linux; driver.context.targetArch = TargetArch.arm64; break; 130 default: 131 writefln("Unknown target: %s", outputTarget); 132 printHelp = true; 133 } 134 135 if (args.length < 2) printHelp = true; 136 if (args.length > 0) args = args[1..$]; // skip program name 137 138 if (optResult.helpWanted) printHelp = true; 139 140 if (printHelp) 141 { 142 writeln("Usage: vox [tool] [options]... [.vx|.har]..."); 143 writeln(" tools:"); 144 writeln(" pdb-dump - dumps the contents of a .pdb file"); 145 writeln(" options:"); 146 147 size_t maxShortLength; 148 size_t maxLongLength; 149 foreach (it; optResult.options) 150 { 151 maxShortLength = max(maxShortLength, it.optShort.length); 152 maxLongLength = max(maxLongLength, it.optLong.length); 153 } 154 155 foreach (it; optResult.options) 156 { 157 writefln("%-*s %-*s %s", maxShortLength, it.optShort, maxLongLength, it.optLong, it.help); 158 } 159 160 return 0; 161 } 162 163 final switch (subSystem) { 164 case WindowsSubsystemCli.CUI: driver.context.windowsSubsystem = WindowsSubsystem.WINDOWS_CUI; break; 165 case WindowsSubsystemCli.GUI: driver.context.windowsSubsystem = WindowsSubsystem.WINDOWS_GUI; break; 166 } 167 168 string[] filenames = args; 169 170 auto passes = exePasses; 171 // Disable backend for check-only 172 if (checkOnly) passes = frontendOnlyPasses; 173 if (driver.context.bundleInputs) passes = bundlePasses; 174 175 driver.context.buildType = BuildType.exe; 176 177 driver.initialize(passes); 178 driver.beginCompilation(); 179 180 if (outputFilename) driver.context.outputFilename = outputFilename; 181 else { 182 string ext; 183 if (driver.context.targetOs == TargetOs.windows) ext = ".exe"; 184 driver.context.outputFilename = filenames[0].baseName.setExtension(ext); // GC 185 } 186 if (driver.context.bundleInputs) { 187 driver.context.outputFilename = driver.context.outputFilename.setExtension(".har"); 188 } 189 190 if (filterFuncName) driver.context.setDumpFilter(filterFuncName); 191 192 auto times = PerPassTimeMeasurements(1, driver.passes); 193 auto endInitTime = currTime; 194 195 try 196 { 197 foreach(filename; filenames) 198 { 199 string ext = filename.extension; 200 switch(ext) 201 { 202 case ".har": 203 if (!exists(filename)) 204 { 205 driver.context.error("File `%s` not found", absolutePath(filename)); 206 break; 207 } 208 209 auto file = File(filename, "r"); 210 char[] sourceBuffer = driver.context.sourceBuffer.voidPut(file.size); 211 char[] harData = file.rawRead(sourceBuffer); 212 file.close(); 213 214 void onHarFile(SourceFileInfo fileInfo) 215 { 216 if (fileInfo.name == "<args>") { 217 // skip 218 } else { 219 driver.addModule(fileInfo); 220 } 221 } 222 223 parseHar(driver.context, filename, harData, &onHarFile); 224 break; 225 default: 226 driver.addModule(SourceFileInfo(filename)); 227 break; 228 } 229 } 230 231 scope(exit) { 232 // In case of an error write the output 233 if (driver.context.bundleInputs) write_bundle(driver.context); 234 } 235 236 driver.compile(); 237 } 238 catch(CompilationException e) { 239 writeln(driver.context.sink.text); 240 if (e.isICE) { 241 writeln(e); 242 } 243 return 1; 244 } 245 catch(Throwable t) { 246 writeln(driver.context.sink.text); 247 writeln(t); 248 return 1; 249 } 250 251 TextSink sink; 252 if (printMem) driver.context.printMemSize(sink); 253 254 auto startReleaseTime = currTime; 255 // releasing memory is not necessary when running in standalone mode 256 driver.releaseMemory; 257 auto endReleaseTime = currTime; 258 259 auto time2 = currTime; 260 Duration duration = time2-time1; 261 262 times.onIteration(0, duration); 263 264 if (printMem) write(cast(string)sink.data.data); 265 266 if (printStats) { 267 writefln("Lexed %s lines", driver.context.numLinesLexed); 268 } 269 270 if (printTime) { 271 times.print; 272 } 273 274 if (printStats || printTime) { 275 writefln("Finished in %ss, init %ss, release %ss", 276 scaledNumberFmt(duration), 277 scaledNumberFmt(endInitTime-startInitTime), 278 scaledNumberFmt(endReleaseTime-startReleaseTime)); 279 } 280 281 return 0; 282 }