将项目传递给sbt动态任务

将项目传递给sbt动态任务,sbt,Sbt,我有一个sbt项目,其中包括代码生成 build.sbt的一部分是 lazy val generator = (project in file("generator")). settings(mainClass := Some("com.example.Generator")) lazy val generate = (project in file("generate")). dependsOn(generator). settings(runGeneration) de

我有一个sbt项目,其中包括代码生成

build.sbt的一部分是

lazy val generator = (project in file("generator")).
   settings(mainClass := Some("com.example.Generator"))

lazy val generate = (project in file("generate")).
   dependsOn(generator).
   settings(runGeneration)

def runGeneration: SettingsDefinition = sourceGenerators in Compile += Def.taskDyn {
    val cachedFun = FileFunction.cached(
                streams.value.cacheDirectory / "generation"
            ) { (in: Set[File]) =>
                    val dir = (sourceManaged in Compile).value
                    (generator / run in Compile).toTask(" " + dir.getAbsolutePath).value
                    collectFiles(dir) 
                  }

        val dependentFiles = ((generator / fullClasspath in Compile) map { cp => cp.files }).taskValue.value

        val genFiles = cachedFun(dependenctFiles).toSeq
        Def.task {
            genFiles
        }
    }.taskValue
这似乎是可行的,并且只在依赖项发生更改时生成文件。但是,我希望有多个发电机。我没有复制代码,而是尝试将
generator
项目传递给它:

lazy val generate = (project in file("generate")).
   dependsOn(generator).
   settings(runGeneration(generator))

def runGeneration(p: project): SettingsDefinition = 
   <same as before but with p instead of generator>
我猜问题在于它无法在编译时判断是否存在依赖循环,因此它会保守地给出一个错误


有没有办法让它发挥作用?是否有一个完全不同的结构让我知道运行
generator
是否会产生不同的结果?

根本的问题是
sbt
中的任务定义有两个组件,看起来它们可以混合,但不能。如果你像这样写代码

Def.task {
  val doIt = checkIfShouldDoIt()

  if (doIt) {
    someTask.value
  } else { 
    ()
  }
}
如果
doIt
为真,那么它看起来只会运行
someTask
。实际发生的情况是,
someTask.value
声明此任务对
someTask
的依赖关系,并且在对此任务执行任何操作之前运行
someTask
。要以更直接地映射实际发生的情况的方式编写上述代码,可以编写

Def.task {
  val someTaskValue = someTask.value

  val doIt = checkIfShouldDoIt()
  if (doIt) {
    someTaskValue
  } else { 
    ()
  }
}

仅当依赖项已更改时才尝试运行任务无法在单个任务中运行

我的工作解决方案如下所示。我修改了生成器以接受一个附加参数,如果该参数为false,则不执行任何操作。这两项任务是:

// Task to check if we need to generate
def checkGeneration(p: Project) = Def.taskDyn {
  var needToGenerate = false
  val cachedFunction = FileFunction.cached(someDir) {
     (in: Set[File]) =>  
        needToGenerate = ! in.isEmpty
        Set()
    }

  val dependentFiles = ((p / fullClasspath in Compile) map { cp => cp.files }).taskValue

  Def.task {
    cachedFun(dependentFiles.value.toSet)
    needToGenerate
  }
}

// Task to run generation
def runGeneration(p: Project): SettingsDefinition = sourceGenerators in Compile += Def.taskDyn {
  val needToGenerate = checkGeneration(p).value

  Def.task {
    // Run generator as before but pass needToGenerate as additional argument
    ...
    // Used FileFunction.cached as before to find the generated files (but not run the generator)
    ...
  }
}
我可能有比我需要的更多的动态任务,但这是可行的

// Task to check if we need to generate
def checkGeneration(p: Project) = Def.taskDyn {
  var needToGenerate = false
  val cachedFunction = FileFunction.cached(someDir) {
     (in: Set[File]) =>  
        needToGenerate = ! in.isEmpty
        Set()
    }

  val dependentFiles = ((p / fullClasspath in Compile) map { cp => cp.files }).taskValue

  Def.task {
    cachedFun(dependentFiles.value.toSet)
    needToGenerate
  }
}

// Task to run generation
def runGeneration(p: Project): SettingsDefinition = sourceGenerators in Compile += Def.taskDyn {
  val needToGenerate = checkGeneration(p).value

  Def.task {
    // Run generator as before but pass needToGenerate as additional argument
    ...
    // Used FileFunction.cached as before to find the generated files (but not run the generator)
    ...
  }
}