如何使 SBT 任务依赖于同一 SBT 项目中定义的模块?
How to make a SBT task depend on a module defined in the same SBT project?
我在多模块 SBT 项目中有模块 A 和模块 B。我想为模块 B 编写一个资源生成器任务,它调用模块 A 的代码。一种方法是从 project/
下的模块 A 中提取所有代码,但这是不可行的,因为模块 A 很大,我会喜欢将其保留在原处(参见 )。我如何在 SBT 中执行此操作?
其次,是否有可能完全摆脱模块 B,即我希望模块 A 的资源生成器任务实际上从模块 A 调用代码,但模块 A 是根模块并且不存在于 SBT 中 project
?
注意:这个问题不是这个问题的重复:Defining sbt task that invokes method from project code? 因为那个问题决定将代码移到 SBT 的项目中,这是我在这里特别要避免的事情。
我认为我正在做类似于您在以下项目中的第一部分的事情:我的module gen is the equivalent of your module A, and my module core相当于您的模块B。未经测试,结构大致如下:
// taking inspiration from
//
lazy val ugenGenerator = TaskKey[Seq[File]]("ugen-generate", "Generate UGen class files")
lazy val gen = Project(id = "gen", base = file("gen")) ...
lazy val core = Project(id = "core", base = file("core"))
.settings(
sourceGenerators in Compile <+= ugenGenerator in Compile,
ugenGenerator in Compile := {
val src = (sourceManaged in Compile ).value
val cp = (dependencyClasspath in Runtime in gen ).value
val st = streams.value
runUGenGenerator(description.value, outputDir = src,
cp = cp.files, log = st.log)
}
)
def runUGenGenerator(name: String, outputDir: File, cp: Seq[File],
log: Logger): Seq[File] = {
val mainClass = "my.class.from.Gen"
val tmp = java.io.File.createTempFile("sources", ".txt")
val os = new java.io.FileOutputStream(tmp)
log.info(s"Generating UGen source code in $outputDir for $name")
try {
val outs = CustomOutput(os)
val fOpt = ForkOptions(javaHome = None, outputStrategy = Some(outs), bootJars = cp,
workingDirectory = None, connectInput = false)
val res: Int = Fork.scala(config = fOpt,
arguments = mainClass :: "-d" :: outputDir.getAbsolutePath :: Nil)
if (res != 0) {
sys.error(s"UGen class file generator failed with exit code $res")
}
} finally {
os.close()
}
val sources = scala.io.Source.fromFile(tmp).getLines().map(file).toList
tmp.delete()
sources
}
这在 sbt 0.13 中有效,但我还没有时间弄清楚为什么它在 1.x 中不起作用。
顺便问一下,如何在不使用弃用语法的情况下编写 sourceGenerators in Compile <+= ugenGenerator in Compile
?
基于@0__上面的回答,这是我的简化版本:
/**
* Util to run the main of a sub-module from within SBT
*
* @param cmd The cmd to run with the main class with args (if any)
* @param module The sub-module
*/
def runModuleMain(cmd: String, module: Reference) = Def.task {
val log = streams.value.log
log.info(s"Running $cmd ...")
val classPath = (fullClasspath in Runtime in module).value.files
val opt = ForkOptions(bootJars = classPath, outputStrategy = Some(LoggedOutput(log)))
val res = Fork.scala(config = opt, arguments = cmd.split(' '))
require(res == 0, s"$cmd exited with code $res")
}
然后您可以调用它:
resourceGenerators in Compile += Def.taskDyn {
val dest = (resourceManaged in Compile).value
IO.createDirectory(dest)
runModuleMain(
cmd = "com.mycompany.foo.ResourceGen $dest $arg2 $arg3 ...",
module = $referenceToModule // submodule containing com.mycompany.foo.ResourceGen
).taskValue
dest.listFiles()
}
有 可以做到这一点:
resourceGenerators in Compile += Def.taskDyn {
val dest = (resourceManaged in Compile).value
IO.createDirectory(dest)
Def.task {
val cmd = "com.mycompany.foo.ResourceGen $dest $arg2 $arg3 ..."
(runMain in Compile).toTask(" " + $cmd).value
dest.listFiles()
}
}.taskValue
我在多模块 SBT 项目中有模块 A 和模块 B。我想为模块 B 编写一个资源生成器任务,它调用模块 A 的代码。一种方法是从 project/
下的模块 A 中提取所有代码,但这是不可行的,因为模块 A 很大,我会喜欢将其保留在原处(参见
其次,是否有可能完全摆脱模块 B,即我希望模块 A 的资源生成器任务实际上从模块 A 调用代码,但模块 A 是根模块并且不存在于 SBT 中 project
?
注意:这个问题不是这个问题的重复:Defining sbt task that invokes method from project code? 因为那个问题决定将代码移到 SBT 的项目中,这是我在这里特别要避免的事情。
我认为我正在做类似于您在以下项目中的第一部分的事情:我的module gen is the equivalent of your module A, and my module core相当于您的模块B。未经测试,结构大致如下:
// taking inspiration from
//
lazy val ugenGenerator = TaskKey[Seq[File]]("ugen-generate", "Generate UGen class files")
lazy val gen = Project(id = "gen", base = file("gen")) ...
lazy val core = Project(id = "core", base = file("core"))
.settings(
sourceGenerators in Compile <+= ugenGenerator in Compile,
ugenGenerator in Compile := {
val src = (sourceManaged in Compile ).value
val cp = (dependencyClasspath in Runtime in gen ).value
val st = streams.value
runUGenGenerator(description.value, outputDir = src,
cp = cp.files, log = st.log)
}
)
def runUGenGenerator(name: String, outputDir: File, cp: Seq[File],
log: Logger): Seq[File] = {
val mainClass = "my.class.from.Gen"
val tmp = java.io.File.createTempFile("sources", ".txt")
val os = new java.io.FileOutputStream(tmp)
log.info(s"Generating UGen source code in $outputDir for $name")
try {
val outs = CustomOutput(os)
val fOpt = ForkOptions(javaHome = None, outputStrategy = Some(outs), bootJars = cp,
workingDirectory = None, connectInput = false)
val res: Int = Fork.scala(config = fOpt,
arguments = mainClass :: "-d" :: outputDir.getAbsolutePath :: Nil)
if (res != 0) {
sys.error(s"UGen class file generator failed with exit code $res")
}
} finally {
os.close()
}
val sources = scala.io.Source.fromFile(tmp).getLines().map(file).toList
tmp.delete()
sources
}
这在 sbt 0.13 中有效,但我还没有时间弄清楚为什么它在 1.x 中不起作用。
顺便问一下,如何在不使用弃用语法的情况下编写 sourceGenerators in Compile <+= ugenGenerator in Compile
?
基于@0__上面的回答,这是我的简化版本:
/**
* Util to run the main of a sub-module from within SBT
*
* @param cmd The cmd to run with the main class with args (if any)
* @param module The sub-module
*/
def runModuleMain(cmd: String, module: Reference) = Def.task {
val log = streams.value.log
log.info(s"Running $cmd ...")
val classPath = (fullClasspath in Runtime in module).value.files
val opt = ForkOptions(bootJars = classPath, outputStrategy = Some(LoggedOutput(log)))
val res = Fork.scala(config = opt, arguments = cmd.split(' '))
require(res == 0, s"$cmd exited with code $res")
}
然后您可以调用它:
resourceGenerators in Compile += Def.taskDyn {
val dest = (resourceManaged in Compile).value
IO.createDirectory(dest)
runModuleMain(
cmd = "com.mycompany.foo.ResourceGen $dest $arg2 $arg3 ...",
module = $referenceToModule // submodule containing com.mycompany.foo.ResourceGen
).taskValue
dest.listFiles()
}
有
resourceGenerators in Compile += Def.taskDyn {
val dest = (resourceManaged in Compile).value
IO.createDirectory(dest)
Def.task {
val cmd = "com.mycompany.foo.ResourceGen $dest $arg2 $arg3 ..."
(runMain in Compile).toTask(" " + $cmd).value
dest.listFiles()
}
}.taskValue