visa
visa

Reputation: 320

How to get source directories of all project dependencies of an sbt project?

I'm trying to implement an sbt task which collects all the source directories of projects specified using dependsOn method of a project. I end up with this piece of code:

def sourcesOfDependencies(p: Project): Def.Initialize[Task[Seq[File]]] = 
  Def.taskDyn {
    p.dependencies
     .map(dep => (sourceDirectory in dep.project).toTask.map(Seq(_)))
     .reduce((t1, t2) =>
        t1.flatMap(s1 => t2.map(s2 => s1 ++ s2).taskValue)
     )
  }

sources := Def.taskDyn { sourcesOfDependencies(myProject) }.value

As for me it should work but it fails to compile:

[error] /home/visa/src/Playtech-BIT/project/SparkDeployment.scala:57:32: The evaluation of `map` inside an anonymous function is prohibited.
[error]
[error] Problem: Task invocations inside anonymous functions are evaluated independently of whether the anonymous function is invoked or not.
[error]
[error] Solution:
[error]   1. Make `map` evaluation explicit outside of the function body if you don't care about its evaluation.
[error]   2. Use a dynamic task to evaluate `map` and pass that value as a parameter to an anonymous function.
[error]
[error]         t1.flatMap(s1 => t2.map(s2 => s1 ++ s2).taskValue)
[error]                                ^
[error] /home/visa/src/Playtech-BIT/project/SparkDeployment.scala:57:26: Illegal dynamic reference: t2
[error]         t1.flatMap(s1 => t2.map(s2 => s1 ++ s2).taskValue)
[error]                          ^
[error] /home/visa/src/Playtech-BIT/project/SparkDeployment.scala:57:39: Illegal dynamic reference: s1
[error]         t1.flatMap(s1 => t2.map(s2 => s1 ++ s2).taskValue)

Could anyone advise on how to apply the proposed solution? Or maybe there is a simpler way to achieve my goal?

Upvotes: 1

Views: 661

Answers (1)

Pavel S.
Pavel S.

Reputation: 1336

I've been struggling with a similar problem but finally found an answer.

So, what you may do is to define a dynamic task performing your complex computation, like

val buildMaping: Def.Initialize[Task[Seq[Def.Initialize[(ProjectRef, Seq[Seq[File]])]]]] = {
  Def.taskDyn {
    val refs = loadedBuild.value.allProjectRefs

    val tt = refs.map(_._1).map {
      ref =>
        sourceDirectories.all(ScopeFilter(inProjects(ref)))
          .zipWith(Def.setting(ref)) { case (a, b) => b -> a }
    }

    Def.task {
      tt
    }
  }
}

So, this buildMapping allows you to get a map of project references to their source directories.

then invoke your task this way:

Def.task {
    val sd = settingsData.in(Global).value
    val mapping = buildMaping.value.map(_.evaluate(sd)).toMap
    val allProjectRefs = loadedBuild.value.allProjectRefs.map(_._1)
    //... now you may iterate over each project, 
    // get deps and use the mapping to resolve your values
}

Upvotes: 1

Related Questions