28 kwietnia 2008

Tworzenie projektów Scali z pomocą mavenowego scala-archetype-simple

Poprzednio w Zestawienie środowiska do programowania w Scali z Apache Maven 2 i Eclipse IDE opisałem moje wrażenia z tworzeniem projektu opartego o Scalę z pomocą archetypu maven-archetype-quickstart. Tym razem sprawdzę możliwości specjalizowanego archetypu scala-archetype-simple, który niweluje konieczność wykonywania kilku z kroków, a którego jedyną dokumentacją, jaką udało mi się znaleźć w Sieci to wpis na blogu Scala Blog - maven for scala.
 jlaskowski@work /cygdrive/c/projs/sandbox
$ mvn org.apache.maven.plugins:maven-archetype-plugin:1.0-alpha-7:create \
-DarchetypeGroupId=org.scala-tools.archetypes \
-DarchetypeArtifactId=scala-archetype-simple \
-DarchetypeVersion=1.2 \
-DremoteRepositories=http://scala-tools.org/repo-releases \
-DgroupId=pl.jaceklaskowski.scala -DartifactId=scala-witaj-swiecie -Dversion=1.0.0
...
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating Archetype: scala-archetype-simple:1.2
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: pl.jaceklaskowski.scala
[INFO] Parameter: packageName, Value: pl.jaceklaskowski.scala
[INFO] Parameter: basedir, Value: c:\projs\sandbox
[INFO] Parameter: package, Value: pl.jaceklaskowski.scala
[INFO] Parameter: version, Value: 1.0.0
[INFO] Parameter: artifactId, Value: scala-witaj-swiecie
[INFO] ********************* End of debug info from resources from generated POM ***********************
[INFO] Archetype created in dir: c:\projs\sandbox\scala-witaj-swiecie
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
Poprawne uruchomienie wtyczki scala-archetype-simple jest możliwe jedynie z wersją maven-archetype-plugin 1.0-alpha-7 - najnowsza wersja nie odnajduje poprawnie archetypu scala-archetype-simple, który nie jest dostępny w repozytorium głównym m2 i kończy się błędem:
 jlaskowski@work /cygdrive/c/projs/sandbox
$ mvn archetype:create \
-DarchetypeGroupId=org.scala-tools.archetypes \
-DarchetypeArtifactId=scala-archetype-simple \
-DarchetypeVersion=1.2 \
-DremoteRepositories=http://scala-tools.org/repo-releases \
-DgroupId=pl.jaceklaskowski.scala -DartifactId=scala-witaj-swiecie -Dversion=1.0.0
...
[INFO] [archetype:create]
[INFO] Defaulting package to group ID: pl.jaceklaskowski.scala
[INFO] We are using command line specified remote repositories: http://scala-tools.org/repo-releases
Downloading: http://repo1.maven.org/maven2/org/scala-tools/archetypes/scala-archetype-simple/1.2/scala-archetype-simple-1.2.jar
[INFO] ------------------------------------------------------------------------
[ERROR] BUILD ERROR
[INFO] ------------------------------------------------------------------------
[INFO] Failed to resolve artifact.

GroupId: org.scala-tools.archetypes
ArtifactId: scala-archetype-simple
Version: 1.2

Reason: Unable to locate resource in repository

Try downloading the file manually from the project website.

Then, install it using the command:
mvn install:install-file -DgroupId=org.scala-tools.archetypes -DartifactId=scala-archetype-simple
-Dversion=1.2 -Dpackaging=jar -Dfile=/path/to/file

Alternatively, if you host your own repository you can deploy the file there:
mvn deploy:deploy-file -DgroupId=org.scala-tools.archetypes -DartifactId=scala-archetype-simple
-Dversion=1.2 -Dpackaging=jar -Dfile=/path/to/file -Durl=[url] -DrepositoryId=[id]

org.scala-tools.archetypes:scala-archetype-simple:jar:1.2

from the specified remote repositories:
id0 (http://scala-tools.org/repo-releases)

Przykładowa aplikacja w Scali, która jest tworzona przy zakładaniu projektu z archetypem scala-archetype-simple prezentuje się następująco:
 jlaskowski@work /cygdrive/c/projs/sandbox/scala-witaj-swiecie
$ cat src/main/scala/pl/jaceklaskowski/scala/App.scala
package pl.jaceklaskowski.scala

/**
* Hello world!
*
*/
object App extends Application {
println( "Hello World!" )
}
Różnica między powyższą wersją a poprzednią wersją:
 package pl.jaceklaskowski.scala

object WitajSwiecie {
def main(args: Array[String]) {
println("Witaj Świecie!")
}
}
jest taka, że w Scala użycie extends Application powoduje, że wszystkie instrukcje w klasie są traktowane jakby były umieszczone w metodzie def main(args: Array[String]). Szybko skorzystałem z tego uproszczenia w dzisiejszej, nowej wersji klasy pl.jaceklaskowski.scala.WitajSwiecie:
 object WitajSwiecie extends Application {
println("Witaj Świecie!")
}
Sprawdzę jej gotowość poleceniem mvn scala:run.
 jlaskowski@work /cygdrive/c/projs/sandbox/scala-witaj-swiecie
$ mvn scala:run
[INFO] Scanning for projects...
...
[INFO] ------------------------------------------------------------------------
[ERROR] BUILD ERROR
[INFO] ------------------------------------------------------------------------
[INFO] One or more required plugin parameters are invalid/missing for 'scala:run'

[0] Inside the definition for plugin 'maven-scala-plugin' specify the following:

<configuration>
...
<mainClass>VALUE</mainClass>
</configuration>

-OR-

on the command line, specify: '-DmainClass=VALUE'
Niestety. Mam dwa rozwiązania tego problemu - określenie mainClass w konfiguracji wtyczki maven-scala-plugin w sekcji configuration jak opisałem w poprzednim wpisie Zestawienie środowiska do programowania w Scali z Maven 2 i Eclipse IDE, albo skorzystanie z możliwości zdefiniowania jej z linii poleceń. Dla czysto demonstracyjnych pobudek skorzystam z drugiej opcji (normalnie skorzystałbym z możliwości określenia klasy głównej w pom.xml).
 jlaskowski@work /cygdrive/c/projs/sandbox/scala-witaj-swiecie
$ mvn scala:run -DmainClass=pl.jaceklaskowski.scala.App
...
[INFO] [scala:run]
[INFO] Hello World!
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
I to już działa!

Uwaga na pom.xml, który deklaruje zmienną scala.version o wartości 2.7.0, a mamy już wersję 2.7.1-rc2. Dla zmniejszenia ilości miejsca zajmowanego przez lokalne repozytorium m2 zmieniam wersję Scala w pom.xml do 2.7.1-rc2 (należałoby również skasować zawartość już pobranych katalogów związanych z wersją 2.7.0, ale to pozostawiam jako zadanie domowe dla czytelników - może ktoś zechciałby napisać to w Scali?).

Pytanie konkursowe: Jaka konstrukcja w Scali pozwala na stworzenie klasy głównej bez jawnego deklarowania metody def main(args: Array[String])? (Byłoby wspaniale, gdybym mógł nagrodzić uczestników, ale na razie sponsorów brak, więc i nagród - zdjąłem reklamy Google AdSense, aby ich trochę zmotywować ;-)).