1 /**
2  * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved.
3  * Authors: Jacob Carlborg
4  * Version: Initial created: Oct 1, 2011
5  * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
6  */
7 module dstep.driver.Application;
8 
9 import std.getopt;
10 import std.stdio : writeln, stderr;
11 import Path = std.path : setExtension;
12 
13 import DStack = dstack.application.Application;
14 
15 import mambo.core._;
16 import mambo.util.Singleton;
17 import mambo.util.Use;
18 
19 import clang.c.index;
20 
21 import clang.Index;
22 import clang.TranslationUnit;
23 
24 import dstep.core.Exceptions;
25 import dstep.translator.Translator;
26 
27 class Application : DStack.Application
28 {
29 	mixin Singleton;
30 	
31 	enum Version = "0.1.0";
32 	
33 	private
34 	{
35 		string[] inputFiles;
36 		
37 		Index index;
38 		TranslationUnit translationUnit;
39 		DiagnosticVisitor diagnostics;
40 
41 		Language language;
42 		string[] argsToRestore;
43 		bool helpFlag;
44 	}
45 	
46 	protected override void run ()
47 	{
48 		handleArguments;
49 		inputFiles ~= arguments.argument.input;
50 		startConversion(inputFiles.first);
51 	}
52 
53 	protected override void setupArguments ()
54 	{
55 		arguments.sloppy = true;
56 		arguments.passThrough = true;
57 
58 		arguments.argument("input", "input files");
59 
60 		arguments('o', "output", "Write output to")
61 			.params(1)
62 			.defaults(&defaultOutputFilename);
63 
64 		arguments('x', "language", "Treat subsequent input files as having type <language>")
65 			.params(1)
66 			.restrict("c", "c-header", "objective-c", "objective-c-header")
67 			.on(&handleLanguage);
68 
69 		arguments("objective-c", "Treat source input file as Objective-C input.");
70 	}
71 
72 private:
73 
74 	string defaultOutputFilename ()
75 	{
76 		return Path.setExtension(arguments.argument.input, "d");
77 	}
78 
79 	void startConversion (string file)
80 	{
81 		if (arguments["objective-c"])
82 			argsToRestore ~= "-ObjC";
83 
84 		index = Index(false, false);
85 		translationUnit = TranslationUnit.parse(index, file, remainingArgs);
86 		
87 		if (!translationUnit.isValid)
88 			throw new DStepException("An unknown error occurred");
89 		
90 		diagnostics = translationUnit.diagnostics;
91 		
92 		scope (exit)
93 			clean;
94 			
95 		if (handleDiagnostics)
96 		{
97 			Translator.Options options;
98 			options.outputFile = arguments.output;
99 			options.language = language;
100 
101 			auto translator = new Translator(file, translationUnit, options);
102 			translator.translate;
103 		}
104 	}
105 
106 	void clean ()
107 	{
108 		translationUnit.dispose;
109 		index.dispose;
110 	}
111 	
112 	bool anyErrors ()
113 	{
114 		return diagnostics.length > 0;
115 	}
116 	
117 	void handleArguments ()
118 	{
119 		// FIXME: Cannot use type inference here, probably a bug. Results in segfault.
120 		if (arguments.rawArgs.any!((string e) => e == "-ObjC"))
121 			handleObjectiveC();
122 	}
123 	
124 	void handleObjectiveC ()
125 	{
126 		argsToRestore ~= "-ObjC";
127 		language = Language.objC;
128 	}
129 
130 	void handleLanguage (string language)
131 	{
132 		switch (language)
133 		{
134 			case "c":
135 			case "c-header":
136 				this.language = Language.c;
137 			break;
138 		
139 			// Can't handle C++ yet
140 			//
141 			// case "c++":
142 			// case "c++-header":
143 			// 	this.language = Language.cpp;
144 			// break;
145 		
146 			case "objective-c":
147 			case "objective-c-header":
148 				this.language = Language.objC;
149 			break;
150 
151 			default:
152 				throw new DStepException(`Unrecognized language "` ~ language ~ `"`);
153 		}
154 
155 		argsToRestore ~= "-x";
156 		argsToRestore ~= language;
157 	}
158 
159 	@property string[] remainingArgs ()
160 	{
161 		return arguments.rawArgs[1 .. $] ~ argsToRestore;
162 	}
163 
164 	bool handleDiagnostics ()
165 	{
166 	    bool translate = true;
167 	    	
168 		foreach (diag ; diagnostics)
169 		{
170 		    auto severity = diag.severity;
171 		    
172 		    with (CXDiagnosticSeverity)
173 		        if (translate)
174 	                translate = !(severity == CXDiagnostic_Error || severity == CXDiagnostic_Fatal);
175 
176 	        writeln(stderr, diag.format);
177 		}
178 
179 		return translate;
180 	}
181 
182 	override protected void showHelp ()
183 	{
184 		helpFlag = true;
185 
186 		println("Usage: dstep [options] <input>");
187 		println("Version: ", Version);
188 		println();
189 		println("Options:");
190 		println("    -o, --output <file>          Write output to <file>.");
191 		println("    -ObjC, --objective-c         Treat source input file as Objective-C input.");
192 		println("    -x, --language <language>    Treat subsequent input files as having type <language>.");
193 		println("    -h, --help                   Show this message and exit.");
194 		println();
195 		println("All options that Clang accepts can be used as well.");
196 		println();
197 		println("Use the `-h' flag for help.");
198 	}
199 }