本教程将指导您完成编写自己的sbt插件的过程。 这样做有几个原因,而且非常简单:
- 将定制的构建步骤添加到您的持续集成过程中
- 为各种项目的不同环境提供默认设置
在开始之前,请确保已在计算机上安装了sbt ,并且可以通过命令行对其进行访问。 该代码可在github上找到 。 如果您是从第一次提交开始的,则可以在每次提交时逐步完成本教程。
设定
第一步是设置我们的插件项目。 在build.sbt中只有一个重要的设置,其余的取决于您。
sbtPlugin := true
这会将您的项目标记为sbt-plugin构建。 在本教程中,我使用的是sbt 0.13.7-M3,可让您根据需要编写build.sbt。 无需分隔线。 完整的build.sbt看起来像这样。
name := "awesome-os"
organization := "de.mukis"
scalaVersion in Global := "2.10.2"
sbtPlugin := true
// Settings to build a nice looking plugin site
site.settings
com.typesafe.sbt.SbtSite.SiteKeys.siteMappings <+= (baseDirectory) map { dir =>
val nojekyll = dir / "src" / "site" / ".nojekyll"
nojekyll -> ".nojekyll"
}
site.sphinxSupport()
site.includeScaladoc()
// enable github pages
ghpages.settings
git.remoteRepo := "git@github.com:muuki88/sbt-autoplugins-tutorial.git"
// Scripted - sbt plugin tests
scriptedSettings
scriptedLaunchOpts <+= version apply { v => "-Dproject.version="+v }
在此版本中使用的插件在project / plugins.sbt中配置。 没什么特别的。
外挂程式
现在,我们实现了插件的第一个工作版本和一个测试项目来进行尝试。 插件实际要做的是打印出很棒的操作系统。 稍后,我们将自定义此行为。
让我们看一下我们的插件代码。
import sbt._
import sbt.Keys.{ streams }
/**
* This plugin helps you which operating systems are awesome
*/
object AwesomeOSPlugin extends AutoPlugin {
/**
* Defines all settings/tasks that get automatically imported,
* when the plugin is enabled
*/
object autoImport {
lazy val awesomeOsPrint = TaskKey[Unit]("awesome-os-print", "Prints all awesome operating systems")
lazy val awesomeOsList = SettingKey[Seq[String]]("awesome-os-list", "A list of awesome operating systems")
}
import autoImport._
/**
* Provide default settings
*/
override lazy val projectSettings = Seq(
awesomeOsList := Seq(
"Ubuntu 12.04 LTS","Ubuntu 14.04 LTS","Debian Squeeze",
"Fedora 20","CentOS 6",
"Android 4.x",
"Windows 2000","Windows XP","Windows 7","Windows 8.1",
"MacOS Maverick","MacOS Yosemite",
"iOS 6","iOS 7"
),
awesomeOsPrint := {
awesomeOsList.value foreach (os => streams.value.log.info(os))
}
)
}
就是这样。 我们定义了两个键。 AwesomeOsList是SettingKey ,这意味着它是预先设置的,并且仅在某些人将其显式设置为另一个值或更改它时才会更改,例如
awesomeOsList += "Solaris"
awesomeOsPrint是一项任务,这意味着它在您每次调用时都会执行。
测试项目
让我们尝试一下插件。 为此,我们创建了一个测试项目,该项目对我们的os插件具有插件依赖性。 我们在插件项目的根目录下创建一个test-project目录。 在测试项目内部,我们添加一个build.sbt,其内容如下:
name := "test-project"
version := "1.0"
// enable our now plugin
enablePlugins(AwesomeOSPlugin)
但是,真正的技巧是在test-project / project / plugins.sbt内部完成的。 我们在父目录中创建对项目的引用:
// build root project
lazy val root = Project("plugins", file(".")) dependsOn(awesomeOS)
// depends on the awesomeOS project
lazy val awesomeOS = file("..").getAbsoluteFile.toURI
就这样。 在测试项目中运行sbt并打印出很棒的操作系统。
sbt awesomeOsPrint
如果您在插件代码中更改了某些内容,只需调用reload ,您的测试项目就会重新编译更改。
添加一个新任务并对其进行测试
接下来,我们添加一个将awesomeOsList存储在文件中的任务。 这是我们可以自动测试的。 测试sbt-plugins有点乏味,但是可以使用脚本化插件。
首先,我们在src / sbt-test内部创建一个文件夹。 sbt-test内的目录可以视为将测试放入的类别。 我创建了一个全局文件夹,在其中放置了两个测试项目。 关键配置再次位于project / plugins.sbt中
addSbtPlugin("de.mukis" % "awesome-os" % sys.props("project.version"))
脚本化插件首先在本地对插件进行润饰,然后通过系统属性project.version将版本号传递给每个已启动的sbt测试版本。 我们之前在build.sbt中添加了此行为:
scriptedLaunchOpts <+= version apply { v => "-Dproject.version="+v }
每个测试项目都包含一个名为test的文件,该文件可以包含sbt命令和一些简单的check命令。 通常,您需要进行一些简单的检查,例如文件是否存在 ,并在测试项目中定义的任务中执行更复杂的工作。
我们第二次测试的测试文件如下所示。
# Create the another-os.txt file
> awesomeOsStore
$ exists target/another-os.txt
> check-os-list
检查操作系统列表任务测试项目的build.sbt内部定义(/src/sbt-test/global/store-custom-oslist/build.sbt。
enablePlugins(AwesomeOSPlugin)
name := "simple-test"
version := "0.1.0"
awesomeOsFileName := "another-os.txt"
// this is the scripted test
TaskKey[Unit]("check-os-list") := {
val list = IO.read(target.value / awesomeOsFileName.value)
assert(list contains "Ubuntu", "Ubuntu not present in awesome operating systems: " + list)
}
每个插件单独的操作系统
我们的下一个目标是自定义操作系统列表,以便用户可以选择他们最喜欢的系统。 为此,我们为每个操作系统类别生成一个配置范围 ,并为该范围中的设置生成一个插件。
在实际的插件中,您可以使用它来定义不同环境中的不同操作。 例如发展,分期或生产。 这是自动插件非常关键的一点,因为它允许您启用特定的插件以获得不同的构建风格和/或创建由不同的插件配置的不同的作用域。
第一步是创建三个新的自动插件: AwesomeWindowsPlugin , AwesomeMacPlugin和AwesomeLinuxPlugin 。 它们将以相同的方式工作:
- 将projectSettings的范围从AwesomeOSPlugin扩展到自定义定义的配置范围 ,并将其作为设置提供
- 覆盖自定义定义的配置范围内的特定设置/任务
AwesomeLinuxPlugin如下所示:
import sbt._
object AwesomeLinuxPlugin extends AutoPlugin{
object autoImport {
/** Custom configuration scope */
lazy val Linux = config("awesomeLinux")
}
import AwesomeOSPlugin.autoImport._
import autoImport._
/** This plugin requires the AwesomeOSPlugin to be enabled */
override def requires = AwesomeOSPlugin
/** If all requirements are met, this plugin will automatically get enabled */
override def trigger = allRequirements
/**
* 1. Use the AwesomeOSPlugin settings as default and scope them to Linux
* 2. Override the default settings inside the Linux scope
*/
override lazy val projectSettings = inConfig(Linux)(AwesomeOSPlugin.projectSettings) ++ settings
/**
* the linux specific settings
*/
private lazy val settings: Seq[Setting[_]] = Seq(
awesomeOsList in Linux := Seq(
"Ubuntu 12.04 LTS",
"Ubuntu 14.04 LTS",
"Debian Squeeze",
"Fedora 20",
"CentOS 6",
"Android 4.x"),
// add awesome os to the general list
awesomeOsList ++= (awesomeOsList in Linux).value
)
}
其他插件的定义方式相同。 让我们尝试一下。 在测试项目中启动sbt。
sbt
awesomeOsPrint # will print all operating systems
awesomeWindows:awesomeOsPrint # will only print awesome windows os
awesomeMac:awesomeOsPrint # only mac
awesomeLinux:awesomeOsPrint # only linux
SBT已经提供了一些范围,例如Compile , Test等。因此,只需要创建自己的范围。 大多数时候,您将使用已提供的插件,并在插件中自定义这些插件。
还有一张便条。 您可能想知道为什么全部启用了插件,而我们不必在测试项目中进行任何更改。 这是自动插件的另一个好处。 您可以指定require ,以定义插件和触发器之间的依赖关系,这些触发器指定何时应启用插件。
// what is required that this plugin can be enabled
<span class="k">override</span> <span class="k">def</span> <span class="nf">requires</span> <span class="o">=</span> <span class="n">AwesomeOSPlugin
</span>
// when should this plugin be enabled
<span class="k">override</span> <span class="k">def</span> <span class="nf">trigger</span> <span class="o">=</span> allRequirements
您的插件用户现在不必关心将插件放入build.sbt中的顺序,因为开发人员会预先定义需求,而sbt会尝试满足它们。
结论
SBT Autoplugins使插件用户和开发人员的生活更加轻松。 它稍微降低了sbt的陡峭学习曲线,并创建了更具可读性的buildfile。 对于sbt插件开发人员来说,迁移过程并不困难。 用sbt.AutoPlugin替换sbt.Plugin并创建一个autoImport字段。
翻译自: https://www.javacodegeeks.com/2014/11/sbt-autoplugins-tutorial.html