如何实现sbt任务以获取项目所有依赖项目的源目录?
Collecting Source Directories from sbt Dependent Projects
Hey there! Your approach to fetching source directories via project dependencies is on the right track, but let's refine it to fix edge cases and make it more aligned with sbt's best practices.
Potential Issues in Your Current Code
- If your project has no dependencies, the
reducecall will throw anUnsupportedOperationException—that's a crash waiting to happen. - The nested
taskDynandflatMaplogic is overcomplicated; sbt's task system can handle dependency resolution automatically without manual task composition here. - Overriding the default
sourcestask isn't ideal, since that's meant to collect the current project's source files, not dependencies'.
Refined Implementation
Here's a cleaner, more robust version:
// Helper to fetch source directories from direct dependencies def sourcesOfDependencies(p: Project): Def.Initialize[Task[Seq[File]]] = Def.task { p.dependencies.map { dep => // sbt automatically resolves the task dependency here (sourceDirectory in dep.project).value }.toSeq } // Create a custom task instead of overriding the default `sources` lazy val collectDependencySources = taskKey[Seq[File]]( "Collects source directories from all directly dependent projects" ) // Bind the helper to your custom task collectDependencySources := sourcesOfDependencies(myProject).value
Key Improvements
- Crash-proof for empty dependencies: Using
map+toSeqreturns an empty sequence when there are no dependencies, instead of throwing an error. - Simpler task logic: We don't need manual
taskDynorflatMap—sbt handles the task dependencies under the hood when we call.valueon each dependency'ssourceDirectory. - Clearer intent: A custom task key (
collectDependencySources) avoids confusion with the defaultsourcestask, which has a different purpose.
Bonus: Recursively Collect All Dependencies (Direct + Indirect)
If you need to grab source directories from all dependencies (including indirect ones), here's a recursive version that handles cyclic dependencies too:
def sourcesOfDependenciesRecursive(p: Project): Def.Initialize[Task[Seq[File]]] = Def.task { // Recursively fetch all unique dependencies, avoiding cycles def getAllDeps(project: Project, visited: Set[Project] = Set.empty): Set[Project] = { if (visited.contains(project)) visited else { val newVisited = visited + project project.dependencies.flatMap(dep => getAllDeps(dep.project, newVisited)).toSet ++ newVisited } } // Exclude the current project itself, then get all source directories val allDeps = getAllDeps(p) - p allDeps.map(dep => (sourceDirectory in dep).value).toSeq } lazy val collectAllDependencySources = taskKey[Seq[File]]( "Collects source directories from all direct and indirect dependent projects" ) collectAllDependencySources := sourcesOfDependenciesRecursive(myProject).value
This version uses a visited set to prevent infinite loops from cyclic dependencies, and pulls in every dependency's source directory recursively.
内容的提问来源于stack exchange,提问作者visa
相关产品推荐
相关产品推荐

