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