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 }