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 }