Skip to content

Latest commit

 

History

History
286 lines (236 loc) · 8.24 KB

README.adoc

File metadata and controls

286 lines (236 loc) · 8.24 KB

Scala Dialect for Polyglot Maven

Enabling the Polyglot Scala Extension

In your project extension configuration .mvn/extensions.xml add the following:

<extensions xmlns="http://maven.apache.org/EXTENSIONS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/EXTENSIONS/1.0.0 http://maven.apache.org/xsd/core-extensions-1.0.0.xsd">
  <extension>
    <groupId>io.takari.polyglot</groupId>
    <artifactId>polyglot-scala</artifactId>
    <version>0.4.9</version>
  </extension>
</extensions>

Converting existing pom.xml files

You can (initially) convert existing projects with the polyglot-translate-plugin.

mvn io.takari.polyglot:polyglot-translate-plugin:0.4.9:translate -Dinput=pom.xml -Doutput=pom.scala

Example

As an example, the pom.xml of this project pom (in version 0.4.9) written as pom.scala would look like:

import org.sonatype.maven.polyglot.scala.model._
import scala.collection.immutable.Seq

Model(
  "" % "polyglot-scala",
  packaging = "takari-jar",
  name = "Polyglot :: Scala",
  contributors = Seq(
    Contributor(
      name = "Christopher Hunt",
      organization = "Typesafe",
      organizationUrl = "http://typesafe.com"
    ),
    Contributor(
      name = "Tobias Roeser",
      url = "https://github.com/lefou"
    )
  ),
  parent = Parent(
    gav = "io.takari.polyglot" % "polyglot" % "0.4.7-SNAPSHOT"
  ),
  repositories = Seq(
    Repository(
      snapshots = RepositoryPolicy(
        enabled = false
      ),
      id = "sonatype-public-grid",
      url = "http://repository.sonatype.org/content/groups/sonatype-public-grid/"
    )
  ),
  dependencies = Seq(
    "io.takari.polyglot" % "polyglot-common" % "",
    "com.twitter" % "util-eval_2.11" % "6.43.0",
    "com.googlecode.kiama" % "kiama_2.11" % "1.8.0",
    "org.scala-lang" % "scala-library" % "${scala.version}",
    "org.scala-lang" % "scala-reflect" % "${scala.version}",
    "org.scala-lang" % "scala-compiler" % "${scala.version}",
    "org.specs2" % "specs2-junit_2.11" % "3.9.5" % "test"
  ),
  properties = Map(
    "scala.version" -> "2.11.12"
  ),
  build = Build(
    sourceDirectory = "src/main/scala",
    testSourceDirectory = "src/test/scala",
    pluginManagement = PluginManagement(
      plugins = Seq(
        Plugin(
          "org.eclipse.m2e" % "lifecycle-mapping" % "1.0.0",
          configuration = Config(
            lifecycleMappingMetadata = Config(
              pluginExecutions = Config(
                pluginExecution = Config(
                  pluginExecutionFilter = Config(
                    groupId = "net.alchim31.maven",
                    artifactId = "scala-maven-plugin",
                    versionRange = "[3.3.0,)",
                    goals = Config(
                      goal = "add-source",
                      goal = "compile",
                      goal = "testCompile"
                    )
                  ),
                  action = Config(
                    ignore = None
                  )
                )
              )
            )
          )
        )
      )
    ),
    plugins = Seq(
      Plugin(
        "org.apache.maven.plugins" % "maven-compiler-plugin",
        executions = Seq(
          Execution(
            id = "default-compile",
            phase = "none"
          )
        )
      ),
      Plugin(
        "net.alchim31.maven" % "scala-maven-plugin" % "4.3.1",
        executions = Seq(
          Execution(
            goals = Seq(
              "compile",
              "testCompile"
            ),
            configuration = Config(
              args = Config(
                arg = "-deprecation",
                arg = "-feature"
              ),
              checkMultipleScalaVersions = "false",
              recompileMode = "incremental",
              useZincServer = "true"
            )
          )
        )
      ),
      Plugin(
        "org.apache.maven.plugins" % "maven-surefire-plugin",
        configuration = Config(
          includes = Config(
            include = "%regex[.*Spec.*]"
          )
        )
      )
    )
  ),
  modelVersion = "4.0.0"
)

More examples…​

Features

  • Supports the complete Maven POM model

  • compact sbt-like notation for dependencies: group % artiact % version % scope

  • Dynamic notation of plugin configurations with Config

  • Provides full Scala Language features to write poms

  • Conversion to and from pom.xml via the polyglot-translate-plugin

  • Powerful include feature, to share configuration code between poms

  • Works out of the box with Maven 3.3.1+

Scala-specific features

Scala binary version specific dependencies

Polyglot-Scala allows you to write sbt-like scala-binary-version specific dependencies with %%.

import org.sonatype.maven.polyglot.scala.model._
import scala.collection.immutable.Seq

implicit val scalaVersion = ScalaVersion("2.12.6")

Model(
  dependencies = Seq(
    "com.typesafe.akka" %% "akka-actor" % "2.5.9"
  )
  // ...
)

which is preferred over

"com.typesafe.akka" % s"akka-actor_${scalaVersion.binaryVersion}" % "2.5.9"

or even

"com.typesafe.akka" % "akka-actor_2.12" % "2.5.9"

Scala Projects

Best Practices

Prefer variables over Maven’s property resolution mechanism

For example, consider the following snippet, which uses a Maven property to define the Scala version

Model(
  properties = Map(
    "scala.version" -> "2.12.6"
  ),
  dependencies = Seq(
    "org.scala-lang" % "scala-library" % "${scala.version}",
    "org.scala-lang" % "scala-reflect" % "${scala.version}"
  )
  // ...
)

Instead, you can use a Scala immutable variable to hold the Scala version.

val scalaVersion = "2.12.6"

Model(
  dependencies = Seq(
    "org.scala-lang" % "scala-library" % scalaVersion,
    "org.scala-lang" % "scala-reflect" % scalaVersion
  )
  // ...
)

Benefits:

  • Variables are checked at compile-time (parse-time of pom), thus incomplete removals or renaming will fail early before Maven start building the project.

  • Maven will only see the actual value, thus no variables will be in the resulting pom which gets installed into the artifact repository.

Use shared include files for versions and other settings

One unique feature of the Polyglot Scala Extension is the include macro.

pom.scala: Include code from the shared file mvn-shared.scala
//#include mvn-shared.scala

By placing the project version number in a shared scala file and including it into each project, you have to only edit one central place when the version number needs to be modified.

Also, the include process is completely transparent to Maven. When your artifact gets installed and deployed, the version is no longer a variable, which makes later resolve processes more robust.

Tip
Avoid Maven parent poms completely, by using include files for dependency and plugin definitions.

Replace Maven parent poms with include files

Instead of defining and referencing parent poms, you should consider to define an object which produces the desired project model with all its settings and potential defaults. This not only makes the reasoning about your project easier, it also avoid various issues Maven has with parent poms. It also speeds up the build and produces less artifacts to download.

Known Issues

Converting/Translating existing pom.xml fails when there is no XML Header

In case your pom.xml does not have an proper XML header (aka XML Declaration), conversion with the polyglot-translate-plugin will fail. You can work around this issue by adding the missing XML header to the pom file. E.g. just add to following line as to the top of the pom.xml.

<?xml version="1.0" encoding="UTF-8"?>

Eclipse IDE does not directly support Polyglot-Scala

There is a proof-of-concept Eclipse Plug-in that integrates some polyglot support into M2E, but it isn’t integrated as smooth as pom.xml.