/* * [The "BSD license"] * Copyright (c) 2010 Terence Parr * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.antlr.tool; import org.antlr.Tool; import org.antlr.codegen.CodeGenerator; import org.antlr.misc.Utils; import org.stringtemplate.v4.ST; import org.stringtemplate.v4.STGroup; import org.stringtemplate.v4.STGroupFile; import java.io.*; import java.util.ArrayList; import java.util.List; /** Given a grammar file, show the dependencies on .tokens etc... * Using ST, emit a simple "make compatible" list of dependencies. * For example, combined grammar T.g (no token import) generates: * * TParser.java : T.g * T.tokens : T.g * T__g : T.g * * For tree grammar TP with import of T.tokens: * * TP.g : T.tokens * TP.java : TP.g * * If "-lib libdir" is used on command-line with -depend, then include the * path like * * TP.g : libdir/T.tokens * * Pay attention to -o as well: * * outputdir/TParser.java : T.g * * So this output shows what the grammar depends on *and* what it generates. * * Operate on one grammar file at a time. If given a list of .g on the * command-line with -depend, just emit the dependencies. The grammars * may depend on each other, but the order doesn't matter. Build tools, * reading in this output, will know how to organize it. * * This is a wee bit slow probably because the code generator has to load * all of its template files in order to figure out the file extension * for the generated recognizer. * * This code was obvious until I removed redundant "./" on front of files * and had to escape spaces in filenames :( */ public class BuildDependencyGenerator { protected String grammarFileName; protected String tokenVocab; protected Tool tool; protected Grammar grammar; protected CodeGenerator generator; protected STGroup templates; public BuildDependencyGenerator(Tool tool, String grammarFileName) throws IOException { this.tool = tool; this.grammarFileName = grammarFileName; grammar = tool.getRootGrammar(grammarFileName); String language = (String) grammar.getOption("language"); generator = new CodeGenerator(tool, grammar, language); generator.loadTemplates(language); } /** From T.g return a list of File objects that * name files ANTLR will emit from T.g. */ public List getGeneratedFileList() { List files = new ArrayList(); File outputDir = tool.getOutputDirectory(grammarFileName); if (outputDir.getName().equals(".")) { outputDir = null; } else if (outputDir.getName().indexOf(' ') >= 0) { // has spaces? String escSpaces = Utils.replace(outputDir.toString(), " ", "\\ "); outputDir = new File(escSpaces); } // add generated recognizer; e.g., TParser.java String recognizer = generator.getRecognizerFileName(grammar.name, grammar.type); files.add(new File(outputDir, recognizer)); // add output vocab file; e.g., T.tokens. This is always generated to // the base output directory, which will be just . if there is no -o option // files.add(new File(tool.getOutputDirectory(), generator.getVocabFileName())); // are we generating a .h file? ST headerExtST = null; ST extST = generator.getTemplates().getInstanceOf("codeFileExtension"); if (generator.getTemplates().isDefined("headerFile")) { headerExtST = generator.getTemplates().getInstanceOf("headerFileExtension"); String suffix = Grammar.grammarTypeToFileNameSuffix[grammar.type]; String fileName = grammar.name + suffix + headerExtST.render(); files.add(new File(outputDir, fileName)); } if (grammar.type == Grammar.COMBINED) { // add autogenerated lexer; e.g., TLexer.java TLexer.h TLexer.tokens // don't add T__.g (just a temp file) String suffix = Grammar.grammarTypeToFileNameSuffix[Grammar.LEXER]; String lexer = grammar.name + suffix + extST.render(); files.add(new File(outputDir, lexer)); // TLexer.h if (headerExtST != null) { String header = grammar.name + suffix + headerExtST.render(); files.add(new File(outputDir, header)); } // for combined, don't generate TLexer.tokens } // handle generated files for imported grammars List imports = grammar.composite.getDelegates(grammar.composite.getRootGrammar()); for (Grammar g : imports) { outputDir = tool.getOutputDirectory(g.getFileName()); String fname = groomQualifiedFileName(outputDir.toString(), g.getRecognizerName() + extST.render()); files.add(new File(fname)); } if (files.size() == 0) { return null; } return files; } /** * Return a list of File objects that name files ANTLR will read * to process T.g; This can be .tokens files if the grammar uses the tokenVocab option * as well as any imported grammar files. */ public List getDependenciesFileList() { // Find all the things other than imported grammars List files = getNonImportDependenciesFileList(); // Handle imported grammars List imports = grammar.composite.getDelegates(grammar.composite.getRootGrammar()); for (Grammar g : imports) { String libdir = tool.getLibraryDirectory(); String fileName = groomQualifiedFileName(libdir, g.fileName); files.add(new File(fileName)); } if (files.size() == 0) { return null; } return files; } /** * Return a list of File objects that name files ANTLR will read * to process T.g; This can only be .tokens files and only * if they use the tokenVocab option. * * @return List of dependencies other than imported grammars */ public List getNonImportDependenciesFileList() { List files = new ArrayList(); // handle token vocabulary loads tokenVocab = (String) grammar.getOption("tokenVocab"); if (tokenVocab != null) { File vocabFile = tool.getImportedVocabFile(tokenVocab); files.add(vocabFile); } return files; } public ST getDependencies() { loadDependencyTemplates(); ST dependenciesST = templates.getInstanceOf("dependencies"); dependenciesST.add("in", getDependenciesFileList()); dependenciesST.add("out", getGeneratedFileList()); dependenciesST.add("grammarFileName", grammar.fileName); return dependenciesST; } public void loadDependencyTemplates() { if (templates != null) return; String fileName = "org/antlr/tool/templates/depend.stg"; templates = new STGroupFile(fileName); } public String getTokenVocab() { return tokenVocab; } public CodeGenerator getGenerator() { return generator; } public String groomQualifiedFileName(String outputDir, String fileName) { if (outputDir.equals(".")) { return fileName; } else if (outputDir.indexOf(' ') >= 0) { // has spaces? String escSpaces = Utils.replace(outputDir.toString(), " ", "\\ "); return escSpaces + File.separator + fileName; } else { return outputDir + File.separator + fileName; } } }