Introductions are all well and good but as a Java developer I want some first-class tools and I want to run some code! Until recently first-class tools were seriously lacking. Luckily in recent months even the languages creator has acknowledged the tooling was lacking and has taken aggressive steps to correct the issue.
At the time of writing I use Eclipse 3.6.2 Helios (Java Developer edition) with Scala-IDE 2.0.0.somethingOrOther-beta. For the sake of this example I will assume we wish to build the project using Maven. First step, download the tools:
- Eclipse Helios
- At the time of writing I favor the Java developer edition as it is somewhat less bloated than the EE version; http://www.eclipse.org/downloads/packages/eclipse-ide-java-developers/heliossr2
- Scala IDE 2
- The 2.0 release is a major modification and the first solid Eclipse IDE plugin for Scala; the 1.x version was prone to all sorts of "exciting" behaviors
- Add to Eclipse using the update site from link on the front page of http://www.scala-ide.org/
- m2eclipse
- Add to Eclipse using the update site from http://m2eclipse.sonatype.org/installing-m2eclipse.html
- Maven 3
- Download from http://maven.apache.org/download.html
- Make sure if you run mvn -v in command prompt it prints the expected version
Now we have the tools, lets setup a project! First up, create a directory for your project and in that directory create a file called pom.xml similar to:
<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> <groupId>com.blogspot.whileonefork</groupId> <artifactId>mvn-scala-trial1</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <name>Sample Project for Blog</name> <url>http://com.blogspot.whileonefork</url> </project>
Next, create a directory named src containing a directory named main containing a directory named scala. Your project should now contain the following:
mvn-scala-project |--pom.xml `--src `--main `--scala
In the src/main/scala directory, create a new file HelloWorldJustLikeJava.scala with the simplest hello world implementation possible:
This, while arguably not the absolute simplest Scala hello world, is the definition closest to the one you would write in Java. Now to get it compiling in Maven. If you open a command prompt in the mvn-scala-project directory and run mvn clean install it will succeed but you will get a warning that no content was marked for inclusion; this occurs because by default Maven doesn't include the plugin necessary to build Scala code.
To compile Scala files we must update our pom.xml; we'll tell Maven what plugin to use to build Scala files and we'll advise it of the existence of a URL where it can find the plugin in question. Our updated pom.xml should look like this:
If you run mvn scala:compile in the mvn-scala-project directory the project should build successfully and in /target/classes you should find two .class files: HelloWorldJustLikeJava$.class and HelloWorldJustLikeJava.class. Scala (usually) compiles the .class files to run on the JVM, just like Java.
All the usual Java tools can be used on these .class files; for example if we run javap on HelloWorldJustLikeJava$ we see the Scala source was compiled to Java; in this case fairly predictable Java:
The practice of outputting additional classes beyond what you directly coded is VERY common in Scala. A class using closures and other features of Scala will often output an exciting wack of $whatever classes.
object HelloWorldJustLikeJava { def main(args:Array[String]) = { println("Hello, World"); } }
This, while arguably not the absolute simplest Scala hello world, is the definition closest to the one you would write in Java. Now to get it compiling in Maven. If you open a command prompt in the mvn-scala-project directory and run mvn clean install it will succeed but you will get a warning that no content was marked for inclusion; this occurs because by default Maven doesn't include the plugin necessary to build Scala code.
To compile Scala files we must update our pom.xml; we'll tell Maven what plugin to use to build Scala files and we'll advise it of the existence of a URL where it can find the plugin in question. Our updated pom.xml should look like this:
<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> <groupId>com.blogspot.whileonefork</groupId> <artifactId>mvn-scala-trial1</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <name>Sample Project for Blog</name> <url>http://com.blogspot.whileonefork</url> <!-- Notify Maven it can download things from here --> <repositories> <repository> <id>scala-tools.org</id> <name>Scala-tools Maven2 Repository</name> <url>http://scala-tools.org/repo-releases</url> </repository> </repositories> <dependencies> <!-- Scala version is very important. Luckily the plugin warns you if you don't specify: [WARNING] you don't define org.scala-lang:scala-library as a dependency of the project --> <dependency> <groupId>org.scala-lang</groupId> <artifactId>scala-library</artifactId> <version>2.9.0-1</version> </dependency> </dependencies> <build> <!-- add the maven-scala-plugin to the toolchain --> <plugins> <plugin> <groupId>org.scala-tools</groupId> <artifactId>maven-scala-plugin</artifactId> <version>2.14.2</version> </plugin> </plugins> </build> </project>
If you run mvn scala:compile in the mvn-scala-project directory the project should build successfully and in /target/classes you should find two .class files: HelloWorldJustLikeJava$.class and HelloWorldJustLikeJava.class. Scala (usually) compiles the .class files to run on the JVM, just like Java.
All the usual Java tools can be used on these .class files; for example if we run javap on HelloWorldJustLikeJava$ we see the Scala source was compiled to Java; in this case fairly predictable Java:
public final class HelloWorldJustLikeJava$ extends java.lang.Object implements scala.ScalaObject{ public static final HelloWorldJustLikeJava$ MODULE$; public static {}; public void main(java.lang.String[]); }Digging a little deeper, using javap -verbose, we can see that our HelloWorldJustLikeJava class invokes HelloWorldJustLikeJava$:
public static final void main(java.lang.String[]); Code: Stack=2, Locals=1, Args_size=1 0: getstatic #11; //Field HelloWorldJustLikeJava$.MODULE$:LHelloWorldJustLikeJava$; 3: aload_0 4: invokevirtual #13; //Method HelloWorldJustLikeJava$.main:([Ljava/lang/String;)V 7: returnHelloWorldJustLikeJava$ provides a simple implementation of main and some other odds and ends:
public static final HelloWorldJustLikeJava$ MODULE$; public static {}; Code: Stack=1, Locals=0, Args_size=0 0: new #9; //class HelloWorldJustLikeJava$ 3: invokespecial #12; //Method "":()V 6: return public void main(java.lang.String[]); Code: Stack=2, Locals=2, Args_size=2 0: getstatic #19; //Field scala/Predef$.MODULE$:Lscala/Predef$; 3: ldc #22; //String Hello, World 5: invokevirtual #26; //Method scala/Predef$.println:(Ljava/lang/Object;)V 8: return LineNumberTable: line 3: 0 LocalVariableTable: Start Length Slot Name Signature 0 9 0 this LHelloWorldJustLikeJava$; 0 9 1 args [Ljava/lang/String;
The practice of outputting additional classes beyond what you directly coded is VERY common in Scala. A class using closures and other features of Scala will often output an exciting wack of $whatever classes.
Anyway, at this point the objective of making our project compile with Maven is achieved; we can now move on to getting it setup to edit in Eclipse. In the mvn-scala-project directory (the same one as pom.xml), create a .classpath file and a .project file:
.classpath
<?xml version="1.0" encoding="UTF-8"?> <classpath> <classpathentry kind="src" path="src/main/scala"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/> <classpathentry kind="con" path="org.scala-ide.sdt.launching.SCALA_CONTAINER"/> <classpathentry kind="output" path="target/classes"/> </classpath>
.project
<?xml version="1.0" encoding="UTF-8"?> <projectDescription> <name>mvn-scala-project</name> <comment></comment> <projects> </projects> <buildSpec> <buildCommand> <name>org.scala-ide.sdt.core.scalabuilder</name> <arguments> </arguments> </buildCommand> <buildCommand> <name>org.maven.ide.eclipse.maven2Builder</name> <arguments> </arguments> </buildCommand> </buildSpec> <natures> <nature>org.maven.ide.eclipse.maven2Nature</nature> <nature>org.scala-ide.sdt.core.scalanature</nature> <nature>org.eclipse.jdt.core.javanature</nature> </natures> </projectDescription>
At this point your project should contain the following:
mvn-scala-project |--.classpath |--.project |--pom.xml `--src `--main `--scala |--HelloWorldJustLikeJava.scala `target `--classes |--HelloWorldJustLikeJava$.class |--HelloWorldJustLikeJava.classIf you happened to run mvn clean the target directory may be absent.
In Package Explorer in Eclipse your project should look similar to:
And now we can edit our Scala in Eclipse and compile with Maven, and can therefore easily load our project into just about any continuous integration server. Hooray!
EDIT: Assuming you are using m2eclipse, you will want to make sure your Eclipse IDE boots using a JDK VM; this is accomplished by editing eclipse.ini and adding a -vm argument. Bumping memory up a bit usually helps too; the file I am currently using on a Windows dev box follows:
-startup plugins/org.eclipse.equinox.launcher_1.1.1.R36x_v20101122_1400.jar --launcher.library plugins/org.eclipse.equinox.launcher.win32.win32.x86_1.1.2.R36x_v20101222 -showsplash org.eclipse.platform --launcher.XXMaxPermSize 256m -vm C:/Program Files/Java/jdk1.6.0_07/bin/javaw.exe --launcher.defaultAction openFile -product org.eclipse.epp.package.java.product --launcher.defaultAction openFile --launcher.XXMaxPermSize 256M -vmargs -Dosgi.requiredJavaVersion=1.5 -Xms40m -Xmx768mEDIT2: If you want the project to build a bit more gracefully in Eclipse consider binding Scala compile to run by default (eg when you run mvn clean install instead of the somewhat novel mvn scala:compile). Modify the plugin element to add the execution:
<build> <!-- add the maven-scala-plugin to the toolchain --> <plugins> <plugin> <groupId>org.scala-tools</groupId> <artifactId>maven-scala-plugin</artifactId> <version>2.15.2</version> <executions> <execution> <goals><goal>compile</goal></goals> </execution> </executions> </plugin> </plugins> </build>