このところ、Spring Bootなどで遊んでいると、Scalaを使っているにも関わらずMavenを使うような事態になります。
※極めて、個人の趣向です
で、自分はScalaコードを書く時はEmacs+Ensime+sbtなのですが、Mavenにしてしまうとこの組み合わせが使えません。
これはどうしたものかと調べたところ、下記のようなものが見つかりました。
Maven support dropped?
https://groups.google.com/forum/#!topic/ensime/uxzWOj_T964
ここに載っているPythonのコードを動かせばいいんですね!
と思ったら、出力される設定ファイルの形が全然違っていて、今では動かないようです…。これは困りました(普通、困らない)。
ところで、よくよく見ると、このPythonコードはmaven-dependency-pluginの結果を使っているだけでみたいです。だったら、同じようなことをすればいいんじゃない?ということで、書いてみました。
Perlで。
書いたスクリプトは、こちら。
#!/usr/bin/perl use strict; use warnings; my $sbt_build_file = '___gen_ensime.sbt'; my $scala_version = '2.11.6'; if (@ARGV ge 1) { $scala_version = $ARGV[0]; } my @maven_dependency_tree = split /\r?\n/, `mvn dependency:tree`; my $line_index = 0; for (my $i = 0; $i < @maven_dependency_tree; $i++) { if ($maven_dependency_tree[$i] =~ /^\[INFO\] --- maven-dependency-plugin:([^ ]+):tree .+/) { $line_index = $i + 1; last; } } my $project_group; my $project_name; my $project_version; if ($maven_dependency_tree[$line_index] =~ /^\[INFO\] ([^:]+):([^:]+):[^:]+:([^:]+)$/) { ($project_group, $project_name, $project_version) = ($1, $2, $3); } $line_index++; my @dependencies = (); for (my $i = $line_index; $i < @maven_dependency_tree; $i++) { if ($maven_dependency_tree[$i] =~ /^\[INFO\] -+/) { last; } if ($maven_dependency_tree[$i] =~ /^\[INFO\] [+\\]- ([^:]+):([^:]+):[^:]+:([^:]+):([^:]+)/) { my ($group, $name, $version, $scope) = ($1, $2, $3, $4); push(@dependencies, "\"$group\" % \"$name\" % \"$version\" % \"$scope\""); } } open my $fh, '>', $sbt_build_file or die "Can't open file:$!"; my $library_dependencies_flat = join(',', @dependencies); print $fh <<BUILD_FILE; name := "$project_name" organization := "$project_group" scalaVersion := "$scala_version" updateOptions := updateOptions.value.withCachedResolution(true) scalacOptions ++= Seq("-Xlint", "-unchecked", "-deprecation", "-feature") libraryDependencies ++= Seq( $library_dependencies_flat ) BUILD_FILE close $fh; system('sbt gen-ensime'); unlink $sbt_build_file;
これを、gen_ensime.plという名前にでもして保存します。
やっていることは単純で、
- 「mvn dependency:tree」の結果から、簡単なsbtの設定ファイルを作る(名前は「___gen_ensime.sbt」)
- 作ったsbtの設定ファイルを使って、「sbt gen-ensime」する
- 作ったsbtの設定ファイルを削除する
という感じで、自分でEnsimeの設定ファイルを書くのではなくEnsime自身に作ってもらうことにしました。
ちなみに、Scalaのバージョンだけは引数で指定できる謎仕様。
よって、このPerlスクリプトを動かすための前提は以下になります。
- Mavenがインストールされていること(mvnコマンドが実行できること)
- sbtがインストールされていること(sbtコマンドが実行できること)
- Ensimeのsbtプラグインが、グローバルに適用されていること(いきなり「sbt gen-ensime」ができること)
- Mavenプロジェクトで構成されていること
- sbtのプロジェクトではないこと
では、試してみましょう。
こんなpom.xmlを用意します。
<?xml version="1.0" encoding="UTF-8"?> <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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.littlewings</groupId> <artifactId>gen-ensime</artifactId> <packaging>jar</packaging> <version>0.0.1-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-search-orm</artifactId> <version>5.1.0.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>4.3.8.Final</version> </dependency> <dependency> <groupId>net.databinder.dispatch</groupId> <artifactId>dispatch-jsoup_2.11</artifactId> <version>0.11.2</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.scala-lang</groupId> <artifactId>scala-library</artifactId> <version>2.11.6</version> </dependency> <dependency> <groupId>org.scalatest</groupId> <artifactId>scalatest_2.11</artifactId> <version>2.2.4</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>1.2.2.RELEASE</version> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>net.alchim31.maven</groupId> <artifactId>scala-maven-plugin</artifactId> <version>3.2.0</version> <executions> <execution> <goals> <goal>compile</goal> <goal>testCompile</goal> </goals> </execution> </executions> <configuration> <scalaVersion>2.11.6</scalaVersion> <args> <arg>-Xlint</arg> <arg>-unchecked</arg> <arg>-deprecation</arg> <arg>-feature</arg> </args> <recompileMode>incremental</recompileMode> </configuration> </plugin> </plugins> </build> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> </project>
依存関係に、特に意味はありません。
ここで、先ほどのスクリプトを実行。
$ perl gen_ensime.pl
すると、このような.ensimeファイルが生成されます。
( :root-dir "/xxxxx/gen-ensime" :cache-dir "/xxxxx/gen-ensime/.ensime_cache" :name "gen-ensime" :java-home "/usr/lib/jvm/java-8-oracle" :java-flags ("-Xms512M" "-Xmx1536M" "-Xss1M" "-XX:+CMSClassUnloadingEnabled" "-XX:MaxMetaspaceSize=384M") :reference-source-roots ("/usr/lib/jvm/java-8-oracle/src.zip") :scala-version "2.11.6" :compiler-args ("-Xlint" "-unchecked" "-deprecation" "-feature") :subprojects (( :name "gen-ensime" :module-name "gen-ensime" :source-roots ("/xxxxx/gen-ensime/src/main/scala" "/xxxxx/gen-ensime/src/main/java" "/xxxxx/gen-ensime/src/test/scala" "/xxxxx/gen-ensime/src/test/java") :target "/xxxxx/gen-ensime/target/scala-2.11/classes" :test-targets ("/xxxxx/gen-ensime/target/scala-2.11/test-classes") :depends-on-modules nil :compile-deps ("/xxxxx/.ivy2/cache/org.hibernate.common/hibernate-commons-annotations/jars/hibernate-commons-annotations-4.0.5.Final.jar" "/xxxxx/.ivy2/cache/org.apache.lucene/lucene-analyzers-common/jars/lucene-analyzers-common-4.10.4.jar" "/xxxxx/.ivy2/cache/org.hibernate/hibernate-core/jars/hibernate-core-4.3.8.Final.jar" "/xxxxx/.ivy2/cache/xml-apis/xml-apis/jars/xml-apis-1.0.b2.jar" "/xxxxx/.ivy2/cache/org.hibernate/hibernate-search-engine/jars/hibernate-search-engine-5.1.0.Final.jar" "/xxxxx/.ivy2/cache/org.javassist/javassist/bundles/javassist-3.18.1-GA.jar" "/xxxxx/.ivy2/cache/org.jboss/jandex/jars/jandex-1.1.0.Final.jar" "/xxxxx/.ivy2/cache/org.apache.lucene/lucene-facet/jars/lucene-facet-4.10.4.jar" "/xxxxx/.ivy2/cache/org.hibernate.javax.persistence/hibernate-jpa-2.1-api/jars/hibernate-jpa-2.1-api-1.0.0.Final.jar" "/xxxxx/.ivy2/cache/org.apache.lucene/lucene-queries/jars/lucene-queries-4.10.4.jar" "/xxxxx/.ivy2/cache/org.jboss.spec.javax.transaction/jboss-transaction-api_1.2_spec/jars/jboss-transaction-api_1.2_spec-1.0.0.Final.jar" "/xxxxx/.ivy2/cache/dom4j/dom4j/jars/dom4j-1.6.1.jar" "/xxxxx/.ivy2/cache/org.hibernate/hibernate-entitymanager/jars/hibernate-entitymanager-4.3.8.Final.jar" "/xxxxx/.ivy2/cache/org.scala-lang/scala-library/jars/scala-library-2.11.6.jar" "/xxxxx/.ivy2/cache/antlr/antlr/jars/antlr-2.7.7.jar" "/xxxxx/.ivy2/cache/org.jboss.logging/jboss-logging/jars/jboss-logging-3.1.4.GA.jar" "/xxxxx/.ivy2/cache/org.apache.lucene/lucene-core/jars/lucene-core-4.10.4.jar" "/xxxxx/.ivy2/cache/org.hibernate/hibernate-search-orm/jars/hibernate-search-orm-5.1.0.Final.jar" "/xxxxx/.ivy2/cache/org.jboss.logging/jboss-logging-annotations/jars/jboss-logging-annotations-1.2.0.Beta1.jar") :runtime-deps nil :test-deps ("/xxxxx/.ivy2/cache/org.scala-lang.modules/scala-xml_2.11/bundles/scala-xml_2.11-1.0.2.jar" "/xxxxx/.ivy2/cache/junit/junit/jars/junit-4.12.jar" "/xxxxx/.ivy2/cache/org.hamcrest/hamcrest-core/jars/hamcrest-core-1.3.jar" "/xxxxx/.ivy2/cache/org.scalatest/scalatest_2.11/bundles/scalatest_2.11-2.2.4.jar" "/xxxxx/.ivy2/cache/org.scala-lang/scala-reflect/jars/scala-reflect-2.11.2.jar") :doc-jars ("/xxxxx/gen-ensime/target/scala-2.11/gen-ensime_2.11-0.1-SNAPSHOT-javadoc.jar") :reference-source-roots nil)) )
よーく見ると、「provided」に指定したDispatchの依存関係がありません…。別にsbtの設定ファイルを作って確認してみましたが、providedは対象にされないみたい…?まあ、いいか…。
なお、スクリプト実行後に削除されますが、一時的に生成されるsbtの設定ファイルはこのような形になります。
name := "gen-ensime" organization := "org.littlewings" scalaVersion := "2.11.6" updateOptions := updateOptions.value.withCachedResolution(true) scalacOptions ++= Seq("-Xlint", "-unchecked", "-deprecation", "-feature") libraryDependencies ++= Seq( "org.hibernate" % "hibernate-search-orm" % "5.1.0.Final" % "compile","org.hibernate" % "hibernate-entitymanager" % "4.3.8.Final" % "compile","net.databinder.dispatch" % "dispatch-jsoup_2.11" % "0.11.2" % "provided","org.scala-lang" % "scala-library" % "2.11.6" % "compile","org.scalatest" % "scalatest_2.11" % "2.2.4" % "test","junit" % "junit" % "4.12" % "test" )
nameやorganizationは、pom.xmlと同じになります。
これで、MavenでもEnsimeが使えます!
Gradleに移行したら、どうしよ…。