You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何遍历build.sc中所有Mill Target并从外部获取target.dest路径

Mill 自定义Target遍历与后处理工具实现方案

已定义Target遍历的最优实现

不要手动解析build.sc源码,也不要扫描./out目录做反向推导,Mill 本身在构建加载阶段就已经完成了所有模块、Target 的注册收集,直接用内置API就能拿到完整的元数据,完全不需要硬编码目标列表:

  • 所有构建内容都以根 Module 为起点组成树形结构,通过递归遍历 Module 树就能拿到所有已定义的 Module 实例,遍历过程发生在构建评估阶段之前,不会触发任何任务执行。
  • 拿到单个 Module 实例后,通过其millInternal.targets属性就能直接获取该 Module 下声明的所有 Target 实例,和 Target 是否运行过、是否有输出目录完全无关。
  • 过滤逻辑直接用 Scala 类型判断即可,比如筛选所有 ScalaModule 只需要判断module.isInstanceOf[ScalaModule],筛选特定类型的 Target(比如编译、测试、打包任务)可以直接判断 Target 的值类型,能拿到完整的类型信息,不存在扫描out目录无法识别类型的问题。

最小实现示例,直接写在build.sc中即可:

import mill._
import mill.scalalib._
import os.Path

object BuildUtils extends Module {
  // 递归收集所有子Module
  private def collectModules(root: Module): Seq[Module] = {
    root +: root.millInternal.modules.flatMap(collectModules)
  }

  // 示例:收集所有ScalaModule的compile目标
  def listAllCompileTargets: T[Seq[String]] = T {
    val rootModule = implicitly[mill.define.BaseModule]
    val allModules = collectModules(rootModule)
    allModules.collect { case m: ScalaModule => m.compile }
      .map(_.toString)
  }
}

运行mill BuildUtils.listAllCompileTargets就能直接输出所有Scala模块的编译目标全限定名,哪怕你是刚拉取代码、从没运行过任何构建任务,结果也是完整准确的。

外部获取Target对应dest目录的方法

Mill 每个 Target 的输出目录路径是根据 Target 全限定名按固定规则计算的,不需要运行 Target,也不需要扫描out目录,直接用官方API就能拿到准确路径:
对于任意 Target/Task 实例,调用mill.eval.PathResolver.dest(task)即可获取其对应的T.dest路径,这个方法是纯路径计算逻辑,不会触发任务执行,也不会检查路径是否存在。
结合这个API你可以很方便地实现结果状态判断:如果计算出的路径不存在,就说明该Target已经定义但从未运行,或者运行结果被clean命令清理了,正好满足你要识别这类状态的需求。

给个扩展的状态检查示例:

def checkAllTargetStatus: T[Seq[(String, Path, String)]] = T {
  val rootModule = implicitly[mill.define.BaseModule]
  val allTargets = collectModules(rootModule).flatMap(_.millInternal.targets)
  allTargets.map { task =>
    val destPath = mill.eval.PathResolver.dest(task)
    val status = if (os.exists(destPath)) "存在运行结果" else "已定义但无输出"
    (task.toString, destPath, status)
  }
}

为什么不推荐扫描./out目录

你提到的遍历out目录的方案有三个无法解决的硬伤,正好被上述原生API方案完全规避:

  • 无法关联Target的类型信息,不能按模块类型、任务类型做过滤
  • 未运行过、或被清理过的Target没有对应out目录,会漏统计已定义的目标
  • 改名或删除的Target会残留旧的out目录,会误统计不存在的目标

不要自己手动拼接out路径,不同Mill版本的目录层级规则可能有调整,直接用PathResolver.dest能保证跨版本的路径准确性。

内容的提问来源于stack exchange,提问作者user4955663

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 09:42:24