• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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