1 /// Copyright: Copyright (c) 2017-2022 Andrey Penechko 2 /// License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0) 3 /// Authors: Andrey Penechko 4 module cli; 5 6 import std.stdio; 7 import std.file : exists; 8 import std.path : absolutePath, extension, baseName, setExtension; 9 import vox.all; 10 11 enum WindowsSubsystemCli : ushort { 12 CUI, 13 GUI 14 } 15 16 int runCli(string[] args) 17 { 18 import std.getopt; 19 20 auto time1 = currTime; 21 auto startInitTime = currTime; 22 23 Driver driver; 24 WindowsSubsystemCli subSystem; 25 26 bool printTime; 27 bool printMem; 28 bool printStats; 29 bool checkOnly; 30 string outputFilename; 31 string outputTarget = TARGET_OS_STRING[driver.context.hostOS]; 32 string filterFuncName; 33 string targetHelp; 34 35 version(Windows) { 36 targetHelp = "Choose target. [windows-x64(default), linux-x64, macos-x64]"; 37 } 38 else version(linux) { 39 targetHelp = "Choose target. [windows-x64, linux-x64(default), macos-x64]"; 40 } 41 else version(OSX) { 42 targetHelp = "Choose target. [windows-x64, linux-x64, macos-x64(default)]"; 43 } 44 else static assert(false, "Unnhandled OS"); 45 46 bool printHelp; 47 GetoptResult optResult; 48 49 // Look for tool usage 50 if (args.length > 1) 51 { 52 switch(args[1]) 53 { 54 case "pdb-dump": 55 if (args.length == 1) { 56 writeln("Usage: vox pdb-dump file.pdb"); 57 return 1; 58 } 59 string filename = absolutePath(args[2]); 60 if (!exists(filename)) 61 { 62 writefln("File `%s` not found", absolutePath(filename)); 63 return 1; 64 } 65 import vox.be.debug_info.pdb; 66 PdbReader.fromFile(filename); 67 return 0; 68 69 default: 70 break; // regular compiler invocation 71 } 72 } 73 74 // Regular compiler invocation 75 retry_parse_opts: 76 try 77 { 78 // GC 79 optResult = getopt( 80 args, 81 "of", "Write output to file.", &outputFilename, 82 "target", targetHelp, &outputTarget, 83 "subsystem", "Select windows subsystem. [CUI(default), GUI]", &subSystem, 84 "check-only", "Disable backend passes, leaving only error checking", &checkOnly, 85 "bundle", "Emit .har file containing all of the input files and CLI arguments. No other output will be generated", &driver.context.bundleInputs, 86 87 "no-dce", "Disable Dead Code Elimination", &driver.context.disableDCE, 88 "no-inline", "Disable Inlining", &driver.context.disableInline, 89 90 "print-time", "Print time of compilation.", &printTime, 91 "print-mem", "Print memory consumtion.", &printMem, 92 "print-stats", "Print general statistics", &printStats, 93 94 "print-source", "Print source code.", &driver.context.printSource, 95 "print-lexemes", "Print lexemes.", &driver.context.printLexemes, 96 "print-ast-fresh", "Print AST after parsing.", &driver.context.printAstFresh, 97 "print-ast-sema", "Print AST after semantic analisys.", &driver.context.printAstSema, 98 "print-ir", "Print IR after AST to IR pass.", &driver.context.printIr, 99 "print-ir-opt-each", "Print IR after each optimization pass.", &driver.context.printIrOptEach, 100 "print-ir-opt", "Print IR after all optimization passes.", &driver.context.printIrOpt, 101 "print-ir-lower-each", "Print IR after each lowering pass.", &driver.context.printIrLowerEach, 102 "print-ir-lower", "Print IR after all lowering passes.", &driver.context.printIrLower, 103 "print-lir", "Print Print LIR after IR to LIR pass.", &driver.context.printLir, 104 "print-liveness", "Print liveness analisys info.", &driver.context.printLiveIntervals, 105 "print-lir-ra", "Print LIR after register allocation pass.", &driver.context.printLirRA, 106 "print-stack-layout", "Print stack layout.", &driver.context.printStackLayout, 107 "print-code-hex", "Print code hex.", &driver.context.printCodeHex, 108 "print-symbols", "Print symbols.", &driver.context.printSymbols, 109 "print-filter", "Print only info about <function name>.", &filterFuncName, 110 "print-error-trace", "Print stack trace for every error", &driver.context.printTraceOnError, 111 ); 112 } 113 catch(GetOptException e) 114 { 115 import std.algorithm.mutation : remove; 116 117 writeln(e.msg); 118 printHelp = true; 119 args = args.remove(1); 120 goto retry_parse_opts; 121 } 122 123 switch(outputTarget) { 124 case "windows-x64": driver.context.targetOs = TargetOs.windows; break; 125 case "linux-x64": driver.context.targetOs = TargetOs.linux; break; 126 case "macos-x64": driver.context.targetOs = TargetOs.macos; break; 127 //case "linux-arm64": driver.context.targetOs = TargetOs.linux; driver.context.targetArch = TargetArch.arm64; break; 128 default: 129 writefln("Unknown target: %s", outputTarget); 130 printHelp = true; 131 } 132 133 if (args.length < 2) printHelp = true; 134 if (args.length > 0) args = args[1..$]; // skip program name 135 136 if (optResult.helpWanted) printHelp = true; 137 138 if (printHelp) 139 { 140 writeln("Usage: vox [tool] [options]... [.vx|.har]..."); 141 writeln(" tools:"); 142 writeln(" pdb-dump - dumps the contents of a .pdb file"); 143 writeln(" options:"); 144 145 size_t maxShortLength; 146 size_t maxLongLength; 147 foreach (it; optResult.options) 148 { 149 maxShortLength = max(maxShortLength, it.optShort.length); 150 maxLongLength = max(maxLongLength, it.optLong.length); 151 } 152 153 foreach (it; optResult.options) 154 { 155 writefln("%-*s %-*s %s", maxShortLength, it.optShort, maxLongLength, it.optLong, it.help); 156 } 157 158 return 0; 159 } 160 161 final switch (subSystem) { 162 case WindowsSubsystemCli.CUI: driver.context.windowsSubsystem = WindowsSubsystem.WINDOWS_CUI; break; 163 case WindowsSubsystemCli.GUI: driver.context.windowsSubsystem = WindowsSubsystem.WINDOWS_GUI; break; 164 } 165 166 string[] filenames = args; 167 168 // Select passes 169 auto passes = exePasses; 170 if (checkOnly) passes = frontendOnlyPasses; // Disable backend for check-only 171 if (driver.context.bundleInputs) passes = bundlePasses; 172 173 driver.context.buildType = BuildType.exe; 174 175 driver.initialize(passes); 176 driver.beginCompilation(); 177 178 // Set outputFilename 179 if (outputFilename) driver.context.outputFilename = outputFilename; 180 else { 181 string ext; 182 if (driver.context.targetOs == TargetOs.windows) ext = ".exe"; 183 driver.context.outputFilename = filenames[0].baseName.setExtension(ext); // GC 184 } 185 if (driver.context.bundleInputs) { 186 driver.context.outputFilename = driver.context.outputFilename.setExtension(".har"); 187 } 188 189 if (filterFuncName) driver.context.setDumpFilter(filterFuncName); 190 191 auto times = PerPassTimeMeasurements(1, driver.passes); 192 auto endInitTime = currTime; 193 194 try 195 { 196 // Add files to the compilation 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 }