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 import org.stringtemplate.v4.STGroupFile; 36 37 import java.io.*; 38 import java.util.ArrayList; 39 import java.util.List; 40 41 /** Given a grammar file, show the dependencies on .tokens etc... 42 * Using ST, emit a simple "make compatible" list of dependencies. 43 * For example, combined grammar T.g (no token import) generates: 44 * 45 * TParser.java : T.g 46 * T.tokens : T.g 47 * T__g : T.g 48 * 49 * For tree grammar TP with import of T.tokens: 50 * 51 * TP.g : T.tokens 52 * TP.java : TP.g 53 * 54 * If "-lib libdir" is used on command-line with -depend, then include the 55 * path like 56 * 57 * TP.g : libdir/T.tokens 58 * 59 * Pay attention to -o as well: 60 * 61 * outputdir/TParser.java : T.g 62 * 63 * So this output shows what the grammar depends on *and* what it generates. 64 * 65 * Operate on one grammar file at a time. If given a list of .g on the 66 * command-line with -depend, just emit the dependencies. The grammars 67 * may depend on each other, but the order doesn't matter. Build tools, 68 * reading in this output, will know how to organize it. 69 * 70 * This is a wee bit slow probably because the code generator has to load 71 * all of its template files in order to figure out the file extension 72 * for the generated recognizer. 73 * 74 * This code was obvious until I removed redundant "./" on front of files 75 * and had to escape spaces in filenames :( 76 */ 77 public class BuildDependencyGenerator { 78 protected String grammarFileName; 79 protected String tokenVocab; 80 protected Tool tool; 81 protected Grammar grammar; 82 protected CodeGenerator generator; 83 protected STGroup templates; 84 BuildDependencyGenerator(Tool tool, String grammarFileName)85 public BuildDependencyGenerator(Tool tool, String grammarFileName) 86 throws IOException { 87 this.tool = tool; 88 this.grammarFileName = grammarFileName; 89 grammar = tool.getRootGrammar(grammarFileName); 90 String language = (String) grammar.getOption("language"); 91 generator = new CodeGenerator(tool, grammar, language); 92 generator.loadTemplates(language); 93 } 94 95 /** From T.g return a list of File objects that 96 * name files ANTLR will emit from T.g. 97 */ getGeneratedFileList()98 public List<File> getGeneratedFileList() { 99 List<File> files = new ArrayList<File>(); 100 File outputDir = tool.getOutputDirectory(grammarFileName); 101 if (outputDir.getName().equals(".")) { 102 outputDir = null; 103 } else if (outputDir.getName().indexOf(' ') >= 0) { // has spaces? 104 String escSpaces = Utils.replace(outputDir.toString(), 105 " ", 106 "\\ "); 107 outputDir = new File(escSpaces); 108 } 109 // add generated recognizer; e.g., TParser.java 110 String recognizer = 111 generator.getRecognizerFileName(grammar.name, grammar.type); 112 files.add(new File(outputDir, recognizer)); 113 // add output vocab file; e.g., T.tokens. This is always generated to 114 // the base output directory, which will be just . if there is no -o option 115 // 116 files.add(new File(tool.getOutputDirectory(), generator.getVocabFileName())); 117 // are we generating a .h file? 118 ST headerExtST = null; 119 ST extST = generator.getTemplates().getInstanceOf("codeFileExtension"); 120 if (generator.getTemplates().isDefined("headerFile")) { 121 headerExtST = generator.getTemplates().getInstanceOf("headerFileExtension"); 122 String suffix = Grammar.grammarTypeToFileNameSuffix[grammar.type]; 123 String fileName = grammar.name + suffix + headerExtST.render(); 124 files.add(new File(outputDir, fileName)); 125 } 126 if (grammar.type == Grammar.COMBINED) { 127 // add autogenerated lexer; e.g., TLexer.java TLexer.h TLexer.tokens 128 // don't add T__.g (just a temp file) 129 130 String suffix = Grammar.grammarTypeToFileNameSuffix[Grammar.LEXER]; 131 String lexer = grammar.name + suffix + extST.render(); 132 files.add(new File(outputDir, lexer)); 133 134 // TLexer.h 135 if (headerExtST != null) { 136 String header = grammar.name + suffix + headerExtST.render(); 137 files.add(new File(outputDir, header)); 138 } 139 // for combined, don't generate TLexer.tokens 140 } 141 142 // handle generated files for imported grammars 143 List<Grammar> imports = 144 grammar.composite.getDelegates(grammar.composite.getRootGrammar()); 145 for (Grammar g : imports) { 146 outputDir = tool.getOutputDirectory(g.getFileName()); 147 String fname = groomQualifiedFileName(outputDir.toString(), g.getRecognizerName() + extST.render()); 148 files.add(new File(fname)); 149 } 150 151 if (files.size() == 0) { 152 return null; 153 } 154 return files; 155 } 156 157 /** 158 * Return a list of File objects that name files ANTLR will read 159 * to process T.g; This can be .tokens files if the grammar uses the tokenVocab option 160 * as well as any imported grammar files. 161 */ getDependenciesFileList()162 public List<File> getDependenciesFileList() { 163 // Find all the things other than imported grammars 164 List<File> files = getNonImportDependenciesFileList(); 165 166 // Handle imported grammars 167 List<Grammar> imports = 168 grammar.composite.getDelegates(grammar.composite.getRootGrammar()); 169 for (Grammar g : imports) { 170 String libdir = tool.getLibraryDirectory(); 171 String fileName = groomQualifiedFileName(libdir, g.fileName); 172 files.add(new File(fileName)); 173 } 174 175 if (files.size() == 0) { 176 return null; 177 } 178 return files; 179 } 180 181 /** 182 * Return a list of File objects that name files ANTLR will read 183 * to process T.g; This can only be .tokens files and only 184 * if they use the tokenVocab option. 185 * 186 * @return List of dependencies other than imported grammars 187 */ getNonImportDependenciesFileList()188 public List<File> getNonImportDependenciesFileList() { 189 List<File> files = new ArrayList<File>(); 190 191 // handle token vocabulary loads 192 tokenVocab = (String) grammar.getOption("tokenVocab"); 193 if (tokenVocab != null) { 194 195 File vocabFile = tool.getImportedVocabFile(tokenVocab); 196 files.add(vocabFile); 197 } 198 199 return files; 200 } 201 getDependencies()202 public ST getDependencies() { 203 loadDependencyTemplates(); 204 ST dependenciesST = templates.getInstanceOf("dependencies"); 205 dependenciesST.add("in", getDependenciesFileList()); 206 dependenciesST.add("out", getGeneratedFileList()); 207 dependenciesST.add("grammarFileName", grammar.fileName); 208 return dependenciesST; 209 } 210 loadDependencyTemplates()211 public void loadDependencyTemplates() { 212 if (templates != null) return; 213 String fileName = "org/antlr/tool/templates/depend.stg"; 214 templates = new STGroupFile(fileName); 215 } 216 getTokenVocab()217 public String getTokenVocab() { 218 return tokenVocab; 219 } 220 getGenerator()221 public CodeGenerator getGenerator() { 222 return generator; 223 } 224 groomQualifiedFileName(String outputDir, String fileName)225 public String groomQualifiedFileName(String outputDir, String fileName) { 226 if (outputDir.equals(".")) { 227 return fileName; 228 } else if (outputDir.indexOf(' ') >= 0) { // has spaces? 229 String escSpaces = Utils.replace(outputDir.toString(), 230 " ", 231 "\\ "); 232 return escSpaces + File.separator + fileName; 233 } else { 234 return outputDir + File.separator + fileName; 235 } 236 } 237 } 238