【问题标题】:How to use custom plugin in multi-module project?如何在多模块项目中使用自定义插件?
【发布时间】:2014-07-23 22:53:38
【问题描述】:

在一个多模块项目中,一个模块使用自定义 TaskKey 实现自定义 SBT 插件,如何导入此插件以用于另一个子模块的项目设置。

如果使用插件的子模块定义在submodule1/build.sbt中,则submodule1/project/plugins.sbt不会被加载。

如果插件在project/plugins.sbt 中注册,加载顶部/聚合项目时将失败,因为插件不一定已经构建。

是否有任何其他方法可以定义需要自定义依赖项的自定义任务,以便子模块可以使用它?

【问题讨论】:

    标签: scala sbt


    【解决方案1】:

    这是我最终实现它的方法:

    import sbt._
    import Keys._
    
    object MyBuild extends Build {
      private lazy val myGenerator = 
        // Private project with generator code and its specific dependencies
        // (e.g. Javassist)
        Project(id = "my-generator", 
          base = file("project") / "my-generator").settings(
          name := "my-generator",
          javacOptions in Test ++= Seq("-Xlint:unchecked", "-Xlint:deprecation"),
          autoScalaLibrary := false,
          scalacOptions += "-feature",
          resolvers += 
            "Typesafe Snapshots" at "http://repo.typesafe.com/typesafe/releases/",
          libraryDependencies ++= Seq( // Dependencies required to generate classes
            "org.javassist" % "javassist" % "3.18.2-GA")
        )
    
      // Some custom setting & task
      lazy val generatedClassDirectory = settingKey[File](
        "Directory where classes get generated")
    
      lazy val generatedClasses = taskKey[Seq[(File, String)]]("Generated classes")
    
      lazy val myProject =
        Project(id = "my-project", base = file("my-project")).settings(
          name := "my-project",
          javacOptions in Test ++= Seq("-Xlint:unchecked", "-Xlint:deprecation"),
          autoScalaLibrary := false,
          scalacOptions += "-feature",
          libraryDependencies ++= Seq(/* ... */),
          generatedClassDirectory := { 
            // Defines setting for path to generated classes
            val dir = target.value / "generated_classes"
            if (!dir.exists) dir.mkdirs()
            dir
          },
          generatedClasses <<= Def.task { // Define task generating .class files
            // first get classloader including generator and its dependencies
            val cp = (fullClasspath in (myGenerator, Compile)).value
            val cl = classpath.ClasspathUtilities.toLoader(cp.files)
    
            // then loaded generator class, and instantiate with structural type
            val genClass = cl loadClass "my.custom.GeneratorClass"
            val generator = genClass.newInstance.
              asInstanceOf[{def writeTo(out: File): File}]
    
            // finally we can call the
            val outdir = generatedClassDirectory.value
            val generated = generator writeTo outdir
            val path = generated.getAbsolutePath
    
            // Mappings describing generated classes
            Seq[(File, String)](generated -> path.
              drop(outdir.getAbsolutePath.length+1))
    
          } dependsOn(compile in (myGenerator, Compile))/* awkward? */,
          managedClasspath in Compile := {
            // Add generated classes to compilation classpath, 
            // so it can be used in my-project sources
            val cp = (managedClasspath in Compile).value
            cp :+ Attributed.blank(generatedClassDirectory.value)
          },
          // Make sure custom class generation is done before compile
          compile in Compile <<= (compile in Compile) dependsOn generatedClasses,
          mappings in (Compile, packageBin) := {
            val ms = mappings.in(Compile, packageBin).value
            ms ++ generatedClasses.value // add generated classes to package
          }
        ).dependsOn(myGenerator/* required even if there dependsOn(compile in (myGenerator, Compile)) */)
    }
    

    不确定是否有更好的解决方案,尤其是关于 redondant dependsOn(compile in (myGenerator, Compile)).dependsOn(myGenerator)

    【讨论】:

      猜你喜欢
      • 2015-08-18
      • 1970-01-01
      • 2012-10-02
      • 1970-01-01
      • 2014-07-27
      • 1970-01-01
      • 2011-03-10
      • 1970-01-01
      • 2013-04-09
      相关资源
      最近更新 更多