如何在SBT中针对特定的开发阶段应用不同的scalacOptions?

huangapple go评论52阅读模式
英文:

How to apply different scalacOptions in SBT for specific development phases?

问题

在开发过程中,通常有两个不同的阶段:"脏开发"阶段,开发人员在这个阶段解决问题,测试各种概念,引入临时代码结构,然后是"清理"阶段,用于在提交最终版本或拉取请求之前完善代码。在脏开发阶段,常常会出现一些问题,比如冗余或无用的代码和未使用的导入,缺少明确的类型等。在这种情况下,即使在开发过程中出现警告,让代码能够编译也是有益的。

我应该如何配置SBT以在编码的不同阶段使用不同的scalacOptions

假设在"脏开发"阶段,我想使用空的scalacOptions,但在进行最终清理时,它们可能如下所示:

val scalaCompilerOptions = Seq(
  "-Xfatal-warnings",
  "-Xlint:-missing-interpolator,_",
  "-Xlint:adapted-args",
  "-Xlint:constant",
  "-Xlint:-byname-implicit",
  "-Ywarn-unused:imports",
  "-Ywarn-unused:patvars",
  "-Ywarn-unused:privates",
  "-Ywarn-unused:locals",
  "-Ywarn-unused:explicits",
  "-Ywarn-unused:params",
  "-Ywarn-unused:implicits",
  "-Ywarn-value-discard",
  "-Ywarn-dead-code",
  "-deprecation",
  "-feature",
  "-unchecked",
  // ...
)

理想情况下,当输入test:compile任务时,它将使用空的scalacOptions,但在使用test(或packagedistTgz)时,它将应用限制性选项。

sbt.version=1.7.2

英文:

During the development process, there are often two distinct phases: "dirty development," where developers work on solutions, test various concepts, and introduce temporary code structures, and a "cleanup" phase to refine the code before submitting a final version or a pull request. During dirty development, it is common to have imperfections such as redundant or dead code and unused imports, missing explicit types, etc. In this context, it is beneficial for the code to compile even when there are warnings during development.

How can I configure SBT to use various scalacOptions for those different phases of coding?

Let's assume that for the "dirty development" phase, I'd like to use empty scalacOptions, but when doing final cleanup, those could look like:

val scalaCompilerOptions = Seq(
  "-Xfatal-warnings",
  "-Xlint:-missing-interpolator,_",
  "-Xlint:adapted-args",
  "-Xlint:constant",
  "-Xlint:-byname-implicit",
  "-Ywarn-unused:imports",
  "-Ywarn-unused:patvars",
  "-Ywarn-unused:privates",
  "-Ywarn-unused:locals",
  "-Ywarn-unused:explicits",
  "-Ywarn-unused:params",
  "-Ywarn-unused:implicits",
  "-Ywarn-value-discard",
  "-Ywarn-dead-code",
  "-deprecation",
  "-feature",
  "-unchecked",
...
)

Ideally, when entering test:compile task, it would use empty scalacOptions, but when using test (or package or distTgz), it would apply the restrictive options.

sbt.version=1.7.2

答案1

得分: 4

以下是翻译好的部分:

一种方法(不是唯一的方法)是创建自己的设置,然后使 scalacOptions 依赖于该设置。然后,您可以随时使用 set 更改该设置。

例如,在 Scala 2 编译器的 sbt 构建中(https://github.com/scala/scala),我们希望在本地开发期间禁用致命警告,但在持续集成中启用。

因此,我们的 build.sbt 如下所示:

val fatalWarnings = settingKey[Boolean](
  "are compiler warnings fatal")
Global / fatalWarnings := insideCI.value
// 然后在您构建中适当的上下文中,
Compile / scalacOptions ++= {
  if (fatalWarnings.value) Seq("-Werror")
  else Nil
}

然后,在本地当我们想临时启用致命警告时,使用 set Global / fatalWarnings := true

这对我来说似乎是一种比 Gastón 建议的更轻量级的解决方案。

英文:

One way (not the only way) is to make your own setting and then have scalacOptions depend on that setting. Then you can change the setting anytime with set.

For example, in the sbt build for the Scala 2 compiler (https://github.com/scala/scala), we want to have fatal warnings disabled during local development, but enabled in CI.

So our build.sbt has:

val fatalWarnings = settingKey[Boolean](
  "are compiler warnings fatal")
Global / fatalWarnings := insideCI.value
// then in whatever context is appropriate in your build,
Compile / scalacOptions ++= {
  if (fatalWarnings.value) Seq("-Werror")
  else Nil
}

Then locally when we want to temporarily enable fatal warnings, set Global / fatalWarnings := true.

This seems like a bit lighter-weight solution to me than Gastón's suggestion.

答案2

得分: 1

In sbt, you can create different sub modules with different configurations.

The app:

// the application
lazy val app = project
  .in(file("."))
  .settings(
    name := "my-app",
    libraryDependencies += (
      dependency1,
      dependency2,
      // ...
      dependencyN,
    )
  )

And then different sub modules with different configurations:

lazy val testPackage = project
  // we put the results in a build folder
  .in(file("build/test"))
  .settings(
    scalacOptions in Compile := Seq(
      // options for test
    ),
    // override the resource directory
    resourceDirectory in Compile := (resourceDirectory in (app, Compile)).value,
    mappings in Universal += {
      ((resourceDirectory in Compile).value / "test.conf") -> "conf/application.conf"
    }
  )
  .dependsOn(app)

// basically identical despite the configuration differences
lazy val stagePackage = project
  .in(file("build/stage"))
  .settings(
    scalacOptions in Compile := Seq(
      // options for stage
    ),
    resourceDirectory in Compile := (resourceDirectory in (app, Compile)).value,
    mappings in Universal += {
      ((resourceDirectory in Compile).value / "stage.conf") -> "conf/application.conf"
    }
  )
  .dependsOn(app)

lazy val prodPackage = project
  .in(file("build/prod"))
  .settings(
    scalacOptions in Compile := Seq(
      // options for prod
    ),
    resourceDirectory in Compile := (resourceDirectory in (app, Compile)).value,
    mappings in Universal += {
      ((resourceDirectory in Compile).value / "prod.conf") -> "conf/application.conf"
    }
  )
  .dependsOn(app)

The plugin sbt-native-packager has a good example of how to do it.

英文:

In sbt you can create different sub modules with different configurations.

The app

// the application
lazy val app = project
  .in(file("."))
  .settings(
    name := "my-app",
    libraryDependencies += (
      dependency1,
      dependency2,
      // ...
      dependencyN,

    )
  )

and then different sub modules with different configurations

lazy val testPackage = project
  // we put the results  in a build folder
  .in(file("build/test"))
  .settings(
    scalacOptions in Compile := Seq(
      // options for test
    ),
    // override the resource directory
    resourceDirectory in Compile := (resourceDirectory in (app, Compile)).value,
    mappings in Universal += {
      ((resourceDirectory in Compile).value / "test.conf") -> "conf/application.conf"
    }
  )
  .dependsOn(app)

// bascially identical despite the configuration differences
lazy val stagePackage = project
  .in(file("build/stage"))
  .settings(
    scalacOptions in Compile := Seq(
      // options for stage
    ),
    resourceDirectory in Compile := (resourceDirectory in (app, Compile)).value,
    mappings in Universal += {
      ((resourceDirectory in Compile).value / "stage.conf") -> "conf/application.conf"
    }
  )
  .dependsOn(app)

lazy val prodPackage = project
  .in(file("build/prod"))
  .settings(
    scalacOptions in Compile := Seq(
      // options for prod
    ),
    resourceDirectory in Compile := (resourceDirectory in (app, Compile)).value,
    mappings in Universal += {
      ((resourceDirectory in Compile).value / "prod.conf") -> "conf/application.conf"
    }
  )
  .dependsOn(app)

The plugin sbt-native-packager have a good example of how to do it.

huangapple
  • 本文由 发表于 2023年5月7日 01:53:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/76190333.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定