1 /* 2 * [The "BSD license"] 3 * Copyright (c) 2010 Terence Parr 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 package org.antlr.tool; 29 30 import org.antlr.Tool; 31 import org.antlr.codegen.CodeGenerator; 32 import org.antlr.misc.Utils; 33 import org.stringtemplate.v4.ST; 34 import org.stringtemplate.v4.STGroup; 35 36 import java.io.*; 37 import java.util.ArrayList; 38 import java.util.List; 39 40 /** Given a grammar file, show the dependencies on .tokens etc... 41 * Using ST, emit a simple "make compatible" list of dependencies. 42 * For example, combined grammar T.g (no token import) generates: 43 * 44 * TParser.java : T.g 45 * T.tokens : T.g 46 * T__g : T.g 47 * 48 * For tree grammar TP with import of T.tokens: 49 * 50 * TP.g : T.tokens 51 * TP.java : TP.g 52 * 53 * If "-lib libdir" is used on command-line with -depend, then include the 54 * path like 55 * 56 * TP.g : libdir/T.tokens 57 * 58 * Pay attention to -o as well: 59 * 60 * outputdir/TParser.java : T.g 61 * 62 * So this output shows what the grammar depends on *and* what it generates. 63 * 64 * Operate on one grammar file at a time. If given a list of .g on the 65 * command-line with -depend, just emit the dependencies. The grammars 66 * may depend on each other, but the order doesn't matter. Build tools, 67 * reading in this output, will know how to organize it. 68 * 69 * This is a wee bit slow probably because the code generator has to load 70 * all of its template files in order to figure out the file extension 71 * for the generated recognizer. 72 * 73 * This code was obvious until I removed redundant "./" on front of files 74 * and had to escape spaces in filenames :( 75 */ 76 public class BuildDependencyGenerator { 77 protected String grammarFileName; 78 protected String tokenVocab; 79 protected Tool tool; 80 protected Grammar grammar; 81 protected CodeGenerator generator; 82 protected STGroup templates; 83 BuildDependencyGenerator(Tool tool, String grammarFileName)84 public BuildDependencyGenerator(Tool tool, String grammarFileName) 85 throws IOException { 86 this.tool = tool; 87 this.grammarFileName = grammarFileName; 88 grammar = tool.getRootGrammar(grammarFileName); 89 String language = (String) grammar.getOption("language"); 90 generator = new CodeGenerator(tool, grammar, language); 91 generator.loadTemplates(language); 92 } 93 94 /** From T.g return a list of File objects that 95 * name files ANTLR will emit from T.g. 96 */ getGeneratedFileList()97 public List<File> getGeneratedFileList() { 98 List<File> files = new ArrayList<File>(); 99 File outputDir = tool.getOutputDirectory(grammarFileName); 100 if (outputDir.getName().equals(".")) { 101 outputDir = outputDir.getParentFile(); 102 } else if (outputDir.getName().indexOf(' ') >= 0) { // has spaces? 103 String escSpaces = Utils.replace(outputDir.toString(), 104 " ", 105 "\\ "); 106 outputDir = new File(escSpaces); 107 } 108 // add generated recognizer; e.g., TParser.java 109 String recognizer = 110 generator.getRecognizerFileName(grammar.name, grammar.type); 111 files.add(new File(outputDir, recognizer)); 112 // add output vocab file; e.g., T.tokens. This is always generated to 113 // the base output directory, which will be just . if there is no -o option 114 // 115 files.add(new File(tool.getOutputDirectory(), generator.getVocabFileName())); 116 // are we generating a .h file? 117 ST headerExtST = null; 118 ST extST = generator.getTemplates().getInstanceOf("codeFileExtension"); 119 if (generator.getTemplates().isDefined("headerFile")) { 120 headerExtST = generator.getTemplates().getInstanceOf("headerFileExtension"); 121 String suffix = Grammar.grammarTypeToFileNameSuffix[grammar.type]; 122 String fileName = grammar.name + suffix + headerExtST.render(); 123 files.add(new File(outputDir, fileName)); 124 } 125 if (grammar.type == Grammar.COMBINED) { 126 // add autogenerated lexer; e.g., TLexer.java TLexer.h TLexer.tokens 127 // don't add T__.g (just a temp file) 128 129 String suffix = Grammar.grammarTypeToFileNameSuffix[Grammar.LEXER]; 130 String lexer = grammar.name + suffix + extST.render(); 131 files.add(new File(outputDir, lexer)); 132 133 // TLexer.h 134 if (headerExtST != null) { 135 String header = grammar.name + suffix + headerExtST.render(); 136 files.add(new File(outputDir, header)); 137 } 138 // for combined, don't generate TLexer.tokens 139 } 140 141 // handle generated files for imported grammars 142 List<Grammar> imports = 143 grammar.composite.getDelegates(grammar.composite.getRootGrammar()); 144 for (Grammar g : imports) { 145 outputDir = tool.getOutputDirectory(g.getFileName()); 146 String fname = groomQualifiedFileName(outputDir.toString(), g.getRecognizerName() + extST.render()); 147 files.add(new File(fname)); 148 } 149 150 if (files.isEmpty()) { 151 return null; 152 } 153 return files; 154 } 155 156 /** 157 * Return a list of File objects that name files ANTLR will read 158 * to process T.g; This can be .tokens files if the grammar uses the tokenVocab option 159 * as well as any imported grammar files. 160 */ getDependenciesFileList()161 public List<File> getDependenciesFileList() { 162 // Find all the things other than imported grammars 163 List<File> files = getNonImportDependenciesFileList(); 164 165 // Handle imported grammars 166 List<Grammar> imports = 167 grammar.composite.getDelegates(grammar.composite.getRootGrammar()); 168 for (Grammar g : imports) { 169 String libdir = tool.getLibraryDirectory(); 170 String fileName = groomQualifiedFileName(libdir, g.fileName); 171 files.add(new File(fileName)); 172 } 173 174 if (files.isEmpty()) { 175 return null; 176 } 177 return files; 178 } 179 180 /** 181 * Return a list of File objects that name files ANTLR will read 182 * to process T.g; This can only be .tokens files and only 183 * if they use the tokenVocab option. 184 * 185 * @return List of dependencies other than imported grammars 186 */ getNonImportDependenciesFileList()187 public List<File> getNonImportDependenciesFileList() { 188 List<File> files = new ArrayList<File>(); 189 190 // handle token vocabulary loads 191 tokenVocab = (String) grammar.getOption("tokenVocab"); 192 if (tokenVocab != null) { 193 194 File vocabFile = tool.getImportedVocabFile(tokenVocab); 195 files.add(vocabFile); 196 } 197 198 return files; 199 } 200 getDependencies()201 public ST getDependencies() { 202 loadDependencyTemplates(); 203 ST dependenciesST = templates.getInstanceOf("dependencies"); 204 dependenciesST.add("in", getDependenciesFileList()); 205 dependenciesST.add("out", getGeneratedFileList()); 206 dependenciesST.add("grammarFileName", grammar.fileName); 207 return dependenciesST; 208 } 209 loadDependencyTemplates()210 public void loadDependencyTemplates() { 211 if (templates != null) return; 212 String fileName = "org/antlr/tool/templates/depend.stg"; 213 templates = new ToolSTGroupFile(fileName); 214 } 215 getTokenVocab()216 public String getTokenVocab() { 217 return tokenVocab; 218 } 219 getGenerator()220 public CodeGenerator getGenerator() { 221 return generator; 222 } 223 groomQualifiedFileName(String outputDir, String fileName)224 public String groomQualifiedFileName(String outputDir, String fileName) { 225 if (outputDir.equals(".")) { 226 return fileName; 227 } else if (outputDir.indexOf(' ') >= 0) { // has spaces? 228 String escSpaces = Utils.replace(outputDir.toString(), 229 " ", 230 "\\ "); 231 return escSpaces + File.separator + fileName; 232 } else { 233 return outputDir + File.separator + fileName; 234 } 235 } 236 } 237