mlte.de

Scala mit Maven kompilieren

Um Scala- und Java-Code gleichzeitig mit Maven zu kompilieren, verwende ich das scala-maven-plugin, in dessen Dokumentation sich bereits für die meisten Fäll sehr gute Beispiele finden. Die Ordnerstruktur muss dabei wie folgt aussehen:

demo
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   └── scala
    └── test
        ├── java
        └── scala

Ganz am Anfang der pom.xml muss direkt in den properties die Eigenschaft project.build.sourceEncoding auf UTF-8 gesetzt werden, um seltsame Warnungen bzgl. des Encodings zu vermeiden.

In der Konfiguration des scala-maven-plugins kann als Argument -feature angegeben werden, damit Scala detailliere Warnungen ausgibt und nicht nur etwas wenig hilfreich mitteilt, dass Warnungen vorhanden sind. Ebenfalls in dieser Konfiguration kann als recompileMode der Wert incremental angegeben werden, was bei mir allerdings nie so ganz den gewünschten Effekt hat, da zwar durchaus Scala-Klassen inkrementell kompiliert werden, nach meinem subjektiven Eindruck am Ende aber doch immer alle Scala-Klassen kompiliert werden.

Im maven-compiler-plugin muss die Java-Version auf 1.7 (oder 1.6) gesetzt werden, damit Maven gegen die korrekte Java-Version kompiliert.

Als Abhängigkeit muss auf jeden Fall die scala-library angegeben werden. An der Version der scala-library erkennt das scala-maven-plugin auch direkt, gegen welche Scala-Version der Code kompiliert werden soll. In den meisten Fällen ist es darüber hinaus sinnvoll junit als Abhängigkeit für den scope test anzugeben.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <!-- options of the project -->
  <groupId>de.mlte.maven</groupId>
  <artifactId>simple-demo</artifactId>
  <version>0.0.1</version>
  <packaging>jar</packaging>
  <name>simple-demo</name>
  <!-- set encoding of this project to UTF-8 to avoid warnings -->
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <!-- set up build process of this project -->
  <build>
    <plugins>
      <plugin>
        <groupId>net.alchim31.maven</groupId>
        <artifactId>scala-maven-plugin</artifactId>
        <version>3.1.6</version>
        <!--
          The scala plugin detects the version of the scala library in the dependents and
          uses that version to compile the scala code
        -->
        <executions>
          <execution>
            <id>scala-compile-first</id>
            <phase>process-resources</phase>
            <goals>
              <goal>add-source</goal>
              <goal>compile</goal>
            </goals>
          </execution>
          <execution>
            <id>scala-test-compile</id>
            <phase>process-test-resources</phase>
            <goals>
              <goal>testCompile</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <!-- enable detailed feature warnings of the scala compiler by setting its command line parameter -feature -->
          <args>
            <arg>-feature</arg>
          </args>
          <!-- enable incremental compilation mode for faster compiling -->
          <recompileMode>incremental</recompileMode>
          <!-- use zink server as scalac provider -->
          <!-- <useZincServer>true</useZincServer> -->
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <!-- set the version of the java compile (source) and the target JVM -->
        <configuration>
          <source>1.7</source>
          <target>1.7</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
  <!-- set up dependencies of this project -->
  <dependencies>
    <!-- scala-library is needed to run scala code in JVM -->
    <dependency>
      <groupId>org.scala-lang</groupId>
      <artifactId>scala-library</artifactId>
      <version>2.10.3</version>
    </dependency>
    <!-- junit is needed, because our test cases use JUnit -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

Darüber hinaus kann das maven-dependency-plugin verwendet werden, damit beim Kommando mvn package alle Abhängigkeiten als jar-Dateien in den Ordner target/lib kopiert werden. Bei Verwendung der Maven-Eclipse-Integration M2Eführt dieses Plugin allerdings zu Problemen, da es von M2E nicht korrekt erkannt wird. Um die entsprechenden Warnungen zu unterdrücken, kann im pluginManagement das Plugin lifecycle-mapping hinzugefügt werden, in dessen pluginExecutionFilter das Ziel copy-dependencies des maven-dependency-plugin vom Import in das Eclipse-Projekt ausgeschlossen werden kann.

Ebenfalls sehr nützlich ist das maven-assembly-plugin, mit dem eine jar-Datei erzeugt werden kann, in der alle verwendeten Abhängigkeiten eingebunden sind. Auf diese Weise muss nur eine Datei weitergegeben werden, die dann mit java -jar gestartet werden kann, auch wenn es sich um ein Scala-Projekt handelt. Der Nachteil liegt natürlich in der Dateigröße dieser jar-Datei, die dann die scala-library und alle weiteren Abhängigkeiten enthält. Damit das alles so funktioniert, muss in der configuration dieses Plugins die mainClass für das manifest des archive also der jar-Datei angegeben werden. Statt mvn package wird die jar-with-dependencies dann über mvn assembly:single erzeugt.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <!-- options of the project -->
  <groupId>de.mlte.maven</groupId>
  <artifactId>demo</artifactId>
  <version>0.0.1</version>
  <packaging>jar</packaging>
  <name>demo</name>
  <!-- set encoding of this project to UTF-8 to avoid warnings -->
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <!-- set up build process of this project -->
  <build>
    <pluginManagement>
      <plugins>
        <!-- ignore/execute plugin execution in m2e -->
        <plugin>
          <groupId>org.eclipse.m2e</groupId>
          <artifactId>lifecycle-mapping</artifactId>
          <version>1.0.0</version>
          <configuration>
            <lifecycleMappingMetadata>
              <pluginExecutions>
                <!-- ignore maven-dependency-plugin's copy-dependency goal -->
                <pluginExecution>
                  <pluginExecutionFilter>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-dependency-plugin</artifactId>
                    <versionRange>[1.0.0,)</versionRange>
                    <goals>
                      <goal>copy-dependencies</goal>
                    </goals>
                  </pluginExecutionFilter>
                  <action>
                    <ignore />
                  </action>
                </pluginExecution>
              </pluginExecutions>
            </lifecycleMappingMetadata>
          </configuration>
        </plugin>
      </plugins>
    </pluginManagement>
    <plugins>
      <plugin>
        <artifactId>maven-dependency-plugin</artifactId>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>copy-dependencies</goal>
            </goals>
            <configuration>
              <outputDirectory>${project.build.directory}/lib</outputDirectory>
            </configuration>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>net.alchim31.maven</groupId>
        <artifactId>scala-maven-plugin</artifactId>
        <version>3.1.6</version>
        <!--
          The scala plugin detects the version of the scala library in the dependents and
          uses that version to compile the scala code
        -->
        <executions>
          <execution>
            <id>scala-compile-first</id>
            <phase>process-resources</phase>
            <goals>
              <goal>add-source</goal>
              <goal>compile</goal>
            </goals>
          </execution>
          <execution>
            <id>scala-test-compile</id>
            <phase>process-test-resources</phase>
            <goals>
              <goal>testCompile</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <!-- enable detailed feature warnings of the scala compiler by setting its command line parameter -feature -->
          <args>
            <arg>-feature</arg>
          </args>
          <!-- enable incremental compilation mode for faster compiling -->
          <recompileMode>incremental</recompileMode>
          <!-- use zink server as scalac provider -->
          <!-- <useZincServer>true</useZincServer> -->
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <!-- set the version of the java compile (source) and the target JVM -->
        <configuration>
          <source>1.7</source>
          <target>1.7</target>
        </configuration>
      </plugin>
      <!-- mvn assembly:single generates a single .jar file containing all dependencies.-->
      <plugin>
        <artifactId>maven-assembly-plugin</artifactId>
        <configuration>
          <archive>
            <manifest>
              <mainClass>de.mlte.maven.demo.Main</mainClass>
            </manifest>
          </archive>
          <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
          </descriptorRefs>
        </configuration>
      </plugin>
    </plugins>
  </build>
  <!-- set up dependencies of this project -->
  <dependencies>
    <!-- scala-library is needed to run scala code in JVM -->
    <dependency>
      <groupId>org.scala-lang</groupId>
      <artifactId>scala-library</artifactId>
      <version>2.10.3</version>
    </dependency>
    <!-- junit is needed, because our test cases use JUnit -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>