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;
12 import std.file;
13 import std.parallelism;
14 
15 import clang.c.Index;
16 
17 import clang.Compiler;
18 import clang.Index;
19 import clang.TranslationUnit;
20 import clang.Util;
21 
22 import dstep.Configuration;
23 import dstep.translator.Options;
24 import dstep.core.Exceptions;
25 import dstep.translator.Options;
26 import dstep.translator.Translator;
27 
28 class Application
29 {
30     private
31     {
32         Configuration config;
33     }
34 
35     public this (Configuration config)
36     {
37         this.config = config;
38     }
39 
40     public void run ()
41     {
42         import std.exception : enforce;
43         import std.range;
44 
45         enforce!DStepException(config.inputFiles.length > 0,
46             "dstep: error: must supply at least one input file\n");
47 
48         enforceInputFilesExist(config);
49 
50         auto translationUnits = makeTranslationUnits(config);
51 
52         enforceTranslationUnitsCompiled(translationUnits);
53 
54         auto inputFiles = config.inputFiles;
55         auto outputFiles = makeOutputFiles(config);
56 
57         foreach (tuple; zip(inputFiles, outputFiles, translationUnits).parallel(1))
58         {
59             string outputDirectory = Path.dirName(tuple[1]);
60 
61             if (!exists(outputDirectory))
62                 mkdirRecurse(outputDirectory);
63 
64             Options options = this.config.toOptions(tuple[0], tuple[1]);
65 
66             auto translator = new Translator(tuple[2], options);
67             translator.translate;
68         }
69 
70         taskPool.finish(true);
71     }
72 
73     static void enforceInputFilesExist(const Configuration config)
74     {
75         import std.exception : enforce;
76         import std.format : format;
77 
78         foreach (inputFile; config.inputFiles)
79         {
80             enforce!DStepException(
81                 exists(inputFile),
82                 format("dstep: error: file '%s' doesn't exist\n", inputFile));
83 
84             enforce!DStepException(
85                 isFile(inputFile),
86                 format("dstep: error: '%s' is not a file\n", inputFile));
87         }
88     }
89 
90     void enforceTranslationUnitsCompiled(TranslationUnit[] translationUnits)
91     {
92         import std.array : Appender;
93         import std.exception : enforce;
94 
95         bool translate = true;
96         auto message = Appender!string();
97 
98         foreach (translationUnit; translationUnits)
99         {
100             foreach (diagnostics ; translationUnit.diagnostics)
101             {
102                 auto severity = diagnostics.severity;
103 
104                 with (CXDiagnosticSeverity)
105                     if (translate)
106                         translate = !(severity == error || severity == fatal);
107 
108                 message.put(diagnostics.format);
109                 message.put("\n");
110             }
111         }
112 
113         enforce!DStepException(translate, message.data);
114     }
115 
116     static string[] makeOutputFiles(Configuration config)
117     {
118         import std.algorithm;
119         import std.array;
120         import std.range;
121 
122         import dstep.driver.Util : makeDefaultOutputFile;
123 
124         auto inputFiles = config.inputFiles;
125 
126         // when only one input file is supplied, -o argument is
127         // interpreted as file path, otherwise as base directory path
128         if (inputFiles.length == 1)
129         {
130             return [config.output.empty
131                 ? makeDefaultOutputFile(inputFiles.front, false)
132                 : config.output];
133         }
134         else
135         {
136             alias fmap = file => Path.buildPath(
137                 config.output,
138                 makeDefaultOutputFile(file, false));
139 
140             return inputFiles.map!fmap.array;
141         }
142     }
143 
144     static TranslationUnit[] makeTranslationUnits(Configuration config)
145     {
146         import std.parallelism;
147 
148         Index translationIndex = Index(false, false);
149         Compiler compiler;
150 
151         auto translationUnits = new TranslationUnit[config.inputFiles.length];
152 
153         foreach (index, ref unit; translationUnits.parallel(1))
154         {
155             unit = TranslationUnit.parse(
156                 translationIndex,
157                 config.inputFiles[index],
158                 config.clangParams ~ compiler.internalFlags,
159                 compiler.internalHeaders);
160         }
161 
162         return translationUnits;
163     }
164 }