All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jastadd.JastAddPlugin.groovy Maven / Gradle / Ivy

There is a newer version: 1.13.3
Show newest version
/* Copyright (c) 2014-2018, Jesper Öqvist
 * 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. Neither the name of the copyright holder nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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.jastadd

import org.gradle.api.*
import org.gradle.api.tasks.*
import org.gradle.api.file.*
import org.gradle.api.logging.*

class JastAddPlugin implements Plugin {

  void apply(Project project) {
    def jastadd = project.extensions.create('jastadd', JastAddExtension, project)

    project.configurations.create('jastadd2')
    project.configurations['jastadd2'].defaultDependencies { deps ->
      deps.add(project.dependencies.create('org.jastadd:jastadd:2.3.2'))
    }

    project.repositories {
      mavenCentral()
    }
  }

}

class ScannerConfig {
  /** The name of the generated scanner (default="Scanner"). */
  String name = 'Scanner'
}

class ParserConfig {
  /** Generated parser name (default="Parser"). */
  String name = 'Parser'
}

class JastAddExtension {
  private static final Logger LOG = Logging.getLogger(JastAddPlugin.class)

  Project project
  final ModuleLoader loader

  /** All loaded modules. */
  List modules = []

  /** All module sources. */
  List moduleSources = []

  /** Rag root directory for documentation generation. */
  String ragroot

  JastAddExtension(Project project) {
    loader = new ModuleLoader(this)
    this.project = project
    this.ragroot = project.rootDir
  }

  void configureModuleBuild() {
    LOG.info("Configuring JastAdd build for ${project.name}.")

    project.gradle.taskGraph.whenReady {
      // Run some checks before the build starts.
      if (!module) {
        LOG.warn("No target JastAdd module is configured for ${project}. "
            + 'Add jastadd.module = "..." to fix this.')
      }
      module?.checkEmptyIncludes(project)
    }

    project.configurations.create('jastaddParser')
    project.configurations['jastaddParser'].defaultDependencies { deps ->
      deps.add(project.dependencies.create('org.jastadd:jastaddparser:1.0.3'))
      deps.add(project.dependencies.create('net.sf.beaver:beaver-rt:0.9.11'))
    }

    project.configurations.create('jflex')
    project.configurations['jflex'].defaultDependencies { deps ->
      deps.add(project.dependencies.create('de.jflex:jflex:1.6.1'))
    }

    project.configurations.create('beaver')
    project.configurations['beaver'].defaultDependencies { deps ->
      deps.add(project.dependencies.create('net.sf.beaver:beaver-ant:0.9.11'))
    }

    project.compileJava.source "${project.buildDir}/generated-src/ast"
    project.compileJava.source "${project.buildDir}/generated-src/parser"
    project.compileJava.source "${project.buildDir}/generated-src/scanner"

    project.task('generateAst', type: JavaExec) {
      description 'Generates Java sources from JastAdd code.'

      onlyIf { module }

      def genDir = "${project.buildDir}/generated-src/ast"
      inputs.files { moduleSources + (module ? module.files(project, 'jastadd') : []) }
      outputs.dir { project.file(genDir) }

      classpath = project.configurations.jastadd2
      main = 'org.jastadd.JastAdd'

      doFirst {
        def outdir = project.file(genDir)
        if (outdir.isDirectory()) {
          // Clean output directory.
          project.fileTree(outdir).visit{ file -> file.getFile().delete() }
        } else {
          outdir.mkdirs()
        }
        def addBeaverOption
        if (useBeaver == 'maybe') {
          addBeaverOption = !module.files(project, 'parser').isEmpty()
        } else {
          addBeaverOption = useBeaver
        }
        def beaverOption = addBeaverOption ? [ '--beaver' ] : []
        args (
            [ "--package=${astPackage}",
            "--o=${outdir.path}" ]
            + beaverOption
            + jastaddOptions
            + extraJastAddOptions
            + module.files(project, 'jastadd'))
      }
    }

    project.task('generateScanner', type: JavaExec) {
      description 'Generates scanner with JFlex.'

      // Generate scanner only if there are some source files.
      onlyIf { module && !module.files(project, 'scanner').isEmpty() }

      def genDir = "${project.buildDir}/generated-src/scanner"
      inputs.files { moduleSources + (module ? module.files(project, 'scanner') : []) }
      outputs.dir { project.file(genDir) }

      classpath = project.configurations.jflex
      main = 'jflex.Main'

      doFirst {
        def inputFiles = project.files(module.files(project, 'scanner'))
        def outdir = project.file(genDir)
        if (outdir.isDirectory()) {
          // Clean output directory.
          project.fileTree(outdir).visit{ file -> file.getFile().delete() }
        } else {
          outdir.mkdirs()
        }
        ant.concat(destfile: "${temporaryDir}/${scanner.name}.flex",
          binary: true, force: false) {
          inputFiles.addToAntBuilder(ant, "fileset", FileCollection.AntType.FileSet)
        }
        args ([ '-d', outdir.path, "${temporaryDir}/${scanner.name}.flex" ])
      }
    }

    project.task('ragdoc', type: JavaExec) {
      description 'Generates RagDoc metadata for this JastAdd project.'
      dependsOn 'generateAst'

      // RD-Builder dependency should be added by user in the ragdoc
      // configuration (only needed if ragdoc task is run).

      inputs.files {
        project.files(module.files(project, 'java')) + project.sourceSets.main.allJava.files
      }

      main = 'org.extendj.ragdoc.RagDocBuilder'

      doFirst {
        def destDir = new File(project.docsDir, 'ragdoc')
        if (!destDir.isDirectory()) {
          destDir.mkdirs()
        }
        classpath = project.configurations.ragdoc
        def sourceFiles = project.sourceSets.main.allJava.files
        args ([ '-cp', project.sourceSets.main.compileClasspath.asPath,
            '-d', "$destDir",
            '-ragroot', "$ragroot" ]
            + project.files(module.files(project, 'java'))
            + sourceFiles)
      }
    }

    project.task('preprocessParser', type: JavaExec) {
      description 'Generates Beaver parser with JastAddParser.'

      // Generate parser only if there are some source files.
      onlyIf { module && !module.files(project, 'parser').isEmpty() }

      inputs.files { moduleSources + (module ? module.files(project, 'parser') : []) }
      outputs.file {
        project.file("${temporaryDir}/${parser.name}.beaver")
      }

      classpath = project.configurations.jastaddParser
      main = 'org.jastadd.jastaddparser.Main'

      doFirst {
        def inputFiles = project.files(module.files(project, 'parser'))
        ant.concat(destfile: "${temporaryDir}/${parser.name}.all",
          binary: true, force: false) {
          inputFiles.addToAntBuilder(ant, "fileset", FileCollection.AntType.FileSet)
        }
        def allFile = project.file("${temporaryDir}/${parser.name}.all")
        def beaverFile = project.file("${temporaryDir}/${parser.name}.beaver")
        args ([ allFile.path, beaverFile.path ])
      }
    }

    project.task('generateParser', type: JavaExec, dependsOn: [ 'preprocessParser' ]) {
      description 'Generates parser with Beaver.'

      // Generate parser only if there are some source files.
      onlyIf { module && !module.files(project, 'parser').isEmpty() }

      def genDir = "${project.buildDir}/generated-src/parser"
      inputs.files {
        project.file("${project.preprocessParser.temporaryDir}/${parser.name}.beaver")
      }
      outputs.dir { project.file(genDir) }

      classpath = project.configurations.beaver
      main = 'beaver.comp.run.Make'

      doFirst {
        def outdir = project.file(genDir)
        if (outdir.isDirectory()) {
          // Clean output directory.
          project.fileTree(outdir).visit{ file -> file.getFile().delete() }
        } else {
          outdir.mkdirs()
        }
        def inputFile = project.file("${project.preprocessParser.temporaryDir}/${parser.name}.beaver")
        args ([ '-d', outdir.path, '-t', '-c', '-w', inputFile.path ])
      }
    }

    project.task('buildInfo') {
      description 'Generates a property file with the module name.'
      outputs.dir { if (buildInfoDir != null) project.file(buildInfoDir) }

      // Generate build info only if buildInfoDir is set.
      onlyIf { module && !(buildInfoDir ?: "").isEmpty() }

      doLast {
        if (buildInfoDir) {
          def date = new Date()
          ant.mkdir dir: "${buildInfoDir}"
          ant.propertyfile(file: "${buildInfoDir}/BuildInfo.properties") {
            entry(key: 'moduleName', value: module.moduleName())
            entry(key: 'moduleVariant', value: module.moduleVariant())
            entry(key: 'timestamp', value: date.format("yyyy-MM-dd'T'HH:mm'Z'"))
            entry(key: 'build.date', value: date.format('yyyy-MM-dd'))
          }
        }
      }
    }

    project.task('cleanGen', type: Delete) {
      description 'Removes generated files.'
      delete {
        def dirs = [
          "${project.buildDir}/generated-src/ast",
          "${project.buildDir}/generated-src/scanner",
          "${project.buildDir}/generated-src/parser",
          buildInfoDir
        ]
        dirs.removeAll([null])
        dirs
      }
    }

    project.clean.dependsOn 'cleanGen'
    project.compileJava.dependsOn 'generateAst', 'generateScanner', 'generateParser'
    project.processResources.dependsOn 'buildInfo'
  }

  /** Load module specifications. */
  void modules(String... modules) {
    modules.each { loader.load(project.file(it)) }
  }

  void modules(File... modules) {
    modules.each { loader.load(it) }
  }

  // Defines modules with a closure.
  void modules(Closure closure) {
    loader.load(closure, project.projectDir)
  }

  /** Module instance. */
  private JastAddModule module

  /** Set the target module. */
  public void setModule(String name) {
    if (module != null) {
      throw new InvalidUserDataException("Target module already selected!")
    }
    module = getModule(name)
    // Add Java sources included in modules to source set.
    project.compileJava.source project.files(module.files(project, 'java')),
        project.sourceSets.main.java
  }

  /** Get the target module instance. */
  public JastAddModule getModule() {
    module
  }

  public void addModuleSource(source) {
    moduleSources.add source
  }

  /** Supported Java version. */
  String javaVersion

  String astPackage
  String buildInfoDir

  /** Set to {@code true} or {@code false} in JastAdd configuration. */
  def useBeaver = 'maybe'

  /**
   * Default set of JastAdd options
   * (excluding package name, beaver setting, and output directory).
   */
  List jastaddOptions = [
      '--rewrite=cnta',
      '--safeLazy',
      '--visitCheck=false',
      '--cacheCycle=false' ];

  /** List of extra options to append to the default option list. */
  List extraJastAddOptions = [];

  ScannerConfig scanner = new ScannerConfig()
  ParserConfig parser = new ParserConfig()

  public void addModule(module) {
    modules.add module
  }

  def getModule(name) {
    for (module in modules) {
      if (module.name == name) {
        return module
      }
    }
    throw new InvalidUserDataException("Unknown JastAdd module \"${name}\".")
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy