建议需要改进包装sbt项目的所有来源和javadoc

时间:2014-08-11 17:35:49

标签: scala sbt

为了避免与scala(2.9,2.10,2.11,...)的版本相关的问题,我们希望包含所有必需的jar文件以在Java应用程序中使用scala。为方便调试和发展,我们希望包括来源和所有这些图书馆的javadoc也是。

我知道这个话题以前曾被多次询问过;但是,我还没有找到适用于我们的解决方案(scala 2.11& sbt 0.13.5)。

我设法使用如下配置的sbt项目对近似解决方案进行原型设计:

./ build.sbt:

val packAllCommand = Command.command("packAll") {
  state => 
    "clean" :: "update" :: "updateClassifiers" :: 
    "pack" :: "dependencyGraph" ::  "dependencyDot" :: 
    state
}

commands += packAllCommand

./项目/ plugins.sbt:

resolvers += 
  "sonatype-releases" at "https://oss.sonatype.org/content/repositories/releases/"

addSbtPlugin("org.xerial.sbt" % "sbt-pack" % "0.6.1")

addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.7.4")

./项目/ Build.scala

import sbt._
import Keys._
import net.virtualvoid.sbt.graph.Plugin.graphSettings
import xerial.sbt.Pack._

/**
 * Goal: 
 * 
 * use sbt to package all the jars/sources/javadoc for scala & related libraries needed to use scala in a java application 
 * without requiring scala to be installed on the system.
 * 
 * @author Nicolas.F.Rouquette@jpl.nasa.gov
 */
object BuildWithSourcesAndJavadocs extends Build {

  object Versions {
    val scala = "2.11.2"
    val config = "1.2.1"
    val scalaCheck = "1.11.5"
    val scalaTest = "2.2.1"
    val specs2 = "2.4"
    val parboiled = "2.0.0"
  }

  lazy val scalaLibs: Project = Project(
    "scalaLibs",
    file( "scalaLibs" ),
    settings = Defaults.coreDefaultSettings ++ Defaults.runnerSettings ++ Defaults.baseTasks ++ graphSettings ++ packSettings ++ Seq(
      scalaVersion := Versions.scala,
      packExpandedClasspath := true,
      libraryDependencies ++= Seq(
        "org.scala-lang" % "scala-library" % scalaVersion.value % "compile" withSources () withJavadoc (),
        "org.scala-lang" % "scala-compiler" % scalaVersion.value % "compile" withSources () withJavadoc (),
        "org.scala-lang" % "scala-reflect" % scalaVersion.value % "compile" withJavadoc () withJavadoc () ),
      ( mappings in pack ) := { extraPackFun.value } ) )

  lazy val otherLibs: Project = Project(
    "otherLibs",
    file( "otherLibs" ),
    settings = Defaults.coreDefaultSettings ++ Defaults.runnerSettings ++ Defaults.baseTasks ++ graphSettings ++ packSettings ++ Seq(
      scalaVersion := Versions.scala,
      packExpandedClasspath := true,
      libraryDependencies ++= Seq(

        "org.scala-lang" % "scala-library" % Versions.scala % "provided",
        "org.scala-lang" % "scala-compiler" % Versions.scala % "provided",
        "org.scala-lang" % "scala-reflect" % Versions.scala % "provided",

        "com.typesafe" % "config" % Versions.config % "compile" withSources () withJavadoc (),
        "org.scalacheck" %% "scalacheck" % Versions.scalaCheck % "compile" withSources () withJavadoc (),
        "org.scalatest" %% "scalatest" % Versions.scalaTest % "compile" withSources () withJavadoc (),
        "org.specs2" %% "specs2" % Versions.specs2 % "compile" withSources () withJavadoc (),
        "org.parboiled" %% "parboiled" % Versions.parboiled % "compile" withSources () withJavadoc () ),
      ( mappings in pack ) := { extraPackFun.value } ) ).dependsOn( scalaLibs )

  lazy val root: Project = Project( "root", file( "." ) ) aggregate ( scalaLibs, otherLibs )

  val extraPackFun: Def.Initialize[Task[Seq[( File, String )]]] = Def.task[Seq[( File, String )]] {
    def getFileIfExists( f: File, where: String ): Option[( File, String )] = if ( f.exists() ) Some( ( f, s"${where}/${f.getName()}" ) ) else None

    val ivyHome: File = Classpaths.bootIvyHome( appConfiguration.value ) getOrElse sys.error( "Launcher did not provide the Ivy home directory." )

    // this is a workaround; how should it be done properly in sbt?

    // goal: process the list of library dependencies of the project.
    // that is, we should be able to tell the classification of each library dependency module as shown in sbt:
    //
    // > show libraryDependencies
    // [info] List(
    //    org.scala-lang:scala-library:2.11.2, 
    //    org.scala-lang:scala-library:2.11.2:provided, 
    //    org.scala-lang:scala-compiler:2.11.2:provided, 
    //    org.scala-lang:scala-reflect:2.11.2:provided, 
    //    com.typesafe:config:1.2.1:compile, 
    //    org.scalacheck:scalacheck:1.11.5:compile, 
    //    org.scalatest:scalatest:2.2.1:compile, 
    //    org.specs2:specs2:2.4:compile, 
    //    org.parboiled:parboiled:2.0.0:compile)

    // but... libraryDependencies is a SettingKey (see ld below)
    // I haven't figured out how to get the sequence of modules from it.
    val ld: SettingKey[Seq[ModuleID]] = libraryDependencies

    // workaround... I found this API that I managed to call...
    // this overrides the classification of all jars -- i.e., it is as if all library dependencies had been classified as "compile".

    // for now... it's a reasonable approaximation of the goal...
    val managed: Classpath = Classpaths.managedJars( Compile, classpathTypes.value, update.value )
    val result: Seq[( File, String )] = managed flatMap { af: Attributed[File] =>
      af.metadata.entries.toList flatMap { e: AttributeEntry[_] =>
        e.value match {
          case null => Seq()
          case m: ModuleID => Seq() ++
            getFileIfExists( new File( ivyHome, s"cache/${m.organization}/${m.name}/srcs/${m.name}-${m.revision}-sources.jar" ), "lib.srcs" ) ++
            getFileIfExists( new File( ivyHome, s"cache/${m.organization}/${m.name}/docs/${m.name}-${m.revision}-javadoc.jar" ), "lib.javadoc" )
          case _ => Seq()
        }
      }
    }
    result
  }

}

感谢sbt-pack和sbt-dependency-graph插件,上面产生了我需要的东西:

scalaLibs/target/dependencies-compile.dot
scalaLibs/target/pack/lib
scalaLibs/target/pack/lib.srcs
scalaLibs/target/pack/lib.javadoc


otherLibs/target/dependencies-compile.dot
otherLibs/target/pack/lib
otherLibs/target/pack/lib.srcs
otherLibs/target/pack/lib.javadoc

可以使用GraphViz显示点文件;它有助于解释为什么包含特定的库...

我想根据以下内容改进这种方法:

  • scalaLibs中的某些库在otherLibs中重复,
  • 这种方法忽略了库依赖性分类&覆盖(此处未使用)

建议?

  • 尼古拉斯。

0 个答案:

没有答案