Home | History | Annotate | Download | only in antlr3
      1 /**
      2 [The "BSD licence"]
      3 
      4 ANTLR        - Copyright (c) 2005-2008 Terence Parr
      5 Maven Plugin - Copyright (c) 2009      Jim Idle
      6 
      7 All rights reserved.
      8 
      9 Redistribution and use in source and binary forms, with or without
     10 modification, are permitted provided that the following conditions
     11 are met:
     12 1. Redistributions of source code must retain the above copyright
     13 notice, this list of conditions and the following disclaimer.
     14 2. Redistributions in binary form must reproduce the above copyright
     15 notice, this list of conditions and the following disclaimer in the
     16 documentation and/or other materials provided with the distribution.
     17 3. The name of the author may not be used to endorse or promote products
     18 derived from this software without specific prior written permission.
     19 
     20 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     21 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     22 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     23 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     24 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     25 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     29 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 /* ========================================================================
     33  * This is the definitive ANTLR3 Mojo set. All other sets are belong to us.
     34  */
     35 package org.antlr.mojo.antlr3;
     36 
     37 import org.apache.maven.plugin.AbstractMojo;
     38 import org.apache.maven.plugin.MojoExecutionException;
     39 import org.apache.maven.plugin.MojoFailureException;
     40 import org.apache.maven.project.MavenProject;
     41 
     42 import java.io.File;
     43 import java.io.IOException;
     44 import java.util.Collections;
     45 import java.util.HashSet;
     46 import java.util.Set;
     47 import org.antlr.Tool;
     48 import org.antlr.runtime.RecognitionException;
     49 import org.apache.maven.plugin.logging.Log;
     50 import org.codehaus.plexus.compiler.util.scan.InclusionScanException;
     51 import org.codehaus.plexus.compiler.util.scan.SimpleSourceInclusionScanner;
     52 import org.codehaus.plexus.compiler.util.scan.SourceInclusionScanner;
     53 import org.codehaus.plexus.compiler.util.scan.mapping.SourceMapping;
     54 import org.codehaus.plexus.compiler.util.scan.mapping.SuffixMapping;
     55 
     56 /**
     57  * Goal that picks up all the ANTLR grammars in a project and moves those that
     58  * are required for generation of the compilable sources into the location
     59  * that we use to compile them, such as target/generated-sources/antlr3 ...
     60  *
     61  * @goal antlr
     62  *
     63  * @phase process-sources
     64  * @requiresDependencyResolution compile
     65  * @requiresProject true
     66  *
     67  * @author <a href="mailto:jimi (at) temporal-wave.com">Jim Idle</a>
     68  */
     69 public class Antlr3Mojo
     70         extends AbstractMojo {
     71 
     72     // First, let's deal with the options that the ANTLR tool itself
     73     // can be configured by.
     74     //
     75     /**
     76      * If set to true, then after the tool has processed an input grammar file
     77      * it will report various statistics about the parser, such as information
     78      * on cyclic DFAs, which rules may use backtracking, and so on.
     79      *
     80      * @parameter default-value="false"
     81      */
     82     protected boolean report;
     83     /**
     84      * If set to true, then the ANTLR tool will print a version of the input
     85      * grammar which is devoid of any actions that may be present in the input file.
     86      *
     87      * @parameter default-value="false"
     88      */
     89     protected boolean printGrammar;
     90     /**
     91      * If set to true, then the code generated by the ANTLR code generator will
     92      * be set to debug mode. This means that when run, the code will 'hang' and
     93      * wait for a debug connection on a TCP port (49100 by default).
     94      *
     95      * @parameter default-value="false"
     96      */
     97     protected boolean debug;
     98     /**
     99      * If set to true, then then the generated parser will compute and report on
    100      * profile information at runtime.
    101      *
    102      * @parameter default-value="false"
    103      */
    104     protected boolean profile;
    105     /**
    106      * If set to true then the ANTLR tool will generate a description of the nfa
    107      * for each rule in <a href="http://www.graphviz.org">Dot format</a>
    108      *
    109      * @parameter default-value="false"
    110      */
    111     protected boolean nfa;
    112     /**
    113      * If set to true then the ANTLR tool will generate a description of the DFA
    114      * for each decision in the grammar in <a href="http://www.graphviz.org">Dot format</a>
    115      *
    116      * @parameter default-value="false"
    117      */
    118     protected boolean dfa;
    119     /**
    120      * If set to true, the generated parser code will log rule entry and exit points
    121      * to stdout as an aid to debugging.
    122      *
    123      * @parameter default-value="false"
    124      */
    125     protected boolean trace;
    126     /**
    127      * If this parameter is set, it indicates that any warning or error messages returned
    128      * by ANLTR, should be formatted in the specified way. Currently, ANTLR supports the
    129      * built-in formats of antlr, gnu and vs2005.
    130      *
    131      * @parameter default-value="antlr"
    132      */
    133     protected String messageFormat;
    134     /**
    135      * If this parameter is set to true, then ANTLR will report all sorts of things
    136      * about what it is doing such as the names of files and the version of ANTLR and so on.
    137      *
    138      * @parameter default-value="true"
    139      */
    140     protected boolean verbose;
    141 
    142     /**
    143      * The number of alts, beyond which ANTLR will not generate a switch statement
    144      * for the DFA.
    145      *
    146      * @parameter default-value="300"
    147      */
    148     private int maxSwitchCaseLabels;
    149 
    150     /**
    151      * The number of alts, below which ANTLR will not choose to generate a switch
    152      * statement over an if statement.
    153      */
    154     private int minSwitchAlts;
    155 
    156     /* --------------------------------------------------------------------
    157      * The following are Maven specific parameters, rather than specificlly
    158      * options that the ANTLR tool can use.
    159      */
    160     /**
    161      * Provides an explicit list of all the grammars that should
    162      * be included in the generate phase of the plugin. Note that the plugin
    163      * is smart enough to realize that imported grammars should be included but
    164      * not acted upon directly by the ANTLR Tool.
    165      *
    166      * Unless otherwise specified, the include list scans for and includes all
    167      * files that end in ".g" in any directory beneath src/main/antlr3. Note that
    168      * this version of the plugin looks for the directory antlr3 and not the directory
    169      * antlr, so as to avoid clashes and confusion for projects that use both v2 and v3 grammars
    170      * such as ANTLR itself.
    171      *
    172      * @parameter
    173      */
    174     protected Set includes = new HashSet();
    175     /**
    176      * Provides an explicit list of any grammars that should be excluded from
    177      * the generate phase of the plugin. Files listed here will not be sent for
    178      * processing by the ANTLR tool.
    179      *
    180      * @parameter
    181      */
    182     protected Set excludes = new HashSet();
    183     /**
    184      * @parameter expression="${project}"
    185      * @required
    186      * @readonly
    187      */
    188     protected MavenProject project;
    189     /**
    190      * Specifies the Antlr directory containing grammar files. For
    191      * antlr version 3.x we default this to a directory in the tree
    192      * called antlr3 because the antlr directory is occupied by version
    193      * 2.x grammars.
    194      *
    195      * @parameter default-value="${basedir}/src/main/antlr3"
    196      * @required
    197      */
    198     private File sourceDirectory;
    199     /**
    200      * Location for generated Java files. For antlr version 3.x we default
    201      * this to a directory in the tree called antlr3 because the antlr
    202      * directory is occupied by version 2.x grammars.
    203      *
    204      * @parameter default-value="${project.build.directory}/generated-sources/antlr3"
    205      * @required
    206      */
    207     private File outputDirectory;
    208     /**
    209      * Location for imported token files, e.g. <code>.tokens</code> and imported grammars.
    210      * Note that ANTLR will not try to process grammars that it finds to be imported
    211      * into other grammars (in the same processing session).
    212      *
    213      * @parameter default-value="${basedir}/src/main/antlr3/imports"
    214      */
    215     private File libDirectory;
    216 
    217     public File getSourceDirectory() {
    218         return sourceDirectory;
    219     }
    220 
    221     public File getOutputDirectory() {
    222         return outputDirectory;
    223     }
    224 
    225     public File getLibDirectory() {
    226         return libDirectory;
    227     }
    228 
    229     void addSourceRoot(File outputDir) {
    230         project.addCompileSourceRoot(outputDir.getPath());
    231     }
    232     /**
    233      * An instance of the ANTLR tool build
    234      */
    235     protected Tool tool;
    236 
    237     /**
    238      * The main entry point for this Mojo, it is responsible for converting
    239      * ANTLR 3.x grammars into the target language specified by the grammar.
    240      *
    241      * @throws org.apache.maven.plugin.MojoExecutionException When something is discovered such as a missing source
    242      * @throws org.apache.maven.plugin.MojoFailureException When something really bad happens such as not being able to create the ANTLR Tool
    243      */
    244     public void execute()
    245             throws MojoExecutionException, MojoFailureException {
    246 
    247         Log log = getLog();
    248 
    249         // Check to see if the user asked for debug information, then dump all the
    250         // parameters we have picked up if they did.
    251         //
    252         if (log.isDebugEnabled()) {
    253 
    254             // Excludes
    255             //
    256             for (String e : (Set<String>) excludes) {
    257 
    258                 log.debug("ANTLR: Exclude: " + e);
    259             }
    260 
    261             // Includes
    262             //
    263             for (String e : (Set<String>) includes) {
    264 
    265                 log.debug("ANTLR: Include: " + e);
    266             }
    267 
    268             // Output location
    269             //
    270             log.debug("ANTLR: Output: " + outputDirectory);
    271 
    272             // Library directory
    273             //
    274             log.debug("ANTLR: Library: " + libDirectory);
    275 
    276             // Flags
    277             //
    278             log.debug("ANTLR: report              : " + report);
    279             log.debug("ANTLR: printGrammar        : " + printGrammar);
    280             log.debug("ANTLR: debug               : " + debug);
    281             log.debug("ANTLR: profile             : " + profile);
    282             log.debug("ANTLR: nfa                 : " + nfa);
    283             log.debug("ANTLR: dfa                 : " + dfa);
    284             log.debug("ANTLR: trace               : " + trace);
    285             log.debug("ANTLR: messageFormat       : " + messageFormat);
    286             log.debug("ANTLR: maxSwitchCaseLabels : " + maxSwitchCaseLabels);
    287             log.debug("ANTLR: minSwitchAlts       : " + minSwitchAlts);
    288             log.debug("ANTLR: verbose             : " + verbose);
    289         }
    290 
    291         // Ensure that the output directory path is all in tact so that
    292         // ANTLR can just write into it.
    293         //
    294         File outputDir = getOutputDirectory();
    295 
    296         if (!outputDir.exists()) {
    297             outputDir.mkdirs();
    298         }
    299 
    300         // First thing we need is an instance of the ANTLR 3.1 build tool
    301         //
    302         try {
    303             // ANTLR Tool buld interface
    304             //
    305             tool = new Tool();
    306         } catch (Exception e) {
    307             log.error("The attempt to create the ANTLR build tool failed, see exception report for details");
    308 
    309             throw new MojoFailureException("Jim failed you!");
    310         }
    311 
    312         // Next we need to set the options given to us in the pom into the
    313         // tool instance we have created.
    314         //
    315         tool.setDebug(debug);
    316         tool.setGenerate_DFA_dot(dfa);
    317         tool.setGenerate_NFA_dot(nfa);
    318         tool.setProfile(profile);
    319         tool.setReport(report);
    320         tool.setPrintGrammar(printGrammar);
    321         tool.setTrace(trace);
    322         tool.setVerbose(verbose);
    323         tool.setMessageFormat(messageFormat);
    324         tool.setMaxSwitchCaseLabels(maxSwitchCaseLabels);
    325         tool.setMinSwitchAlts(minSwitchAlts);
    326 
    327         // Where do we want ANTLR to produce its output? (Base directory)
    328         //
    329         if (log.isDebugEnabled())
    330         {
    331             log.debug("Output directory base will be " + outputDirectory.getAbsolutePath());
    332         }
    333         tool.setOutputDirectory(outputDirectory.getAbsolutePath());
    334 
    335         // Tell ANTLR that we always want the output files to be produced in the output directory
    336         // using the same relative path as the input file was to the input directory.
    337         //
    338         tool.setForceRelativeOutput(true);
    339 
    340         // Where do we want ANTLR to look for .tokens and import grammars?
    341         //
    342         tool.setLibDirectory(libDirectory.getAbsolutePath());
    343 
    344         if (!sourceDirectory.exists()) {
    345             if (log.isInfoEnabled()) {
    346                 log.info("No ANTLR grammars to compile in " + sourceDirectory.getAbsolutePath());
    347             }
    348             return;
    349         } else {
    350             if (log.isInfoEnabled()) {
    351                 log.info("ANTLR: Processing source directory " + sourceDirectory.getAbsolutePath());
    352             }
    353         }
    354 
    355         // Set working directory for ANTLR to be the base source directory
    356         //
    357         tool.setInputDirectory(sourceDirectory.getAbsolutePath());
    358 
    359         try {
    360 
    361             // Now pick up all the files and process them with the Tool
    362             //
    363             processGrammarFiles(sourceDirectory, outputDirectory);
    364 
    365         } catch (InclusionScanException ie) {
    366 
    367             log.error(ie);
    368             throw new MojoExecutionException("Fatal error occured while evaluating the names of the grammar files to analyze");
    369 
    370         } catch (Exception e) {
    371 
    372             getLog().error(e);
    373             throw new MojoExecutionException(e.getMessage());
    374         }
    375 
    376 
    377 
    378         tool.process();
    379 
    380         // If any of the grammar files caused errors but did nto throw exceptions
    381         // then we should have accumulated errors in the counts
    382         //
    383         if (tool.getNumErrors() > 0) {
    384             throw new MojoExecutionException("ANTLR caught " + tool.getNumErrors() + " build errors.");
    385         }
    386 
    387         // All looks good, so we need to tel Maven about the sources that
    388         // we just created.
    389         //
    390         if (project != null) {
    391             // Tell Maven that there are some new source files underneath
    392             // the output directory.
    393             //
    394             addSourceRoot(this.getOutputDirectory());
    395         }
    396 
    397     }
    398 
    399 
    400     /**
    401      *
    402      * @param sourceDirectory
    403      * @param outputDirectory
    404      * @throws antlr.TokenStreamException
    405      * @throws antlr.RecognitionException
    406      * @throws java.io.IOException
    407      * @throws org.codehaus.plexus.compiler.util.scan.InclusionScanException
    408      */
    409     private void processGrammarFiles(File sourceDirectory, File outputDirectory)
    410             throws RecognitionException, IOException, InclusionScanException {
    411         // Which files under the source set should we be looking for as grammar files
    412         //
    413         SourceMapping mapping = new SuffixMapping("g", Collections.EMPTY_SET);
    414 
    415         // What are the sets of includes (defaulted or otherwise).
    416         //
    417         Set includes = getIncludesPatterns();
    418 
    419         // Now, to the excludes, we need to add the imports directory
    420         // as this is autoscanned for importd grammars and so is auto-excluded from the
    421         // set of gramamr fiels we shuold be analyzing.
    422         //
    423         excludes.add("imports/**");
    424 
    425         SourceInclusionScanner scan = new SimpleSourceInclusionScanner(includes, excludes);
    426 
    427         scan.addSourceMapping(mapping);
    428         Set grammarFiles = scan.getIncludedSources(sourceDirectory, null);
    429 
    430         if (grammarFiles.isEmpty()) {
    431             if (getLog().isInfoEnabled()) {
    432                 getLog().info("No grammars to process");
    433             }
    434         } else {
    435 
    436             // Tell the ANTLR tool that we want sorted build mode
    437             //
    438             tool.setMake(true);
    439 
    440             // Iterate each grammar file we were given and add it into the tool's list of
    441             // grammars to process.
    442             //
    443             for (File grammar : (Set<File>) grammarFiles) {
    444 
    445                 if (getLog().isDebugEnabled()) {
    446                     getLog().debug("Grammar file '" + grammar.getPath() + "' detected.");
    447                 }
    448 
    449 
    450                 String relPath = findSourceSubdir(sourceDirectory, grammar.getPath()) + grammar.getName();
    451 
    452                 if (getLog().isDebugEnabled()) {
    453                     getLog().debug("  ... relative path is: " + relPath);
    454                 }
    455                 tool.addGrammarFile(relPath);
    456 
    457             }
    458 
    459         }
    460 
    461 
    462     }
    463 
    464     public Set getIncludesPatterns() {
    465         if (includes == null || includes.isEmpty()) {
    466             return Collections.singleton("**/*.g");
    467         }
    468         return includes;
    469     }
    470 
    471     /**
    472      * Given the source directory File object and the full PATH to a
    473      * grammar, produce the path to the named grammar file in relative
    474      * terms to the sourceDirectory. This will then allow ANTLR to
    475      * produce output relative to the base of the output directory and
    476      * reflect the input organization of the grammar files.
    477      *
    478      * @param sourceDirectory The source directory File object
    479      * @param grammarFileName The full path to the input grammar file
    480      * @return The path to the grammar file relative to the source directory
    481      */
    482     private String findSourceSubdir(File sourceDirectory, String grammarFileName) {
    483         String srcPath = sourceDirectory.getPath() + File.separator;
    484 
    485         if (!grammarFileName.startsWith(srcPath)) {
    486             throw new IllegalArgumentException("expected " + grammarFileName + " to be prefixed with " + sourceDirectory);
    487         }
    488 
    489         File unprefixedGrammarFileName = new File(grammarFileName.substring(srcPath.length()));
    490 
    491         return unprefixedGrammarFileName.getParent() + File.separator;
    492     }
    493 }
    494