[
  {
    "path": ".git-blame-ignore-revs",
    "content": "# Reformatted code base with current scalafmt settings\nd910117c4c113c5358b0bb7f79be3f2090d55f77\n\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n"
  },
  {
    "path": ".github/workflows/actions.yml",
    "content": "name: ci\n\non:\n  push:\n  pull_request:\n    branches:\n      - main\n  workflow_dispatch:\n    inputs:\n      scala_version:\n        description: 'Single Scala version to publish'\n        required: true\n        type: string\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    env:\n      SCALA_VERSION: ${{ inputs.scala_version }}\n    steps:\n      - uses: actions/checkout@v4\n      - name: Run tests\n        run: ./mill __.publishArtifacts + __.test\n\n  publish-sonatype:\n    if: github.repository == 'com-lihaoyi/acyclic' && contains(github.ref, 'refs/tags/')\n    needs: test\n    runs-on: ubuntu-latest\n    env:\n      SCALA_VERSION: ${{ inputs.scala_version }}\n      MILL_SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}\n      MILL_SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}\n      MILL_PGP_SECRET_BASE64: ${{ secrets.SONATYPE_PGP_PRIVATE_KEY }}\n      MILL_PGP_PASSPHRASE: ${{ secrets.SONATYPE_PGP_PRIVATE_KEY_PASSWORD }}\n      LANG: \"en_US.UTF-8\"\n      LC_MESSAGES: \"en_US.UTF-8\"\n      LC_ALL: \"en_US.UTF-8\"\n    steps:\n      - uses: actions/checkout@v4\n      - name: Publish to Maven Central\n        run: ./mill -i mill.scalalib.SonatypeCentralPublishModule/\n"
  },
  {
    "path": ".gitignore",
    "content": "out/*\ntarget/*\nproject/target/*\n.idea/*\n.idea_modules/*\n*.iml\n.bsp/\n.cursor/\n.idea/\n"
  },
  {
    "path": ".mill-jvm-version",
    "content": "temurin:17\n"
  },
  {
    "path": ".mill-version",
    "content": "1.0.5-native\n"
  },
  {
    "path": ".scalafmt.conf",
    "content": "version = \"3.8.1\"\n\nalign.preset = none\nalign.openParenCallSite = false\nalign.stripMargin = true\n\nassumeStandardLibraryStripMargin = true\n\ncontinuationIndent.callSite = 2\ncontinuationIndent.defnSite = 4\n\ndocstrings.style = Asterisk\ndocstrings.oneline = keep\ndocstrings.wrap = no\n\nmaxColumn = 120\n\nnewlines.source = keep\n\nrunner.dialect = scala213\n\nfileOverride {\n  \"glob:**/*.mill\" {\n    runner.dialect = scala3\n  }\n  \"glob:**/src-3/**/*.scala\" {\n    runner.dialect = scala3\n  }\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2014 Li Haoyi\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "acyclic/resources/plugin.properties",
    "content": "pluginClass=acyclic.plugin.RuntimePlugin\n"
  },
  {
    "path": "acyclic/resources/scalac-plugin.xml",
    "content": "<plugin>\n    <name>acyclic</name>\n    <classname>acyclic.plugin.RuntimePlugin</classname>\n</plugin>"
  },
  {
    "path": "acyclic/src/acyclic/package.scala",
    "content": "import scala.annotation.compileTimeOnly\npackage object acyclic {\n\n  /**\n   * Import this within a file to make Acyclic verify that the file does not\n   * have any circular dependencies with other files.\n   */\n  @compileTimeOnly(\"acyclic.file is just a marker and not a real value\")\n  def file = ()\n\n  /**\n   */\n  @compileTimeOnly(\"acyclic.file is just a marker and not a real value\")\n  def skipped = ()\n\n  /**\n   * Import this within a package object to make Acyclic verify that the entire\n   * package does not have any circular dependencies with other files or\n   * packages. Circular dependencies *within* the package are Ok.\n   */\n  @compileTimeOnly(\"acyclic.pkg is just a marker and not a real value\")\n  def pkg = ()\n}\n"
  },
  {
    "path": "acyclic/src/acyclic/plugin/BasePluginPhase.scala",
    "content": "package acyclic.plugin\n\nimport acyclic.plugin.Compat._\nimport scala.collection.{mutable, SortedSet}\n\ntrait BasePluginPhase[CompilationUnit, Tree, Symbol] { self: GraphAnalysis[Tree] =>\n  protected val cycleReporter: Seq[(Value, SortedSet[Int])] => Unit\n  protected def force: Boolean\n  protected def fatal: Boolean\n\n  def treeLine(tree: Tree): Int\n  def treeSymbolString(tree: Tree): String\n\n  def reportError(msg: String): Unit\n  def reportWarning(msg: String): Unit\n  def reportInform(msg: String): Unit\n  def reportEcho(msg: String, tree: Tree): Unit\n\n  def units: Seq[CompilationUnit]\n  def unitTree(unit: CompilationUnit): Tree\n  def unitPath(unit: CompilationUnit): String\n  def unitPkgName(unit: CompilationUnit): List[String]\n  def findPkgObjects(tree: Tree): List[Tree]\n  def pkgObjectName(pkgObject: Tree): String\n  def hasAcyclicImport(tree: Tree, selector: String): Boolean\n\n  def extractDependencies(unit: CompilationUnit): Seq[(Symbol, Tree)]\n  def symbolPath(sym: Symbol): String\n  def isValidSymbol(sym: Symbol): Boolean\n\n  final def findAcyclics(): (Seq[Value.File], Seq[Value.File], Seq[Value.Pkg]) = {\n    val acyclicNodePaths = for {\n      unit <- units if hasAcyclicImport(unitTree(unit), \"file\")\n    } yield {\n      Value.File(unitPath(unit), unitPkgName(unit))\n    }\n    val skipNodePaths = for {\n      unit <- units if hasAcyclicImport(unitTree(unit), \"skipped\")\n    } yield {\n      Value.File(unitPath(unit), unitPkgName(unit))\n    }\n\n    val acyclicPkgNames = for {\n      unit <- units\n      pkgObject <- findPkgObjects(unitTree(unit))\n      if hasAcyclicImport(pkgObject, \"pkg\")\n    } yield Value.Pkg(pkgObjectName(pkgObject).split('.').toList)\n    (skipNodePaths, acyclicNodePaths, acyclicPkgNames)\n  }\n\n  final def runAllUnits(): Unit = {\n    val unitMap = units.map(u => unitPath(u) -> u).toMap\n    val nodes = for (unit <- units) yield {\n      val deps = extractDependencies(unit)\n\n      val connections = for {\n        (sym, tree) <- deps\n        if isValidSymbol(sym)\n        if symbolPath(sym) != unitPath(unit)\n        if unitMap.contains(symbolPath(sym))\n      } yield (symbolPath(sym), tree)\n\n      Node[Value.File, Tree](\n        Value.File(unitPath(unit), unitPkgName(unit)),\n        connections.groupBy(c => Value.File(c._1, unitPkgName(unitMap(c._1))): Value)\n          .mapValues(_.map(_._2))\n          .toMap\n      )\n    }\n\n    val nodeMap = nodes.map(n => n.value -> n).toMap\n\n    val (skipNodePaths, acyclicFiles, acyclicPkgs) = findAcyclics()\n\n    val allAcyclics = acyclicFiles ++ acyclicPkgs\n\n    // synthetic nodes for packages, which aggregate the dependencies of\n    // their contents\n    val pkgNodes = acyclicPkgs.map { value =>\n      Node(\n        value,\n        nodes.filter(_.value.pkg.startsWith(value.pkg))\n          .flatMap(_.dependencies.toSeq)\n          .groupBy(_._1)\n          .mapValues(_.flatMap(_._2))\n          .toMap\n      )\n    }\n\n    val linkedNodes: Seq[DepNode] = (nodes ++ pkgNodes).map { d =>\n      val extraLinks = d.dependencies.flatMap {\n        case (value: Value.File, pos) =>\n          for {\n            acyclicPkg <- acyclicPkgs\n            if nodeMap(value).value.pkg.startsWith(acyclicPkg.pkg)\n            if !d.value.pkg.startsWith(acyclicPkg.pkg)\n          } yield (acyclicPkg, pos)\n\n        case (_: Value.Pkg, _) => Nil\n      }\n      d.copy(dependencies = d.dependencies ++ extraLinks)\n    }\n\n    // only care about cycles with size > 1 here\n    val components = DepNode.stronglyConnectedComponents(linkedNodes).filter(_.size > 1)\n\n    val usedNodes = mutable.Set.empty[DepNode]\n    for {\n      c <- components\n      n <- c\n      if !usedNodes.contains(n)\n      if (!force && allAcyclics.contains(n.value)) || (force && !skipNodePaths.contains(n.value))\n    } {\n      val cycle = DepNode.smallestCycle(n, c)\n      val cycleInfo =\n        (cycle :+ cycle.head).sliding(2)\n          .map { case Seq(a, b) => (a.value, a.dependencies(b.value)) }\n          .toSeq\n      cycleReporter(\n        cycleInfo.map { case (a, b) => a -> b.map(treeLine).to(SortedSet) }\n      )\n\n      val msg = \"Unwanted cyclic dependency\"\n      if (fatal) {\n        reportError(msg)\n      } else {\n        reportWarning(msg)\n      }\n\n      for (Seq((value, locs), (nextValue, _)) <- (cycleInfo :+ cycleInfo.head).sliding(2)) {\n        reportInform(\"\")\n        value match {\n          case Value.Pkg(pkg) => reportInform(s\"package ${pkg.mkString(\".\")}\")\n          case Value.File(_, _) =>\n        }\n\n        reportEcho(\"\", locs.head)\n\n        val otherLines = locs.tail\n          .map(treeLine)\n          .filter(_ != treeLine(locs.head))\n\n        reportInform(\"symbol: \" + treeSymbolString(locs.head))\n\n        if (!otherLines.isEmpty) {\n          reportInform(\"More dependencies at lines \" + otherLines.mkString(\" \"))\n        }\n\n      }\n      reportInform(\"\")\n      usedNodes ++= cycle\n    }\n  }\n}\n"
  },
  {
    "path": "acyclic/src/acyclic/plugin/GraphAnalysis.scala",
    "content": "package acyclic.plugin\n\nimport acyclic.file\nimport collection.mutable\n\nsealed trait Value {\n  def pkg: List[String]\n  def prettyPrint: String\n}\nobject Value {\n  case class File(path: String, pkg: List[String] = Nil) extends Value {\n    def prettyPrint = s\"file $path\"\n  }\n  case class Pkg(pkg: List[String]) extends Value {\n    def prettyPrint = s\"package ${pkg.mkString(\".\")}\"\n  }\n  object Pkg {\n    def apply(s: String): Pkg = apply(s.split('.').toList)\n  }\n}\n\ncase class Node[+T <: Value, Tree](value: T, dependencies: Map[Value, Seq[Tree]]) {\n  override def toString = s\"DepNode(\\n  $value, \\n  ${dependencies.keys}\\n)\"\n}\n\ntrait GraphAnalysis[Tree] {\n  type DepNode = Node[Value, Tree]\n  type FileNode = Node[Value.File, Tree]\n  type PkgNode = Node[Value.Pkg, Tree]\n\n  object DepNode {\n\n    /**\n     * Does a double Breadth-First-Search to find the shortest cycle starting\n     * from `from` within the DepNodes in `among`.\n     */\n    def smallestCycle(from: DepNode, among: Seq[DepNode]): Seq[DepNode] = {\n      val nodeMap = among.map(n => n.value -> n).toMap\n      val distances = mutable.Map(from -> 0)\n      val queue = mutable.Queue(from)\n      while (queue.nonEmpty) {\n        val next = queue.dequeue()\n        val children = next.dependencies\n          .keys\n          .collect(nodeMap)\n          .filter(!distances.contains(_))\n\n        children.foreach(distances(_) = distances(next) + 1)\n        queue ++= children\n      }\n      var route = List(from)\n      while (route.length == 1 || route.head != from) {\n        route ::= among.filter(x => x.dependencies.keySet.contains(route.head.value))\n          .minBy(distances)\n      }\n      route.tail\n    }\n\n    /**\n     * Finds the strongly-connected components of the directed DepNode graph\n     * by finding cycles in a Depth-First manner and collapsing any components\n     * whose nodes are involved in the cycle.\n     */\n    def stronglyConnectedComponents(nodes: Seq[DepNode]): Seq[Seq[DepNode]] = {\n\n      val nodeMap = nodes.map(n => n.value -> n).toMap\n\n      val components = mutable.Map.empty[DepNode, Int] ++ nodes.zipWithIndex.toMap\n      val visited = mutable.Set.empty[DepNode]\n\n      nodes.foreach(n => rec(n, Nil))\n\n      def rec(node: DepNode, path: List[DepNode]): Unit = {\n        if (path.exists(components(_) == components(node))) {\n          val cycle = path.reverse\n            .dropWhile(components(_) != components(node))\n\n          val involved = cycle.map(components)\n          val firstIndex = involved.head\n          for ((n, i) <- components.toSeq) {\n            if (involved.contains(i)) {\n              components(n) = firstIndex\n            }\n          }\n        } else if (!visited(node)) {\n          visited.add(node)\n          // sketchy sorting to make sure we're doing this deterministically...\n          for ((key, lines) <- node.dependencies.toSeq.sortBy(_._1.toString)) {\n            rec(nodeMap(key), node :: path)\n          }\n        }\n      }\n\n      components.groupBy { case (node, i) => i }\n        .toSeq\n        .sortBy(_._1)\n        .map(_._2.keys.toSeq)\n    }\n  }\n\n}\n"
  },
  {
    "path": "acyclic/src-2/acyclic/plugin/DependencyExtraction.scala",
    "content": "//acyclic\npackage acyclic.plugin\nimport acyclic.file\nimport scala.tools.nsc.Global\nobject DependencyExtraction {\n  def apply(global: Global)(unit: global.CompilationUnit): Seq[(global.Symbol, global.Tree)] = {\n    import global._\n\n    class CollectTypeTraverser[T](pf: PartialFunction[Type, T]) extends TypeTraverser {\n      var collected: List[T] = Nil\n      def traverse(tpe: Type): Unit = {\n        if (pf.isDefinedAt(tpe))\n          collected = pf(tpe) :: collected\n        mapOver(tpe)\n      }\n    }\n\n    class ExtractDependenciesTraverser extends Traverser {\n      protected val depBuf = collection.mutable.ArrayBuffer.empty[(Symbol, Tree)]\n      protected def addDependency(sym: Symbol, tree: Tree): Unit = depBuf += ((sym, tree))\n      def dependencies: collection.immutable.Set[(Symbol, Tree)] = {\n        // convert to immutable set and remove NoSymbol if we have one\n        depBuf.toSet\n      }\n\n    }\n\n    class ExtractDependenciesByMemberRefTraverser extends ExtractDependenciesTraverser {\n      override def traverse(tree: Tree): Unit = {\n        tree match {\n          case i @ Import(expr, selectors) =>\n            selectors.foreach {\n              case ImportSelector(nme.WILDCARD, _, null, _) =>\n              // in case of wildcard import we do not rely on any particular name being defined\n              // on `expr`; all symbols that are being used will get caught through selections\n              case ImportSelector(name: Name, _, _, _) =>\n                def lookupImported(name: Name) = expr.symbol.info.member(name)\n                // importing a name means importing both a term and a type (if they exist)\n                addDependency(lookupImported(name.toTermName), tree)\n                addDependency(lookupImported(name.toTypeName), tree)\n            }\n          case select: Select =>\n            addDependency(select.symbol, tree)\n          /*\n           * Idents are used in number of situations:\n           *  - to refer to local variable\n           *  - to refer to a top-level package (other packages are nested selections)\n           *  - to refer to a term defined in the same package as an enclosing class;\n           *    this looks fishy, see this thread:\n           *    https://groups.google.com/d/topic/scala-internals/Ms9WUAtokLo/discussion\n           */\n          case ident: Ident =>\n            addDependency(ident.symbol, tree)\n          case typeTree: TypeTree =>\n            val typeSymbolCollector = new CollectTypeTraverser({\n              case tpe if tpe != null && tpe.typeSymbol != null && !tpe.typeSymbol.isPackage => tpe.typeSymbol\n            })\n            typeSymbolCollector.traverse(typeTree.tpe)\n            val deps = typeSymbolCollector.collected.toSet\n            deps.foreach(addDependency(_, tree))\n          case Template(parents, self, body) =>\n            traverseTrees(body)\n          case other => ()\n        }\n        super.traverse(tree)\n      }\n    }\n\n    def byMembers(): collection.immutable.Set[(Symbol, Tree)] = {\n      val traverser = new ExtractDependenciesByMemberRefTraverser\n      if (!unit.isJava)\n        traverser.traverse(unit.body)\n      traverser.dependencies\n    }\n\n    class ExtractDependenciesByInheritanceTraverser extends ExtractDependenciesTraverser {\n      override def traverse(tree: Tree): Unit = tree match {\n        case Template(parents, self, body) =>\n          // we are using typeSymbol and not typeSymbolDirect because we want\n          // type aliases to be expanded\n          val parentTypeSymbols = parents.map(parent => parent.tpe.typeSymbol).toSet\n          debuglog(\"Parent type symbols for \" + tree.pos + \": \" + parentTypeSymbols.map(_.fullName))\n          parentTypeSymbols.foreach(addDependency(_, tree))\n          traverseTrees(body)\n        case tree => super.traverse(tree)\n      }\n    }\n\n    def byInheritence(): collection.immutable.Set[(Symbol, Tree)] = {\n      val traverser = new ExtractDependenciesByInheritanceTraverser\n      if (!unit.isJava)\n        traverser.traverse(unit.body)\n      traverser.dependencies\n    }\n\n    (byMembers() | byInheritence()).toSeq\n  }\n}\n"
  },
  {
    "path": "acyclic/src-2/acyclic/plugin/Plugin.scala",
    "content": "package acyclic.plugin\nimport acyclic.file\nimport tools.nsc.Global\nimport scala.collection.SortedSet\n\nclass RuntimePlugin(global: Global) extends TestPlugin(global)\nclass TestPlugin(val global: Global, cycleReporter: Seq[(Value, SortedSet[Int])] => Unit = _ => ())\n    extends tools.nsc.plugins.Plugin {\n\n  val name = \"acyclic\"\n\n  var force = false\n  var fatal = true\n\n  // Yeah processOptions is deprecated but keep using it anyway for 2.10.x compatibility\n  override def processOptions(options: List[String], error: String => Unit): Unit = {\n    if (options.contains(\"force\")) {\n      force = true\n    }\n    if (options.contains(\"warn\")) {\n      fatal = false\n    }\n  }\n  val description = \"Allows the developer to prohibit inter-file dependencies\"\n\n  val components = List[tools.nsc.plugins.PluginComponent](\n    new PluginPhase(this.global, cycleReporter, force, fatal)\n  )\n}\n"
  },
  {
    "path": "acyclic/src-2/acyclic/plugin/PluginPhase.scala",
    "content": "package acyclic.plugin\n\nimport acyclic.file\nimport acyclic.plugin.Compat._\nimport scala.collection.{SortedSet, mutable}\nimport scala.tools.nsc.{Global, Phase}\nimport tools.nsc.plugins.PluginComponent\n\n/**\n * - Break dependency graph into strongly connected components\n * - Turn acyclic packages into virtual \"files\" in the dependency graph, as\n *   aggregates of all the files within them\n * - Any strongly connected component which includes an acyclic.file or\n *   acyclic.pkg is a failure\n *   - Pick an arbitrary cycle and report it\n * - Don't report more than one cycle per file/pkg, to avoid excessive spam\n */\nclass PluginPhase(\n    val global: Global,\n    cycleReporter: Seq[(Value, SortedSet[Int])] => Unit,\n    force: => Boolean,\n    fatal: => Boolean\n) extends PluginComponent { t =>\n\n  import global._\n\n  val runsAfter = List(\"typer\")\n\n  override val runsBefore = List(\"patmat\")\n\n  val phaseName = \"acyclic\"\n\n  private object base extends BasePluginPhase[CompilationUnit, Tree, Symbol] with GraphAnalysis[Tree] {\n    protected val cycleReporter = t.cycleReporter\n    protected lazy val force = t.force\n    protected lazy val fatal = t.fatal\n\n    def treeLine(tree: Tree): Int = tree.pos.line\n    def treeSymbolString(tree: Tree): String = tree.symbol.toString\n\n    def reportError(msg: String): Unit = global.error(msg)\n    def reportWarning(msg: String): Unit = global.warning(msg)\n    def reportInform(msg: String): Unit = global.inform(msg)\n    def reportEcho(msg: String, tree: Tree): Unit = global.reporter.echo(tree.pos, msg)\n\n    def units: Seq[CompilationUnit] = global.currentRun.units.toSeq.sortBy(_.source.content.mkString.hashCode())\n    def unitTree(unit: CompilationUnit): Tree = unit.body\n    def unitPath(unit: CompilationUnit): String = unit.source.path\n    def unitPkgName(unit: CompilationUnit): List[String] =\n      unit.body.collect { case x: PackageDef => x.pid.toString }.flatMap(_.split('.'))\n    def findPkgObjects(tree: Tree): List[Tree] = tree.collect { case x: ModuleDef if x.name.toString == \"package\" => x }\n    def pkgObjectName(pkgObject: Tree): String = pkgObject.symbol.enclosingPackageClass.fullName\n    def hasAcyclicImport(tree: Tree, selector: String): Boolean =\n      tree.collect {\n        case Import(expr, List(sel)) => expr.symbol.toString == \"package acyclic\" && sel.name.toString == selector\n      }.exists(identity)\n\n    def extractDependencies(unit: CompilationUnit): Seq[(Symbol, Tree)] = DependencyExtraction(global)(unit)\n    def symbolPath(sym: Symbol): String = sym.sourceFile.path\n    def isValidSymbol(sym: Symbol): Boolean = sym != NoSymbol && sym.sourceFile != null\n  }\n\n  override def newPhase(prev: Phase): Phase = new Phase(prev) {\n    override def run(): Unit = base.runAllUnits()\n\n    def name: String = \"acyclic\"\n  }\n}\n"
  },
  {
    "path": "acyclic/src-2.11/acyclic/plugin/Compat.scala",
    "content": "package acyclic.plugin\n\nimport acyclic.file\n\nimport scala.collection.{SortedSet, SortedSetLike}\nimport scala.collection.mutable.Builder\nimport scala.collection.generic.{CanBuildFrom, SortedSetFactory}\nimport scala.language.implicitConversions\n\nobject Compat {\n\n  // from https://github.com/scala/scala-collection-compat/blob/746a7de28223812b19d0d9f68d2253e0c5f655ca/compat/src/main/scala-2.11_2.12/scala/collection/compat/CompatImpl.scala#L8-L11\n  private def simpleCBF[A, C](f: => Builder[A, C]): CanBuildFrom[Any, A, C] = new CanBuildFrom[Any, A, C] {\n    def apply(from: Any): Builder[A, C] = apply()\n    def apply(): Builder[A, C] = f\n  }\n\n  // from https://github.com/scala/scala-collection-compat/blob/746a7de28223812b19d0d9f68d2253e0c5f655ca/compat/src/main/scala-2.11_2.12/scala/collection/compat/PackageShared.scala#L46-L49\n  implicit def sortedSetCompanionToCBF[A: Ordering, CC[X] <: SortedSet[X] with SortedSetLike[X, CC[X]]](\n      fact: SortedSetFactory[CC]\n  ): CanBuildFrom[Any, A, CC[A]] =\n    simpleCBF(fact.newBuilder[A])\n\n}\n"
  },
  {
    "path": "acyclic/src-2.12/acyclic/plugin/Compat.scala",
    "content": "package acyclic.plugin\n\nimport acyclic.file\n\nimport scala.collection.{SortedSet, SortedSetLike}\nimport scala.collection.mutable.Builder\nimport scala.collection.generic.{CanBuildFrom, SortedSetFactory}\nimport scala.language.implicitConversions\n\nobject Compat {\n\n  // from https://github.com/scala/scala-collection-compat/blob/746a7de28223812b19d0d9f68d2253e0c5f655ca/compat/src/main/scala-2.11_2.12/scala/collection/compat/CompatImpl.scala#L8-L11\n  private def simpleCBF[A, C](f: => Builder[A, C]): CanBuildFrom[Any, A, C] = new CanBuildFrom[Any, A, C] {\n    def apply(from: Any): Builder[A, C] = apply()\n    def apply(): Builder[A, C] = f\n  }\n\n  // from https://github.com/scala/scala-collection-compat/blob/746a7de28223812b19d0d9f68d2253e0c5f655ca/compat/src/main/scala-2.11_2.12/scala/collection/compat/PackageShared.scala#L46-L49\n  implicit def sortedSetCompanionToCBF[A: Ordering, CC[X] <: SortedSet[X] with SortedSetLike[X, CC[X]]](\n      fact: SortedSetFactory[CC]\n  ): CanBuildFrom[Any, A, CC[A]] =\n    simpleCBF(fact.newBuilder[A])\n\n}\n"
  },
  {
    "path": "acyclic/src-2.13/acyclic/plugin/Compat.scala",
    "content": "package acyclic.plugin\n\nimport acyclic.file\n\nobject Compat\n"
  },
  {
    "path": "acyclic/src-3/acyclic/plugin/Compat.scala",
    "content": "package acyclic.plugin\n\nimport acyclic.file\n\nobject Compat\n"
  },
  {
    "path": "acyclic/src-3/acyclic/plugin/DependencyExtraction.scala",
    "content": "package acyclic.plugin\n\nimport acyclic.file\nimport dotty.tools.dotc.ast.tpd\nimport dotty.tools.dotc.{CompilationUnit, report}\nimport dotty.tools.dotc.core.Contexts.Context\nimport dotty.tools.dotc.core.Flags\nimport dotty.tools.dotc.core.Names.Name\nimport dotty.tools.dotc.core.Symbols.Symbol\nimport dotty.tools.dotc.core.Types.Type\n\nobject DependencyExtraction {\n  def apply(unit: CompilationUnit)(using Context): Seq[(Symbol, tpd.Tree)] = {\n\n    class CollectTypeTraverser[T](pf: PartialFunction[Type, T]) extends tpd.TreeAccumulator[List[T]] {\n      def apply(acc: List[T], tree: tpd.Tree)(using Context) =\n        foldOver(\n          if (pf.isDefinedAt(tree.tpe)) pf(tree.tpe) :: acc else acc,\n          tree\n        )\n    }\n\n    abstract class ExtractDependenciesTraverser extends tpd.TreeTraverser {\n      protected val depBuf = collection.mutable.ArrayBuffer.empty[(Symbol, tpd.Tree)]\n      protected def addDependency(sym: Symbol, tree: tpd.Tree): Unit = depBuf += ((sym, tree))\n      def dependencies: collection.immutable.Set[(Symbol, tpd.Tree)] = {\n        // convert to immutable set and remove NoSymbol if we have one\n        depBuf.toSet\n      }\n\n    }\n\n    class ExtractDependenciesByMemberRefTraverser extends ExtractDependenciesTraverser {\n      override def traverse(tree: tpd.Tree)(using Context): Unit = {\n        tree match {\n          case i @ tpd.Import(expr, selectors) =>\n            selectors.foreach { s =>\n              def lookupImported(name: Name) = expr.symbol.info.member(name).symbol\n\n              if (s.isWildcard) {\n                addDependency(lookupImported(s.name.toTermName), tree)\n                addDependency(lookupImported(s.name.toTypeName), tree)\n              }\n            }\n          case select: tpd.Select =>\n            addDependency(select.symbol, tree)\n          /*\n           * Idents are used in number of situations:\n           *  - to refer to local variable\n           *  - to refer to a top-level package (other packages are nested selections)\n           *  - to refer to a term defined in the same package as an enclosing class;\n           *    this looks fishy, see this thread:\n           *    https://groups.google.com/d/topic/scala-internals/Ms9WUAtokLo/discussion\n           */\n          case ident: tpd.Ident =>\n            addDependency(ident.symbol, tree)\n          case typeTree: tpd.TypeTree =>\n            val typeSymbolCollector = new CollectTypeTraverser({\n              case tpe if tpe != null && tpe.typeSymbol != null && !tpe.typeSymbol.is(Flags.Package) => tpe.typeSymbol\n            })\n            val deps = typeSymbolCollector(Nil, typeTree).toSet\n            deps.foreach(addDependency(_, tree))\n          case t: tpd.Template =>\n            traverse(t.body)\n          case other => ()\n        }\n        foldOver((), tree)\n      }\n    }\n\n    def byMembers(): collection.immutable.Set[(Symbol, tpd.Tree)] = {\n      val traverser = new ExtractDependenciesByMemberRefTraverser\n      if (!unit.isJava)\n        traverser.traverse(unit.tpdTree)\n      traverser.dependencies\n    }\n\n    class ExtractDependenciesByInheritanceTraverser extends ExtractDependenciesTraverser {\n      override def traverse(tree: tpd.Tree)(using Context): Unit = tree match {\n        case t: tpd.Template =>\n          // we are using typeSymbol and not typeSymbolDirect because we want\n          // type aliases to be expanded\n          val parentTypeSymbols = t.parents.map(parent => parent.tpe.typeSymbol).toSet\n          report.debuglog(\"Parent type symbols for \" + tree.sourcePos.show + \": \" + parentTypeSymbols.map(_.fullName))\n          parentTypeSymbols.foreach(addDependency(_, tree))\n          traverse(t.body)\n        case tree => foldOver((), tree)\n      }\n    }\n\n    def byInheritence(): collection.immutable.Set[(Symbol, tpd.Tree)] = {\n      val traverser = new ExtractDependenciesByInheritanceTraverser\n      if (!unit.isJava)\n        traverser.traverse(unit.tpdTree)\n      traverser.dependencies\n    }\n\n    (byMembers() | byInheritence()).toSeq\n  }\n}\n"
  },
  {
    "path": "acyclic/src-3/acyclic/plugin/Plugin.scala",
    "content": "package acyclic.plugin\n\nimport acyclic.file\nimport dotty.tools.dotc.plugins.{PluginPhase, StandardPlugin}\nimport scala.collection.SortedSet\nimport dotty.tools.dotc.core.Contexts.Context\n\nclass RuntimePlugin extends TestPlugin()\nclass TestPlugin(cycleReporter: Seq[(Value, SortedSet[Int])] => Unit = _ => ()) extends StandardPlugin {\n\n  val name = \"acyclic\"\n  val description = \"Allows the developer to prohibit inter-file dependencies\"\n\n  var force = false\n  var fatal = true\n  var alreadyRun = false\n\n  private class Phase() extends PluginPhase {\n    val phaseName = \"acyclic\"\n    override val runsBefore = Set(\"patternMatcher\")\n\n    override def run(using Context): Unit = {\n      if (!alreadyRun) {\n        alreadyRun = true\n        new acyclic.plugin.PluginPhase(cycleReporter, force, fatal).run()\n      }\n    }\n  }\n\n  override def init(options: List[String]): List[PluginPhase] = {\n    if (options.contains(\"force\")) {\n      force = true\n    }\n    if (options.contains(\"warn\")) {\n      fatal = false\n    }\n    List(Phase())\n  }\n}\n"
  },
  {
    "path": "acyclic/src-3/acyclic/plugin/PluginPhase.scala",
    "content": "package acyclic.plugin\n\nimport acyclic.file\nimport scala.collection.SortedSet\nimport dotty.tools.dotc.ast.tpd\nimport dotty.tools.dotc.{CompilationUnit, report}\nimport dotty.tools.dotc.core.Contexts.Context\nimport dotty.tools.dotc.core.Symbols.{NoSymbol, Symbol}\nimport dotty.tools.dotc.util.NoSource\n\n/**\n * - Break dependency graph into strongly connected components\n * - Turn acyclic packages into virtual \"files\" in the dependency graph, as\n *   aggregates of all the files within them\n * - Any strongly connected component which includes an acyclic.file or\n *   acyclic.pkg is a failure\n *   - Pick an arbitrary cycle and report it\n * - Don't report more than one cycle per file/pkg, to avoid excessive spam\n */\nclass PluginPhase(\n    protected val cycleReporter: Seq[(Value, SortedSet[Int])] => Unit,\n    protected val force: Boolean,\n    protected val fatal: Boolean\n)(using ctx: Context) extends BasePluginPhase[CompilationUnit, tpd.Tree, Symbol], GraphAnalysis[tpd.Tree] {\n\n  def treeLine(tree: tpd.Tree): Int = tree.sourcePos.line + 1\n  def treeSymbolString(tree: tpd.Tree): String = tree.symbol.toString\n\n  def reportError(msg: String): Unit = report.error(msg)\n  def reportWarning(msg: String): Unit = report.warning(msg)\n  def reportInform(msg: String): Unit = report.echo(msg)\n  def reportEcho(msg: String, tree: tpd.Tree): Unit = report.echo(msg, tree.srcPos)\n\n  private val pkgNameAccumulator = new tpd.TreeAccumulator[List[String]] {\n    @annotation.tailrec\n    private def definitivePackageDef(pkg: tpd.PackageDef): tpd.PackageDef =\n      pkg.stats.collectFirst { case p: tpd.PackageDef => p } match {\n        case Some(p) => definitivePackageDef(p)\n        case None => pkg\n      }\n\n    def apply(acc: List[String], tree: tpd.Tree)(using Context) = tree match {\n      case p: tpd.PackageDef => definitivePackageDef(p).pid.show :: acc\n      case _ => foldOver(acc, tree)\n    }\n  }\n\n  private val pkgObjectAccumulator = new tpd.TreeAccumulator[List[tpd.Tree]] {\n    def apply(acc: List[tpd.Tree], tree: tpd.Tree)(using Context): List[tpd.Tree] =\n      foldOver(\n        if (tree.symbol.isPackageObject) tree :: acc else acc,\n        tree\n      )\n  }\n\n  private def hasAcyclicImportAccumulator(selector: String) = new tpd.TreeAccumulator[Boolean] {\n    def apply(acc: Boolean, tree: tpd.Tree)(using Context): Boolean = tree match {\n      case tpd.Import(expr, List(sel)) =>\n        acc || (expr.symbol.toString == \"object acyclic\" && sel.name.show == selector)\n      case _ => foldOver(acc, tree)\n    }\n  }\n\n  lazy val units = Option(ctx.run) match {\n    case Some(run) => run.units.toSeq.sortBy(_.source.content.mkString.hashCode())\n    case None => Seq()\n  }\n\n  def unitTree(unit: CompilationUnit): tpd.Tree = unit.tpdTree\n  def unitPath(unit: CompilationUnit): String = unit.source.path\n  def unitPkgName(unit: CompilationUnit): List[String] =\n    pkgNameAccumulator(Nil, unit.tpdTree).reverse.flatMap(_.split('.'))\n  def findPkgObjects(tree: tpd.Tree): List[tpd.Tree] = pkgObjectAccumulator(Nil, tree).reverse\n  def pkgObjectName(pkgObject: tpd.Tree): String = pkgObject.symbol.enclosingPackageClass.fullName.toString\n  def hasAcyclicImport(tree: tpd.Tree, selector: String): Boolean = hasAcyclicImportAccumulator(selector)(false, tree)\n\n  def extractDependencies(unit: CompilationUnit): Seq[(Symbol, tpd.Tree)] = DependencyExtraction(unit)\n  def symbolPath(sym: Symbol): String = sym.source.path\n  def isValidSymbol(sym: Symbol): Boolean = sym != NoSymbol && sym.source != null && sym.source != NoSource\n\n  def run(): Unit = runAllUnits()\n}\n"
  },
  {
    "path": "acyclic/test/resources/fail/cyclicgraph/A.scala",
    "content": "package fail.cyclicgraph\nimport acyclic.file\n\nclass A {\n  val e = new E\n}\n"
  },
  {
    "path": "acyclic/test/resources/fail/cyclicgraph/B.scala",
    "content": "package fail.cyclicgraph\nimport acyclic.file\n\nclass B {\n  val a: A = null\n}\n"
  },
  {
    "path": "acyclic/test/resources/fail/cyclicgraph/C.scala",
    "content": "package fail.cyclicgraph\nimport acyclic.file\n\nobject C extends A {\n  val a: A = null\n}\n"
  },
  {
    "path": "acyclic/test/resources/fail/cyclicgraph/D.scala",
    "content": "package fail.cyclicgraph\nimport acyclic.file\n\nclass D {\n  val b: A = null\n  val c = C\n}\n"
  },
  {
    "path": "acyclic/test/resources/fail/cyclicgraph/E.scala",
    "content": "package fail.cyclicgraph\nimport acyclic.file\n\nclass E {\n  val a: A = null\n  val d = new D\n}\n"
  },
  {
    "path": "acyclic/test/resources/fail/cyclicpackage/a/A1.scala",
    "content": "package fail.cyclicpackage\npackage a\nimport acyclic.file\n\nclass A1 extends b.B1 {}\n"
  },
  {
    "path": "acyclic/test/resources/fail/cyclicpackage/a/A2.scala",
    "content": "package fail.cyclicpackage.a\nclass A2 {}\n"
  },
  {
    "path": "acyclic/test/resources/fail/cyclicpackage/a/package.scala",
    "content": "package fail.cyclicpackage\n\npackage object a {\n  import acyclic.pkg\n}\n"
  },
  {
    "path": "acyclic/test/resources/fail/cyclicpackage/b/B1.scala",
    "content": "package fail.cyclicpackage.b\nimport acyclic.file\nclass B1\n"
  },
  {
    "path": "acyclic/test/resources/fail/cyclicpackage/b/B2.scala",
    "content": "package fail.cyclicpackage\npackage b\nimport acyclic.file\n\nclass B2 extends a.A2\n"
  },
  {
    "path": "acyclic/test/resources/fail/cyclicpackage/b/package.scala",
    "content": "package fail.cyclicpackage\n\npackage object b {\n  import acyclic.pkg\n}\n"
  },
  {
    "path": "acyclic/test/resources/fail/halfpackagecycle/A.scala",
    "content": "package fail.halfpackagecycle\n\nclass A {\n  val thing = c.C1\n}\n"
  },
  {
    "path": "acyclic/test/resources/fail/halfpackagecycle/B.scala",
    "content": "package fail.halfpackagecycle\n\nclass B extends A\n"
  },
  {
    "path": "acyclic/test/resources/fail/halfpackagecycle/c/C1.scala",
    "content": "package fail.halfpackagecycle.c\n\nobject C1\n"
  },
  {
    "path": "acyclic/test/resources/fail/halfpackagecycle/c/C2.scala",
    "content": "package fail.halfpackagecycle\npackage c\n\nclass C2 {\n  lazy val b = new B\n}\n"
  },
  {
    "path": "acyclic/test/resources/fail/halfpackagecycle/c/package.scala",
    "content": "package fail.halfpackagecycle\n\npackage object c {\n  import acyclic.pkg\n}\n"
  },
  {
    "path": "acyclic/test/resources/fail/indirect/A.scala",
    "content": "package fail.indirect\nimport acyclic.file\n\nobject A\nclass A {\n  val b: B = null\n}\n"
  },
  {
    "path": "acyclic/test/resources/fail/indirect/B.scala",
    "content": "package fail.indirect\n\nclass B extends C\n"
  },
  {
    "path": "acyclic/test/resources/fail/indirect/C.scala",
    "content": "package fail.indirect\n\nclass C {\n  val a = A\n}\n"
  },
  {
    "path": "acyclic/test/resources/fail/simple/A.scala",
    "content": "package fail.simple\nimport acyclic.file\n\nclass A {\n  val b: B = null\n}\n"
  },
  {
    "path": "acyclic/test/resources/fail/simple/B.scala",
    "content": "package fail.simple\n\nclass B {\n  val a1: A = new A\n  val a2: A = new A\n}\n"
  },
  {
    "path": "acyclic/test/resources/force/simple/A.scala",
    "content": "package force.simple\n\nclass A {\n  val b: B = null\n}\n"
  },
  {
    "path": "acyclic/test/resources/force/simple/B.scala",
    "content": "package force.simple\n\nclass B {\n  val a1: A = new A\n  val a2: A = new A\n}\n"
  },
  {
    "path": "acyclic/test/resources/force/skip/A.scala",
    "content": "package force.skip\nimport acyclic.skipped\n\nclass A {\n  val b: B = null\n}\n"
  },
  {
    "path": "acyclic/test/resources/force/skip/B.scala",
    "content": "package force.skip\nimport acyclic.skipped\nclass B {\n  val a1: A = new A\n  val a2: A = new A\n}\n"
  },
  {
    "path": "acyclic/test/resources/success/cyclicunmarked/A.scala",
    "content": "package success.cyclicunmarked\n\nclass A {\n  val b: B = null\n}\n"
  },
  {
    "path": "acyclic/test/resources/success/cyclicunmarked/B.scala",
    "content": "package success.cyclicunmarked\n\nclass B {\n  val a1: A = new A\n  val a2: A = new A\n}\n"
  },
  {
    "path": "acyclic/test/resources/success/dag/A.scala",
    "content": "package success.dag\n\nclass A {}\n"
  },
  {
    "path": "acyclic/test/resources/success/dag/B.scala",
    "content": "package success.dag\n\nclass B {\n  val a: A = null\n}\n"
  },
  {
    "path": "acyclic/test/resources/success/dag/C.scala",
    "content": "package success.dag\n\nobject C extends A\n"
  },
  {
    "path": "acyclic/test/resources/success/dag/D.scala",
    "content": "package success.dag\n\nclass D {\n  val b: A = null\n  val c = C\n}\n"
  },
  {
    "path": "acyclic/test/resources/success/dag/E.scala",
    "content": "package success.dag\n\nclass E {\n  val a: A = null\n  val d = new D\n}\n"
  },
  {
    "path": "acyclic/test/resources/success/java/SomeJava.java",
    "content": "\npublic interface SomeJava {\n\n}"
  },
  {
    "path": "acyclic/test/resources/success/pkg/halfacyclic/a/A1.scala",
    "content": "package success.halfacyclicpackage\npackage a\n\nclass A1 extends b.B1 {}\n"
  },
  {
    "path": "acyclic/test/resources/success/pkg/halfacyclic/a/A2.scala",
    "content": "package success.halfacyclicpackage.a\n\nclass A2 {}\n"
  },
  {
    "path": "acyclic/test/resources/success/pkg/halfacyclic/a/package.scala",
    "content": "package success.halfacyclicpackage\n\npackage object a {\n  import acyclic.pkg\n}\n"
  },
  {
    "path": "acyclic/test/resources/success/pkg/halfacyclic/b/B1.scala",
    "content": "package success.halfacyclicpackage.b\n\nclass B1 {}\n"
  },
  {
    "path": "acyclic/test/resources/success/pkg/halfacyclic/b/B2.scala",
    "content": "package success.halfacyclicpackage\npackage b\n\nclass B2 extends a.A2 {}\n"
  },
  {
    "path": "acyclic/test/resources/success/pkg/innercycle/a/A1.scala",
    "content": "package success.pkg.innercycle.a\n\nclass A1 {\n  val x: A2 = null\n  def y = p\n}\n"
  },
  {
    "path": "acyclic/test/resources/success/pkg/innercycle/a/A2.scala",
    "content": "package success.pkg.innercycle.a\n\nclass A2 {\n  val x: A1 = null\n  def z = p\n}\n"
  },
  {
    "path": "acyclic/test/resources/success/pkg/innercycle/a/package.scala",
    "content": "package success.pkg.innercycle\n\npackage object a {\n  val p: A1 with A2 = null\n  import acyclic.pkg\n}\n"
  },
  {
    "path": "acyclic/test/resources/success/pkg/mutualcyclic/a/A1.scala",
    "content": "package success.cyclicpackage\npackage a\n\nclass A1 extends b.B1 {}\n"
  },
  {
    "path": "acyclic/test/resources/success/pkg/mutualcyclic/a/A2.scala",
    "content": "package success.cyclicpackage.a\n\nclass A2 {}\n"
  },
  {
    "path": "acyclic/test/resources/success/pkg/mutualcyclic/b/B1.scala",
    "content": "package success.cyclicpackage.b\n\nclass B1 {}\n"
  },
  {
    "path": "acyclic/test/resources/success/pkg/mutualcyclic/b/B2.scala",
    "content": "package success.cyclicpackage\npackage b\n\nclass B2 extends a.A2 {}\n"
  },
  {
    "path": "acyclic/test/resources/success/pkg/single/pkg/package.scala",
    "content": "package success.singlepackage\n\npackage object pkg {\n  import acyclic.pkg\n}\n"
  },
  {
    "path": "acyclic/test/resources/success/simple/A.scala",
    "content": "package success.simple\n\nclass A {}\n"
  },
  {
    "path": "acyclic/test/resources/success/simple/B.scala",
    "content": "package success.simple\n\nclass B {\n  val a: A = null\n}\n"
  },
  {
    "path": "acyclic/test/src/acyclic/BaseCycleTests.scala",
    "content": "package acyclic\n\nimport utest._\nimport acyclic.plugin.Value.{Pkg, File}\nimport scala.collection.SortedSet\nimport acyclic.file\n\nclass BaseCycleTests(utils: BaseTestUtils) extends TestSuite {\n  import utils.{make, makeFail, srcDirName}\n\n  def tests = Tests {\n    test(\"fail\") - {\n      test(\"simple\") - makeFail(\"fail/simple\")(Seq(\n        File(\"B.scala\") -> SortedSet(4, 5),\n        File(\"A.scala\") -> SortedSet(5)\n      ))\n\n      test(\"indirect\") - makeFail(\"fail/indirect\")(Seq(\n        File(\"A.scala\") -> SortedSet(6),\n        File(\"B.scala\") -> SortedSet(3),\n        File(\"C.scala\") -> SortedSet(4)\n      ))\n      test(\"cyclicgraph\") - makeFail(\"fail/cyclicgraph\")(\n        Seq(\n          File(\"A.scala\") -> SortedSet(5),\n          File(\"E.scala\") -> SortedSet(6),\n          File(\"D.scala\") -> SortedSet(6),\n          File(\"C.scala\") -> SortedSet(4, 5)\n        )\n      )\n      test(\"cyclicpackage\") - makeFail(\"fail/cyclicpackage\")(\n        Seq(\n          Pkg(\"fail.cyclicpackage.b\") -> SortedSet(5),\n          Pkg(\"fail.cyclicpackage.a\") -> SortedSet(5)\n        )\n      )\n      test(\"halfpackagecycle\") - makeFail(\"fail/halfpackagecycle\")(Seq(\n        File(\"B.scala\") -> SortedSet(3),\n        File(\"A.scala\") -> SortedSet(4),\n        Pkg(\"fail.halfpackagecycle.c\") -> SortedSet(5)\n      ))\n    }\n    test(\"success\") - {\n      test(\"simple\") - make(\"success/simple\")\n      test(\"ignorejava\") - make(\"success/java\")\n      test(\"cyclicunmarked\") - make(\"success/cyclicunmarked\")\n      test(\"dag\") - make(\"success/dag\")\n      test(\"pkg\") {\n        test(\"single\") - make(\"success/pkg/single\")\n        test(\"mutualcyclic\") - make(\"success/pkg/mutualcyclic\")\n        test(\"halfacyclic\") - make(\"success/pkg/halfacyclic\")\n        test(\"innercycle\") - make(\"success/pkg/innercycle\")\n      }\n    }\n    test(\"self\") - make(s\"../../$srcDirName\", extraIncludes = Nil)\n    test(\"force\") - {\n      test(\"warn\") - {\n        test(\"fail\") - {\n          assert(make(\"force/simple\", force = true, warn = true).exists {\n            case (\"Unwanted cyclic dependency\", \"WARNING\") => true\n            case _ => false\n          })\n        }\n      }\n      test(\"fail\") - makeFail(\"force/simple\", force = true)(Seq(\n        File(\"B.scala\") -> SortedSet(4, 5),\n        File(\"A.scala\") -> SortedSet(4)\n      ))\n      test(\"pass\") - make(\"force/simple\")\n      test(\"skip\") - make(\"force/skip\", force = true)\n    }\n  }\n}\n"
  },
  {
    "path": "acyclic/test/src/acyclic/BaseTestUtils.scala",
    "content": "package acyclic\n\nimport acyclic.plugin.Value\n\nimport java.util.jar.JarFile\nimport scala.collection.SortedSet\n\nabstract class BaseTestUtils {\n  val srcDirName: String\n\n  val workspaceRoot = sys.env(\"MILL_WORKSPACE_ROOT\")\n  val testResources = sys.env(\"TEST_ACYCLIC_TEST_RESOURCES\")\n\n  /**\n   * Attempts to compile a resource folder as a compilation run, in order\n   * to test whether it succeeds or fails correctly.\n   */\n  def make(\n      path: String,\n      extraIncludes: Seq[String] = Seq(\"acyclic/src/acyclic/package.scala\"),\n      force: Boolean = false,\n      warn: Boolean = false,\n      collectInfo: Boolean = true\n  ): Seq[(String, String)]\n\n  def makeFail(path: String, force: Boolean = false)(expected: Seq[(Value, SortedSet[Int])]*): Unit\n\n  case class CompilationException(cycles: Seq[Seq[(Value, SortedSet[Int])]]) extends Exception\n\n  final def getFilePaths(src: String): List[String] = {\n    val f = new java.io.File(src)\n    if (f.isDirectory) f.list.toList.flatMap(x => getFilePaths(src + \"/\" + x))\n    else List(src)\n  }\n\n  def getJavaClasspathEntries(): Seq[String] = {\n    System.getProperty(\"java.class.path\")\n      .split(java.io.File.pathSeparator)\n      .toIndexedSeq\n      .flatMap { f =>\n        // If an (empty) classpath pathing jar is used, we extract the `Class-Path` manifest entry\n        // and those entries to the classpath\n        val extra =\n          if (!f.toLowerCase().endsWith(\".jar\")) Seq()\n          else\n            for {\n              manifest <- Option(new JarFile(f).getManifest()).toSeq\n              mainAttr <- Option(manifest.getMainAttributes()).toSeq\n              cp <- Option(mainAttr.getValue(\"Class-Path\")).toSeq\n              entry <- cp.split(\" \")\n              if entry.nonEmpty\n            } yield entry match {\n              case url if url.startsWith(\"file:///\") =>\n                url.substring(\"file://\".length)\n              case url if url.startsWith(\"file:/\") =>\n                url.substring(\"file:\".length)\n              case s => s\n            }\n        Seq(f) ++ extra\n      }\n  }\n}\n"
  },
  {
    "path": "acyclic/test/src-2/acyclic/CycleTests.scala",
    "content": "package acyclic\n\nobject CycleTests extends BaseCycleTests(TestUtils)\n"
  },
  {
    "path": "acyclic/test/src-2/acyclic/TestUtils.scala",
    "content": "package acyclic\n\nimport tools.nsc.{Global, Settings}\nimport tools.nsc.reporters.{ConsoleReporter, StoreReporter}\nimport tools.nsc.plugins.Plugin\nimport java.net.URLClassLoader\nimport scala.tools.nsc.util.ClassPath\nimport utest._\nimport asserts._\n\nimport scala.reflect.io.VirtualDirectory\nimport acyclic.plugin.Value\n\nimport java.io.OutputStream\nimport javax.print.attribute.standard.Severity\nimport scala.collection.SortedSet\n\nobject TestUtils extends BaseTestUtils {\n  val srcDirName: String = \"src-2\"\n\n  /**\n   * Attempts to compile a resource folder as a compilation run, in order\n   * to test whether it succeeds or fails correctly.\n   */\n  def make(\n      path: String,\n      extraIncludes: Seq[String] =\n        Seq(workspaceRoot + \"/acyclic/src/acyclic/package.scala\"),\n      force: Boolean = false,\n      warn: Boolean = false,\n      collectInfo: Boolean = true\n  ): Seq[(String, String)] = {\n    val src = testResources + \"/\" + path\n    val sources = getFilePaths(src) ++ extraIncludes\n\n    val vd = new VirtualDirectory(\"(memory)\", None)\n    lazy val settings = new Settings\n    val entries = getJavaClasspathEntries()\n    settings.outputDirs.setSingleOutput(vd)\n\n    // annoyingly, the Scala library is not in our classpath, so we have to add it manually\n    val sclpath = entries.map(\n      _.replaceAll(\"scala-compiler.jar\", \"scala-library.jar\")\n    )\n\n    settings.classpath.value = ClassPath.join(entries ++ sclpath: _*)\n\n    val opts = List(\n      if (force) Seq(\"force\") else Seq(),\n      if (warn) Seq(\"warn\") else Seq()\n    ).flatten\n    if (opts.nonEmpty) {\n      val options = opts.map(\"acyclic:\" + _)\n      println(\"options: \" + options)\n      settings.pluginOptions.value = options\n    }\n\n    var cycles: Option[Seq[Seq[(acyclic.plugin.Value, SortedSet[Int])]]] = None\n    val storeReporter = if (collectInfo) Some(new StoreReporter()) else None\n\n    lazy val compiler = new Global(settings, storeReporter.getOrElse(new ConsoleReporter(settings))) {\n      override protected def loadRoughPluginsList(): List[Plugin] = {\n        List(new plugin.TestPlugin(\n          this,\n          foundCycles =>\n            cycles = cycles match {\n              case None => Some(Seq(foundCycles))\n              case Some(oldCycles) => Some(oldCycles :+ foundCycles)\n            }\n        ))\n      }\n    }\n    val run = new compiler.Run()\n    run.compile(sources)\n\n    if (vd.toList.isEmpty) throw CompilationException(cycles.get)\n\n    storeReporter.map(_.infos.toSeq.map(i => (i.msg, i.severity.toString))).getOrElse(Seq.empty)\n  }\n\n  def makeFail(path: String, force: Boolean = false)(expected: Seq[(Value, SortedSet[Int])]*) = {\n    def canonicalize(cycle: Seq[(Value, SortedSet[Int])]): Seq[(Value, SortedSet[Int])] = {\n      val startIndex = cycle.indexOf(cycle.minBy(_._1.toString))\n      cycle.toList.drop(startIndex) ++ cycle.toList.take(startIndex)\n    }\n\n    val ex = intercept[CompilationException] { make(path, force = force, collectInfo = false) }\n    val cycles = ex.cycles\n      .map(canonicalize)\n      .map(\n        _.map {\n          case (Value.File(p, pkg), v) => (Value.File(p, Nil), v)\n          case x => x\n        }\n      )\n      .toSet\n\n    def expand(v: Value) = v match {\n      case Value.File(filePath, pkg) => Value.File(testResources + \"/\" + path + \"/\" + filePath, Nil)\n      case v => v\n    }\n\n    val fullExpected = expected.map(_.map(x => x.copy(_1 = expand(x._1))))\n      .map(canonicalize)\n      .toSet\n\n    assert(fullExpected.forall(cycles.contains))\n  }\n\n}\n"
  },
  {
    "path": "acyclic/test/src-3/acyclic/CycleTests.scala",
    "content": "package acyclic\n\nobject CycleTests extends BaseCycleTests(TestUtils)\n"
  },
  {
    "path": "acyclic/test/src-3/acyclic/TestUtils.scala",
    "content": "package acyclic\n\nimport acyclic.plugin.Value\nimport java.io.OutputStream\nimport javax.print.attribute.standard.Severity\nimport scala.collection.SortedSet\nimport dotty.tools.io.{ClassPath, Path, PlainFile, VirtualDirectory}\nimport dotty.tools.dotc.Compiler\nimport dotty.tools.dotc.config.ScalaSettings\nimport dotty.tools.dotc.core.Contexts.{Context, ContextBase, FreshContext, NoContext}\nimport dotty.tools.dotc.interfaces.Diagnostic.{ERROR, INFO, WARNING}\nimport dotty.tools.dotc.plugins.Plugin\nimport dotty.tools.dotc.reporting.{ConsoleReporter, StoreReporter}\nimport java.net.URLClassLoader\nimport java.nio.file.Paths\nimport utest._\nimport utest.asserts._\n\nobject TestUtils extends BaseTestUtils {\n  val srcDirName: String = \"src-3\"\n\n  /**\n   * Attempts to compile a resource folder as a compilation run, in order\n   * to test whether it succeeds or fails correctly.\n   */\n  def make(\n      path: String,\n      extraIncludes: Seq[String] =\n        Seq(workspaceRoot + \"/acyclic/src/acyclic/package.scala\"),\n      force: Boolean = false,\n      warn: Boolean = false,\n      collectInfo: Boolean = true\n  ): Seq[(String, String)] = {\n    val src = testResources + \"/\" + path\n    val sources = (getFilePaths(src) ++ extraIncludes).map(f => PlainFile(Path(Paths.get(f))))\n    val vd = new VirtualDirectory(\"(memory)\", None)\n    val entries = getJavaClasspathEntries()\n\n    val scalaSettings = new ScalaSettings {}\n    val settingsState1 = scalaSettings.outputDir.updateIn(scalaSettings.defaultState, vd)\n    val settingsState2 = scalaSettings.classpath.updateIn(settingsState1, ClassPath.join(entries*))\n\n    val opts = List(\n      if (force) Seq(\"force\") else Seq(),\n      if (warn) Seq(\"warn\") else Seq()\n    ).flatten\n\n    val settingsState3 = if (opts.nonEmpty) {\n      val options = opts.map(\"acyclic:\" + _)\n      println(\"options: \" + options)\n      scalaSettings.pluginOptions.updateIn(settingsState2, options)\n    } else {\n      settingsState2\n    }\n\n    var cycles: Option[Seq[Seq[(Value, SortedSet[Int])]]] = None\n    val storeReporter = if (collectInfo) Some(new StoreReporter()) else None\n\n    val ctxBase = new ContextBase {\n      override val initialCtx: Context = FreshContext.initial(NoContext.base, settings)\n\n      override protected def loadRoughPluginsList(using Context): List[Plugin] =\n        List(new plugin.TestPlugin(foundCycles =>\n          cycles = cycles match {\n            case None => Some(Seq(foundCycles))\n            case Some(oldCycles) => Some(oldCycles :+ foundCycles)\n          }\n        ))\n    }\n\n    given ctx: Context = FreshContext.initial(\n      ctxBase,\n      new ScalaSettings {\n        override val defaultState = settingsState3\n      }\n    )\n      .asInstanceOf[FreshContext]\n      .setReporter(storeReporter.getOrElse(ConsoleReporter()))\n\n    ctx.initialize()\n\n    val compiler = new Compiler()\n    val run = compiler.newRun\n\n    run.compile(sources)\n\n    if (vd.toList.isEmpty) throw CompilationException(cycles.get)\n\n    storeReporter.map(_.pendingMessages.toSeq.map(i =>\n      (\n        i.msg.message,\n        i.level match {\n          case ERROR => \"ERROR\"\n          case INFO => \"INFO\"\n          case WARNING => \"WARNING\"\n        }\n      )\n    )).getOrElse(Seq.empty)\n  }\n\n  def makeFail(path: String, force: Boolean = false)(expected: Seq[(Value, SortedSet[Int])]*) = {\n    def canonicalize(cycle: Seq[(Value, SortedSet[Int])]): Seq[(Value, SortedSet[Int])] = {\n      val startIndex = cycle.indexOf(cycle.minBy(_._1.toString))\n      cycle.toList.drop(startIndex) ++ cycle.toList.take(startIndex)\n    }\n\n    val ex = intercept[CompilationException] { make(path, force = force, collectInfo = false) }\n    val cycles = ex.cycles\n      .map(canonicalize)\n      .map(\n        _.map {\n          case (Value.File(p, pkg), v) => (Value.File(p, Nil), v)\n          case x => x\n        }\n      )\n      .toSet\n\n    def expand(v: Value) = v match {\n      case Value.File(filePath, pkg) => Value.File(testResources + \"/\" + path + \"/\" + filePath, Nil)\n      case v => v\n    }\n\n    val fullExpected = expected.map(_.map(x => x.copy(_1 = expand(x._1))))\n      .map(canonicalize)\n      .toSet\n\n    assert(fullExpected.forall(cycles.contains))\n  }\n\n}\n"
  },
  {
    "path": "build.mill",
    "content": "package build\n\nimport mill.*\nimport mill.api.BuildCtx\nimport mill.scalalib.*\nimport publish.*\nimport mill.util.{Version, VcsVersion}\n\nobject Deps {\n  val scala211: Seq[String] = Seq(\"2.11.12\")\n  val scala212: Seq[String] = 9.to(21).map(\"2.12.\" + _)\n  val scala213: Seq[String] = 3.to(18).map(\"2.13.\" + _)\n  val scala33: Seq[String] = 0.to(7).map(\"3.3.\" + _)\n  val scala34: Seq[String] = 0.to(3).map(\"3.4.\" + _)\n  val scala35: Seq[String] = 0.to(2).map(\"3.5.\" + _)\n  val scala36: Seq[String] = 0.to(4).map(\"3.6.\" + _)\n  val scala37: Seq[String] = 0.to(4).map(\"3.7.\" + _)\n  val scala38: Seq[String] = 0.to(3).map(\"3.8.\" + _)\n\n  val unreleased: Seq[String] = scala33 ++ scala34 ++ scala35 ++ scala36 ++ scala37 ++ scala38\n\n  def scalaCompiler(scalaVersion: String): Dep =\n    if scalaVersion.startsWith(\"3.\")\n    then mvn\"org.scala-lang::scala3-compiler:$scalaVersion\"\n    else mvn\"org.scala-lang:scala-compiler:$scalaVersion\"\n\n  val utest = mvn\"com.lihaoyi::utest:0.8.2\"\n}\n\nval crosses: Seq[String] = sys.env.get(\"SCALA_VERSION\")\n  .map(_.trim)\n  .filter(_.nonEmpty)\n  .map(Seq(_))\n  .getOrElse(\n    Deps.scala211 ++\n      Deps.scala212 ++\n      Deps.scala213 ++\n      Deps.scala33 ++\n      Deps.scala34 ++\n      Deps.scala35 ++\n      Deps.scala36 ++\n      Deps.scala37 ++\n      Deps.scala38\n  )\n\nobject acyclic extends Cross[AcyclicModule](crosses)\ntrait AcyclicModule extends CrossScalaModule with SonatypeCentralPublishModule {\n  override def crossFullScalaVersion = true\n  override def artifactName = \"acyclic\"\n  override def publishVersion: T[String] = VcsVersion.vcsState().format()\n\n  override def pomSettings: T[PomSettings] = PomSettings(\n    description = artifactName(),\n    organization = \"com.lihaoyi\",\n    url = \"https://github.com/com-lihaoyi/acyclic\",\n    licenses = Seq(License.MIT),\n    versionControl = VersionControl.github(owner = \"com-lihaoyi\", repo = \"acyclic\"),\n    developers = Seq(\n      Developer(\"lihaoyi\", \"Li Haoyi\", \"https://github.com/lihaoyi\")\n    )\n  )\n  override def compileMvnDeps: T[Seq[Dep]] =\n    Seq(Deps.scalaCompiler(crossScalaVersion))\n\n  override def javacOptions: T[Seq[String]] = Seq(\n    \"-source\",\n    \"8\",\n    \"-target\",\n    \"8\",\n    \"-encoding\",\n    \"UTF-8\"\n  )\n\n  override def scalacOptions: T[Seq[String]] = {\n    val sv = Version.parse(crossScalaVersion)\n    implicit val ord = Version.IgnoreQualifierOrdering\n\n    if (sv.major == 2) Seq(\"-target:jvm-1.8\")\n    else if(!sv.isAtLeast(Version.parse(\"2.8\"))) Seq(\"-java-output-version\", \"8\")\n    else Seq(\"-java-output-version\", \"17\")\n}\n\n  object test extends ScalaTests with TestModule.Utest {\n    private def customSources: T[Seq[PathRef]] = Task.Sources(\"resources\")\n    override def sources: T[Seq[PathRef]] = Task { super.sources() ++ customSources() }\n    override def mvnDeps: T[Seq[Dep]] = Seq(\n      Deps.utest,\n      Deps.scalaCompiler(crossScalaVersion)\n    )\n    override def scalacPluginMvnDeps: T[Seq[Dep]] = Seq.empty[Dep]\n    override def forkEnv: T[Map[String, String]] = super.forkEnv() ++ Map(\n      \"MILL_WORKSPACE_ROOT\" -> BuildCtx.workspaceRoot.toString,\n      \"TEST_ACYCLIC_TEST_RESOURCES\" -> (moduleDir / \"resources\").toString\n    )\n  }\n}\n"
  },
  {
    "path": "mill",
    "content": "#!/usr/bin/env sh\n\n# This is a wrapper script, that automatically selects or downloads Mill from Maven Central or GitHub release pages.\n#\n# This script determines the Mill version to use by trying these sources\n#   - env-variable `MILL_VERSION`\n#   - local file `.mill-version`\n#   - local file `.config/mill-version`\n#   - `mill-version` from YAML frontmatter of current buildfile\n#   - if accessible, find the latest stable version available on Maven Central (https://repo1.maven.org/maven2)\n#   - env-variable `DEFAULT_MILL_VERSION`\n#\n# If a version has the suffix '-native' a native binary will be used.\n# If a version has the suffix '-jvm' an executable jar file will be used, requiring an already installed Java runtime.\n# If no such suffix is found, the script will pick a default based on version and platform.\n#\n# Once a version was determined, it tries to use either\n#    - a system-installed mill, if found and it's version matches\n#    - an already downloaded version under ~/.cache/mill/download\n#\n# If no working mill version was found on the system,\n# this script downloads a binary file from Maven Central or Github Pages (this is version dependent)\n# into a cache location (~/.cache/mill/download).\n#\n# Mill Project URL: https://github.com/com-lihaoyi/mill\n# Script Version: 1.0.5\n#\n# If you want to improve this script, please also contribute your changes back!\n# This script was generated from: dist/scripts/src/mill.sh\n#\n# Licensed under the Apache License, Version 2.0\n\nset -e\n\nif [ -z \"${DEFAULT_MILL_VERSION}\" ] ; then\n  DEFAULT_MILL_VERSION=\"0.12.16\"\nfi\n\n\nif [ -z \"${GITHUB_RELEASE_CDN}\" ] ; then\n  GITHUB_RELEASE_CDN=\"\"\nfi\n\n\nMILL_REPO_URL=\"https://github.com/com-lihaoyi/mill\"\n\nif [ -z \"${CURL_CMD}\" ] ; then\n  CURL_CMD=curl\nfi\n\n# Explicit commandline argument takes precedence over all other methods\nif [ \"$1\" = \"--mill-version\" ] ; then\n    echo \"The --mill-version option is no longer supported.\" 1>&2\nfi\n\nMILL_BUILD_SCRIPT=\"\"\n\nif [ -f \"build.mill\" ] ; then\n  MILL_BUILD_SCRIPT=\"build.mill\"\nelif [ -f \"build.mill.scala\" ] ; then\n  MILL_BUILD_SCRIPT=\"build.mill.scala\"\nelif [ -f \"build.sc\" ] ; then\n  MILL_BUILD_SCRIPT=\"build.sc\"\nfi\n\n# Please note, that if a MILL_VERSION is already set in the environment,\n# We reuse it's value and skip searching for a value.\n\n# If not already set, read .mill-version file\nif [ -z \"${MILL_VERSION}\" ] ; then\n  if [ -f \".mill-version\" ] ; then\n    MILL_VERSION=\"$(tr '\\r' '\\n' < .mill-version | head -n 1 2> /dev/null)\"\n  elif [ -f \".config/mill-version\" ] ; then\n    MILL_VERSION=\"$(tr '\\r' '\\n' < .config/mill-version | head -n 1 2> /dev/null)\"\n  elif [ -n \"${MILL_BUILD_SCRIPT}\" ] ; then\n    # `s/.*://`:\n    #   This is a greedy match that removes everything from the beginning of the line up to (and including) the last\n    #   colon (:). This effectively isolates the value part of the declaration.\n    #\n    #  `s/#.*//`:\n    #    This removes any comments at the end of the line.\n    #\n    #  `s/['\\\"]//g`:\n    #    This removes all single and double quotes from the string, wherever they appear (g is for \"global\").\n    #\n    #  `s/^[[:space:]]*//; s/[[:space:]]*$//`:\n    #    These two expressions trim any leading or trailing whitespace ([[:space:]] matches spaces and tabs).\n    MILL_VERSION=\"$(grep -E \"//\\|.*mill-version\" \"${MILL_BUILD_SCRIPT}\" | sed -E \"s/.*://; s/#.*//; s/['\\\"]//g; s/^[[:space:]]*//; s/[[:space:]]*$//\")\"\n  fi\nfi\n\nMILL_USER_CACHE_DIR=\"${XDG_CACHE_HOME:-${HOME}/.cache}/mill\"\n\nif [ -z \"${MILL_DOWNLOAD_PATH}\" ] ; then\n  MILL_DOWNLOAD_PATH=\"${MILL_USER_CACHE_DIR}/download\"\nfi\n\n# If not already set, try to fetch newest from Github\nif [ -z \"${MILL_VERSION}\" ] ; then\n  # TODO: try to load latest version from release page\n  echo \"No mill version specified.\" 1>&2\n  echo \"You should provide a version via a '//| mill-version: ' comment or a '.mill-version' file.\" 1>&2\n\n  mkdir -p \"${MILL_DOWNLOAD_PATH}\"\n  LANG=C touch -d '1 hour ago' \"${MILL_DOWNLOAD_PATH}/.expire_latest\" 2>/dev/null || (\n    # we might be on OSX or BSD which don't have -d option for touch\n    # but probably a -A [-][[hh]mm]SS\n    touch \"${MILL_DOWNLOAD_PATH}/.expire_latest\"; touch -A -010000 \"${MILL_DOWNLOAD_PATH}/.expire_latest\"\n  ) || (\n    # in case we still failed, we retry the first touch command with the intention\n    # to show the (previously suppressed) error message\n    LANG=C touch -d '1 hour ago' \"${MILL_DOWNLOAD_PATH}/.expire_latest\"\n  )\n\n  # POSIX shell variant of bash's -nt operator, see https://unix.stackexchange.com/a/449744/6993\n  # if [ \"${MILL_DOWNLOAD_PATH}/.latest\" -nt \"${MILL_DOWNLOAD_PATH}/.expire_latest\" ] ; then\n  if [ -n \"$(find -L \"${MILL_DOWNLOAD_PATH}/.latest\" -prune -newer \"${MILL_DOWNLOAD_PATH}/.expire_latest\")\" ]; then\n    # we know a current latest version\n    MILL_VERSION=$(head -n 1 \"${MILL_DOWNLOAD_PATH}\"/.latest 2> /dev/null)\n  fi\n\n  if [ -z \"${MILL_VERSION}\" ] ; then\n    # we don't know a current latest version\n    echo \"Retrieving latest mill version ...\" 1>&2\n    LANG=C ${CURL_CMD} -s -i -f -I ${MILL_REPO_URL}/releases/latest 2> /dev/null  | grep --ignore-case Location: | sed s'/^.*tag\\///' | tr -d '\\r\\n' > \"${MILL_DOWNLOAD_PATH}/.latest\"\n    MILL_VERSION=$(head -n 1 \"${MILL_DOWNLOAD_PATH}\"/.latest 2> /dev/null)\n  fi\n\n  if [ -z \"${MILL_VERSION}\" ] ; then\n    # Last resort\n    MILL_VERSION=\"${DEFAULT_MILL_VERSION}\"\n    echo \"Falling back to hardcoded mill version ${MILL_VERSION}\" 1>&2\n  else\n    echo \"Using mill version ${MILL_VERSION}\" 1>&2\n  fi\nfi\n\nMILL_NATIVE_SUFFIX=\"-native\"\nMILL_JVM_SUFFIX=\"-jvm\"\nFULL_MILL_VERSION=$MILL_VERSION\nARTIFACT_SUFFIX=\"\"\nset_artifact_suffix(){\n  if [ \"$(expr substr $(uname -s) 1 5 2>/dev/null)\" = \"Linux\" ]; then\n    if [ \"$(uname -m)\" = \"aarch64\" ]; then\n      ARTIFACT_SUFFIX=\"-native-linux-aarch64\"\n    else\n      ARTIFACT_SUFFIX=\"-native-linux-amd64\"\n    fi\n  elif [ \"$(uname)\" = \"Darwin\" ]; then\n    if [ \"$(uname -m)\" = \"arm64\" ]; then\n      ARTIFACT_SUFFIX=\"-native-mac-aarch64\"\n    else\n      ARTIFACT_SUFFIX=\"-native-mac-amd64\"\n    fi\n  else\n     echo \"This native mill launcher supports only Linux and macOS.\" 1>&2\n     exit 1\n  fi\n}\n\ncase \"$MILL_VERSION\" in\n    *\"$MILL_NATIVE_SUFFIX\")\n  MILL_VERSION=${MILL_VERSION%\"$MILL_NATIVE_SUFFIX\"}\n  set_artifact_suffix\n  ;;\n\n    *\"$MILL_JVM_SUFFIX\")\n    MILL_VERSION=${MILL_VERSION%\"$MILL_JVM_SUFFIX\"}\n  ;;\n\n    *)\n  case \"$MILL_VERSION\" in\n    0.1.*) ;;\n    0.2.*) ;;\n    0.3.*) ;;\n    0.4.*) ;;\n    0.5.*) ;;\n    0.6.*) ;;\n    0.7.*) ;;\n    0.8.*) ;;\n    0.9.*) ;;\n    0.10.*) ;;\n    0.11.*) ;;\n    0.12.*) ;;\n    *)\n      set_artifact_suffix\n  esac\n  ;;\nesac\n\nMILL=\"${MILL_DOWNLOAD_PATH}/$MILL_VERSION$ARTIFACT_SUFFIX\"\n\ntry_to_use_system_mill() {\n  if [ \"$(uname)\" != \"Linux\" ]; then\n    return 0\n  fi\n\n  MILL_IN_PATH=\"$(command -v mill || true)\"\n\n  if [ -z \"${MILL_IN_PATH}\" ]; then\n    return 0\n  fi\n\n  SYSTEM_MILL_FIRST_TWO_BYTES=$(head --bytes=2 \"${MILL_IN_PATH}\")\n  if [ \"${SYSTEM_MILL_FIRST_TWO_BYTES}\" = \"#!\" ]; then\n\t  # MILL_IN_PATH is (very likely) a shell script and not the mill\n\t  # executable, ignore it.\n\t  return 0\n  fi\n\n  SYSTEM_MILL_PATH=$(readlink -e \"${MILL_IN_PATH}\")\n  SYSTEM_MILL_SIZE=$(stat --format=%s \"${SYSTEM_MILL_PATH}\")\n  SYSTEM_MILL_MTIME=$(stat --format=%y \"${SYSTEM_MILL_PATH}\")\n\n  if [ ! -d \"${MILL_USER_CACHE_DIR}\" ]; then\n    mkdir -p \"${MILL_USER_CACHE_DIR}\"\n  fi\n\n  SYSTEM_MILL_INFO_FILE=\"${MILL_USER_CACHE_DIR}/system-mill-info\"\n  if [ -f \"${SYSTEM_MILL_INFO_FILE}\" ]; then\n    parseSystemMillInfo() {\n        LINE_NUMBER=\"${1}\"\n        # Select the line number of the SYSTEM_MILL_INFO_FILE, cut the\n        # variable definition in that line in two halves and return\n        # the value, and finally remove the quotes.\n        sed -n \"${LINE_NUMBER}p\" \"${SYSTEM_MILL_INFO_FILE}\" |\\\n            cut -d= -f2 |\\\n            sed 's/\"\\(.*\\)\"/\\1/'\n    }\n\n    CACHED_SYSTEM_MILL_PATH=$(parseSystemMillInfo 1)\n    CACHED_SYSTEM_MILL_VERSION=$(parseSystemMillInfo 2)\n    CACHED_SYSTEM_MILL_SIZE=$(parseSystemMillInfo 3)\n    CACHED_SYSTEM_MILL_MTIME=$(parseSystemMillInfo 4)\n\n    if [ \"${SYSTEM_MILL_PATH}\" = \"${CACHED_SYSTEM_MILL_PATH}\" ] \\\n           && [ \"${SYSTEM_MILL_SIZE}\" = \"${CACHED_SYSTEM_MILL_SIZE}\" ] \\\n           && [ \"${SYSTEM_MILL_MTIME}\" = \"${CACHED_SYSTEM_MILL_MTIME}\" ]; then\n      if [ \"${CACHED_SYSTEM_MILL_VERSION}\" = \"${MILL_VERSION}\" ]; then\n          MILL=\"${SYSTEM_MILL_PATH}\"\n          return 0\n      else\n          return 0\n      fi\n    fi\n  fi\n\n  SYSTEM_MILL_VERSION=$(${SYSTEM_MILL_PATH} --version | head -n1 | sed -n 's/^Mill.*version \\(.*\\)/\\1/p')\n\n  cat <<EOF > \"${SYSTEM_MILL_INFO_FILE}\"\nCACHED_SYSTEM_MILL_PATH=\"${SYSTEM_MILL_PATH}\"\nCACHED_SYSTEM_MILL_VERSION=\"${SYSTEM_MILL_VERSION}\"\nCACHED_SYSTEM_MILL_SIZE=\"${SYSTEM_MILL_SIZE}\"\nCACHED_SYSTEM_MILL_MTIME=\"${SYSTEM_MILL_MTIME}\"\nEOF\n\n  if [ \"${SYSTEM_MILL_VERSION}\" = \"${MILL_VERSION}\" ]; then\n    MILL=\"${SYSTEM_MILL_PATH}\"\n  fi\n}\ntry_to_use_system_mill\n\n# If not already downloaded, download it\nif [ ! -s \"${MILL}\" ] || [ \"$MILL_TEST_DRY_RUN_LAUNCHER_SCRIPT\" = \"1\" ] ; then\n  case $MILL_VERSION in\n    0.0.* | 0.1.* | 0.2.* | 0.3.* | 0.4.* )\n      DOWNLOAD_SUFFIX=\"\"\n      DOWNLOAD_FROM_MAVEN=0\n      ;;\n    0.5.* | 0.6.* | 0.7.* | 0.8.* | 0.9.* | 0.10.* | 0.11.0-M* )\n      DOWNLOAD_SUFFIX=\"-assembly\"\n      DOWNLOAD_FROM_MAVEN=0\n      ;;\n    *)\n      DOWNLOAD_SUFFIX=\"-assembly\"\n      DOWNLOAD_FROM_MAVEN=1\n      ;;\n  esac\n  case $MILL_VERSION in\n    0.12.0 | 0.12.1 | 0.12.2 | 0.12.3 | 0.12.4 | 0.12.5 | 0.12.6 | 0.12.7 | 0.12.8 | 0.12.9 | 0.12.10 | 0.12.11 )\n      DOWNLOAD_EXT=\"jar\"\n      ;;\n    0.12.* )\n      DOWNLOAD_EXT=\"exe\"\n      ;;\n    0.* )\n      DOWNLOAD_EXT=\"jar\"\n      ;;\n    *)\n      DOWNLOAD_EXT=\"exe\"\n      ;;\n  esac\n\n  DOWNLOAD_FILE=$(mktemp mill.XXXXXX)\n  if [ \"$DOWNLOAD_FROM_MAVEN\" = \"1\" ] ; then\n    DOWNLOAD_URL=\"https://repo1.maven.org/maven2/com/lihaoyi/mill-dist${ARTIFACT_SUFFIX}/${MILL_VERSION}/mill-dist${ARTIFACT_SUFFIX}-${MILL_VERSION}.${DOWNLOAD_EXT}\"\n  else\n    MILL_VERSION_TAG=$(echo \"$MILL_VERSION\" | sed -E 's/([^-]+)(-M[0-9]+)?(-.*)?/\\1\\2/')\n    DOWNLOAD_URL=\"${GITHUB_RELEASE_CDN}${MILL_REPO_URL}/releases/download/${MILL_VERSION_TAG}/${MILL_VERSION}${DOWNLOAD_SUFFIX}\"\n    unset MILL_VERSION_TAG\n  fi\n\n  if [ \"$MILL_TEST_DRY_RUN_LAUNCHER_SCRIPT\" = \"1\" ] ; then\n    echo $DOWNLOAD_URL\n    echo $MILL\n    exit 0\n  fi\n  # TODO: handle command not found\n  echo \"Downloading mill ${MILL_VERSION} from ${DOWNLOAD_URL} ...\" 1>&2\n  ${CURL_CMD} -f -L -o \"${DOWNLOAD_FILE}\" \"${DOWNLOAD_URL}\"\n  chmod +x \"${DOWNLOAD_FILE}\"\n  mkdir -p \"${MILL_DOWNLOAD_PATH}\"\n  mv \"${DOWNLOAD_FILE}\" \"${MILL}\"\n\n  unset DOWNLOAD_FILE\n  unset DOWNLOAD_SUFFIX\nfi\n\nif [ -z \"$MILL_MAIN_CLI\" ] ; then\n  MILL_MAIN_CLI=\"${0}\"\nfi\n\nMILL_FIRST_ARG=\"\"\nif [ \"$1\" = \"--bsp\" ] || [ \"${1#\"-i\"}\" != \"$1\" ] || [ \"$1\" = \"--interactive\" ] || [ \"$1\" = \"--no-server\" ] || [ \"$1\" = \"--no-daemon\" ] || [ \"$1\" = \"--repl\" ] || [ \"$1\" = \"--help\" ] ; then\n  # Need to preserve the first position of those listed options\n  MILL_FIRST_ARG=$1\n  shift\nfi\n\nunset MILL_DOWNLOAD_PATH\nunset MILL_OLD_DOWNLOAD_PATH\nunset OLD_MILL\nunset MILL_VERSION\nunset MILL_REPO_URL\n\n# -D mill.main.cli is for compatibility with Mill 0.10.9 - 0.13.0-M2\n# We don't quote MILL_FIRST_ARG on purpose, so we can expand the empty value without quotes\n# shellcheck disable=SC2086\nexec \"${MILL}\" $MILL_FIRST_ARG -D \"mill.main.cli=${MILL_MAIN_CLI}\" \"$@\"\n"
  },
  {
    "path": "mill.bat",
    "content": "@echo off\n\nrem This is a wrapper script, that automatically selects or downloads Mill from Maven Central or GitHub release pages.\nrem\nrem This script determines the Mill version to use by trying these sources\nrem   - env-variable `MILL_VERSION`\nrem   - local file `.mill-version`\nrem   - local file `.config/mill-version`\nrem   - `mill-version` from YAML frontmatter of current buildfile\nrem   - if accessible, find the latest stable version available on Maven Central (https://repo1.maven.org/maven2)\nrem   - env-variable `DEFAULT_MILL_VERSION`\nrem\nrem If a version has the suffix '-native' a native binary will be used.\nrem If a version has the suffix '-jvm' an executable jar file will be used, requiring an already installed Java runtime.\nrem If no such suffix is found, the script will pick a default based on version and platform.\nrem\nrem Once a version was determined, it tries to use either\nrem    - a system-installed mill, if found and it's version matches\nrem    - an already downloaded version under %USERPROFILE%\\.mill\\download\nrem\nrem If no working mill version was found on the system,\nrem this script downloads a binary file from Maven Central or Github Pages (this is version dependent)\nrem into a cache location (%USERPROFILE%\\.mill\\download).\nrem\nrem Mill Project URL: https://github.com/com-lihaoyi/mill\nrem Script Version: 1.0.5\nrem\nrem If you want to improve this script, please also contribute your changes back!\nrem This script was generated from: dist/scripts/src/mill.bat\nrem\nrem Licensed under the Apache License, Version 2.0\n\nrem setlocal seems to be unavailable on Windows 95/98/ME\nrem but I don't think we need to support them in 2019\nsetlocal enabledelayedexpansion\n\nif [!DEFAULT_MILL_VERSION!]==[] ( set \"DEFAULT_MILL_VERSION=1.0.5\" )\n\nif [!MILL_GITHUB_RELEASE_CDN!]==[] ( set \"MILL_GITHUB_RELEASE_CDN=\" )\n\nif [!MILL_MAIN_CLI!]==[] ( set \"MILL_MAIN_CLI=%~f0\" )\n\nset \"MILL_REPO_URL=https://github.com/com-lihaoyi/mill\"\n\nSET MILL_BUILD_SCRIPT=\n\nif exist \"build.mill\" (\n  set MILL_BUILD_SCRIPT=build.mill\n) else (\n    if exist \"build.mill.scala\" (\n      set MILL_BUILD_SCRIPT=build.mill.scala\n    ) else (\n        if exist \"build.sc\" (\n          set MILL_BUILD_SCRIPT=build.sc\n        ) else (\n            rem no-op\n        )\n    )\n)\n\nif [!MILL_VERSION!]==[] (\n  if exist .mill-version (\n    set /p MILL_VERSION=<.mill-version\n  ) else (\n    if exist .config\\mill-version (\n      set /p MILL_VERSION=<.config\\mill-version\n    ) else (\n      if not \"%MILL_BUILD_SCRIPT%\"==\"\" (\n        rem Find the line and process it\n        for /f \"tokens=*\" %%a in ('findstr /R /C:\"//\\|.*mill-version\" \"%MILL_BUILD_SCRIPT%\"') do (\n            set \"line=%%a\"\n\n            rem --- 1. Replicate sed 's/.*://' ---\n            rem This removes everything up to and including the first colon\n            set \"line=!line:*:=!\"\n\n            rem --- 2. Replicate sed 's/#.*//' ---\n            rem Split on '#' and keep the first part\n            for /f \"tokens=1 delims=#\" %%b in (\"!line!\") do (\n                set \"line=%%b\"\n            )\n\n            rem --- 3. Replicate sed 's/['\"]//g' ---\n            rem Remove all quotes\n            set \"line=!line:'=!\"\n            set \"line=!line:\"=!\"\n\n            rem --- 4. NEW: Replicate sed's trim/space removal ---\n            rem Remove all space characters from the result. This is more robust.\n            set \"MILL_VERSION=!line: =!\"\n\n            rem We found the version, so we can exit the loop\n            goto :version_found\n        )\n\n        :version_found\n        rem no-op\n      ) else (\n        rem no-op\n      )\n    )\n  )\n)\n\nif [!MILL_VERSION!]==[] (\n    echo No mill version specified. >&2\n    echo You should provide a version via a '//^| mill-version: ' comment or a '.mill-version' file. >&2\n    set MILL_VERSION=%DEFAULT_MILL_VERSION%\n)\n\nif [!MILL_DOWNLOAD_PATH!]==[] set MILL_DOWNLOAD_PATH=%USERPROFILE%\\.mill\\download\n\nrem without bat file extension, cmd doesn't seem to be able to run it\n\nset \"MILL_NATIVE_SUFFIX=-native\"\nset \"MILL_JVM_SUFFIX=-jvm\"\nset \"FULL_MILL_VERSION=%MILL_VERSION%\"\nset \"MILL_EXT=.bat\"\nset \"ARTIFACT_SUFFIX=\"\nREM Check if MILL_VERSION contains MILL_NATIVE_SUFFIX\necho !MILL_VERSION! | findstr /C:\"%MILL_NATIVE_SUFFIX%\" >nul\nif !errorlevel! equ 0 (\n    set \"MILL_VERSION=%MILL_VERSION:-native=%\"\n    REM -native images compiled with graal do not support windows-arm\n    REM https://github.com/oracle/graal/issues/9215\n    IF /I NOT \"%PROCESSOR_ARCHITECTURE%\"==\"ARM64\" (\n        set \"ARTIFACT_SUFFIX=-native-windows-amd64\"\n        set \"MILL_EXT=.exe\"\n    ) else (\n        rem no-op\n    )\n) else (\n    echo !MILL_VERSION! | findstr /C:\"%MILL_JVM_SUFFIX%\" >nul\n    if !errorlevel! equ 0 (\n        set \"MILL_VERSION=%MILL_VERSION:-jvm=%\"\n    ) else (\n        set \"SKIP_VERSION=false\"\n        set \"MILL_PREFIX=%MILL_VERSION:~0,4%\"\n        if \"!MILL_PREFIX!\"==\"0.1.\" set \"SKIP_VERSION=true\"\n        if \"!MILL_PREFIX!\"==\"0.2.\" set \"SKIP_VERSION=true\"\n        if \"!MILL_PREFIX!\"==\"0.3.\" set \"SKIP_VERSION=true\"\n        if \"!MILL_PREFIX!\"==\"0.4.\" set \"SKIP_VERSION=true\"\n        if \"!MILL_PREFIX!\"==\"0.5.\" set \"SKIP_VERSION=true\"\n        if \"!MILL_PREFIX!\"==\"0.6.\" set \"SKIP_VERSION=true\"\n        if \"!MILL_PREFIX!\"==\"0.7.\" set \"SKIP_VERSION=true\"\n        if \"!MILL_PREFIX!\"==\"0.8.\" set \"SKIP_VERSION=true\"\n        if \"!MILL_PREFIX!\"==\"0.9.\" set \"SKIP_VERSION=true\"\n        set \"MILL_PREFIX=%MILL_VERSION:~0,5%\"\n        if \"!MILL_PREFIX!\"==\"0.10.\" set \"SKIP_VERSION=true\"\n        if \"!MILL_PREFIX!\"==\"0.11.\" set \"SKIP_VERSION=true\"\n        if \"!MILL_PREFIX!\"==\"0.12.\" set \"SKIP_VERSION=true\"\n\n        if \"!SKIP_VERSION!\"==\"false\" (\n            IF /I NOT \"%PROCESSOR_ARCHITECTURE%\"==\"ARM64\" (\n                set \"ARTIFACT_SUFFIX=-native-windows-amd64\"\n                set \"MILL_EXT=.exe\"\n            )\n        ) else (\n            rem no-op\n        )\n    )\n)\n\nset MILL=%MILL_DOWNLOAD_PATH%\\!FULL_MILL_VERSION!!MILL_EXT!\n\nset MILL_RESOLVE_DOWNLOAD=\n\nif not exist \"%MILL%\" (\n  set MILL_RESOLVE_DOWNLOAD=true\n) else (\n    if defined MILL_TEST_DRY_RUN_LAUNCHER_SCRIPT (\n        set MILL_RESOLVE_DOWNLOAD=true\n    ) else (\n        rem no-op\n    )\n)\n\n\nif [!MILL_RESOLVE_DOWNLOAD!]==[true] (\n    set MILL_VERSION_PREFIX=%MILL_VERSION:~0,4%\n    set MILL_SHORT_VERSION_PREFIX=%MILL_VERSION:~0,2%\n    rem Since 0.5.0\n    set MILL_DOWNLOAD_SUFFIX=-assembly\n    rem Since 0.11.0\n    set MILL_DOWNLOAD_FROM_MAVEN=1\n    if [!MILL_VERSION_PREFIX!]==[0.0.] (\n        set MILL_DOWNLOAD_SUFFIX=\n        set MILL_DOWNLOAD_FROM_MAVEN=0\n    )\n    if [!MILL_VERSION_PREFIX!]==[0.1.] (\n        set MILL_DOWNLOAD_SUFFIX=\n        set MILL_DOWNLOAD_FROM_MAVEN=0\n    )\n    if [!MILL_VERSION_PREFIX!]==[0.2.] (\n        set MILL_DOWNLOAD_SUFFIX=\n        set MILL_DOWNLOAD_FROM_MAVEN=0\n    )\n    if [!MILL_VERSION_PREFIX!]==[0.3.] (\n        set MILL_DOWNLOAD_SUFFIX=\n        set MILL_DOWNLOAD_FROM_MAVEN=0\n    )\n    if [!MILL_VERSION_PREFIX!]==[0.4.] (\n        set MILL_DOWNLOAD_SUFFIX=\n        set MILL_DOWNLOAD_FROM_MAVEN=0\n    )\n    if [!MILL_VERSION_PREFIX!]==[0.5.] set MILL_DOWNLOAD_FROM_MAVEN=0\n    if [!MILL_VERSION_PREFIX!]==[0.6.] set MILL_DOWNLOAD_FROM_MAVEN=0\n    if [!MILL_VERSION_PREFIX!]==[0.7.] set MILL_DOWNLOAD_FROM_MAVEN=0\n    if [!MILL_VERSION_PREFIX!]==[0.8.] set MILL_DOWNLOAD_FROM_MAVEN=0\n    if [!MILL_VERSION_PREFIX!]==[0.9.] set MILL_DOWNLOAD_FROM_MAVEN=0\n\n    set MILL_VERSION_PREFIX=%MILL_VERSION:~0,5%\n    if [!MILL_VERSION_PREFIX!]==[0.10.] set MILL_DOWNLOAD_FROM_MAVEN=0\n\n    set MILL_VERSION_PREFIX=%MILL_VERSION:~0,8%\n    if [!MILL_VERSION_PREFIX!]==[0.11.0-M] set MILL_DOWNLOAD_FROM_MAVEN=0\n\n    set MILL_VERSION_PREFIX=%MILL_VERSION:~0,5%\n    set DOWNLOAD_EXT=exe\n    if [!MILL_SHORT_VERSION_PREFIX!]==[0.] set DOWNLOAD_EXT=jar\n    if [!MILL_VERSION_PREFIX!]==[0.12.] set DOWNLOAD_EXT=exe\n    if [!MILL_VERSION!]==[0.12.0] set DOWNLOAD_EXT=jar\n    if [!MILL_VERSION!]==[0.12.1] set DOWNLOAD_EXT=jar\n    if [!MILL_VERSION!]==[0.12.2] set DOWNLOAD_EXT=jar\n    if [!MILL_VERSION!]==[0.12.3] set DOWNLOAD_EXT=jar\n    if [!MILL_VERSION!]==[0.12.4] set DOWNLOAD_EXT=jar\n    if [!MILL_VERSION!]==[0.12.5] set DOWNLOAD_EXT=jar\n    if [!MILL_VERSION!]==[0.12.6] set DOWNLOAD_EXT=jar\n    if [!MILL_VERSION!]==[0.12.7] set DOWNLOAD_EXT=jar\n    if [!MILL_VERSION!]==[0.12.8] set DOWNLOAD_EXT=jar\n    if [!MILL_VERSION!]==[0.12.9] set DOWNLOAD_EXT=jar\n    if [!MILL_VERSION!]==[0.12.10] set DOWNLOAD_EXT=jar\n    if [!MILL_VERSION!]==[0.12.11] set DOWNLOAD_EXT=jar\n\n    set MILL_VERSION_PREFIX=\n    set MILL_SHORT_VERSION_PREFIX=\n\n    for /F \"delims=- tokens=1\" %%A in (\"!MILL_VERSION!\") do set MILL_VERSION_BASE=%%A\n    set MILL_VERSION_MILESTONE=\n    for /F \"delims=- tokens=2\" %%A in (\"!MILL_VERSION!\") do set MILL_VERSION_MILESTONE=%%A\n    set MILL_VERSION_MILESTONE_START=!MILL_VERSION_MILESTONE:~0,1!\n    if [!MILL_VERSION_MILESTONE_START!]==[M] (\n        set MILL_VERSION_TAG=!MILL_VERSION_BASE!-!MILL_VERSION_MILESTONE!\n    ) else (\n        set MILL_VERSION_TAG=!MILL_VERSION_BASE!\n    )\n    if [!MILL_DOWNLOAD_FROM_MAVEN!]==[1] (\n        set MILL_DOWNLOAD_URL=https://repo1.maven.org/maven2/com/lihaoyi/mill-dist!ARTIFACT_SUFFIX!/!MILL_VERSION!/mill-dist!ARTIFACT_SUFFIX!-!MILL_VERSION!.!DOWNLOAD_EXT!\n    ) else (\n        set MILL_DOWNLOAD_URL=!MILL_GITHUB_RELEASE_CDN!%MILL_REPO_URL%/releases/download/!MILL_VERSION_TAG!/!MILL_VERSION!!MILL_DOWNLOAD_SUFFIX!\n    )\n\n    if defined MILL_TEST_DRY_RUN_LAUNCHER_SCRIPT (\n        echo !MILL_DOWNLOAD_URL!\n        echo !MILL!\n        exit /b 0\n    )\n\n    rem there seems to be no way to generate a unique temporary file path (on native Windows)\n    set MILL_DOWNLOAD_FILE=%MILL%.tmp\n\n    echo Downloading mill !MILL_VERSION! from !MILL_DOWNLOAD_URL! ... 1>&2\n\n    if not exist \"%MILL_DOWNLOAD_PATH%\" mkdir \"%MILL_DOWNLOAD_PATH%\"\n    rem curl is bundled with recent Windows 10\n    rem but I don't think we can expect all the users to have it in 2019\n    where /Q curl\n    if !ERRORLEVEL! EQU 0 (\n        curl -f -L \"!MILL_DOWNLOAD_URL!\" -o \"!MILL_DOWNLOAD_FILE!\"\n    ) else (\n        rem bitsadmin seems to be available on Windows 7\n        rem without /dynamic, github returns 403\n        rem bitsadmin is sometimes needlessly slow but it looks better with /priority foreground\n        bitsadmin /transfer millDownloadJob /dynamic /priority foreground \"!MILL_DOWNLOAD_URL!\" \"!MILL_DOWNLOAD_FILE!\"\n    )\n    if not exist \"!MILL_DOWNLOAD_FILE!\" (\n        echo Could not download mill !MILL_VERSION! 1>&2\n        exit /b 1\n    )\n\n    move /y \"!MILL_DOWNLOAD_FILE!\" \"%MILL%\"\n\n    set MILL_DOWNLOAD_FILE=\n    set MILL_DOWNLOAD_SUFFIX=\n)\n\nset MILL_DOWNLOAD_PATH=\nset MILL_VERSION=\nset MILL_REPO_URL=\n\nrem Need to preserve the first position of those listed options\nset MILL_FIRST_ARG=\nif [%~1%]==[--bsp] (\n  set MILL_FIRST_ARG=%1%\n) else (\n  if [%~1%]==[-i] (\n    set MILL_FIRST_ARG=%1%\n  ) else (\n    if [%~1%]==[--interactive] (\n      set MILL_FIRST_ARG=%1%\n    ) else (\n      if [%~1%]==[--no-server] (\n        set MILL_FIRST_ARG=%1%\n      ) else (\n        if [%~1%]==[--no-daemon] (\n          set MILL_FIRST_ARG=%1%\n        ) else (\n          if [%~1%]==[--repl] (\n            set MILL_FIRST_ARG=%1%\n          ) else (\n            if [%~1%]==[--help] (\n              set MILL_FIRST_ARG=%1%\n            )\n          )\n        )\n      )\n    )\n  )\n)\nset \"MILL_PARAMS=%*%\"\n\nif not [!MILL_FIRST_ARG!]==[] (\n  for /f \"tokens=1*\" %%a in (\"%*\") do (\n    set \"MILL_PARAMS=%%b\"\n  )\n)\n\nrem -D mill.main.cli is for compatibility with Mill 0.10.9 - 0.13.0-M2\n\"%MILL%\" %MILL_FIRST_ARG% -D \"mill.main.cli=%MILL_MAIN_CLI%\" %MILL_PARAMS%\n"
  },
  {
    "path": "readme.adoc",
    "content": "= Acyclic\n:version: 0.3.21\n:toc-placement: preamble\n:toc:\n:link-acyclic: https://github.com/com-lihaoyi/acyclic\n:link-acyclic-gitter:  https://gitter.im/lihaoyi/acyclic\n:link-utest: https://github.com/com-lihaoyi/utest\n:link-scalatags: https://github.com/com-lihaoyi/scalatags\n:link-scalarx: https://github.com/lihaoyi/scala.rx\n\nimage:https://badges.gitter.im/Join%20Chat.svg[\"Join the chat\", link=\"{link-acyclic-gitter}?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge\"]\n\n*Acyclic* is a Scala compiler plugin that allows you to mark files within a build as `acyclic`, turning circular dependencies between files into compilation errors.\n\n== Introduction\n\n*Acyclic* is a Scala compiler plugin that allows you to mark files within a build as `acyclic`, turning circular dependencies between files into compilation errors.\n\nFor example, the following two files have a circular dependency between them:\n\n[source,scala]\n----\npackage fail.simple\n\nclass A {\n  val b: B = null\n}\n\n----\n\n[source,scala]\n----\npackage fail.simple\n\nclass B {\n  val a: A = null\n}\n----\n\nIn this case it is very obvious that there is a circular dependency, but in larger projects the fact that a circular dependency exists can be difficult to spot. With *Acyclic*, you can annotate either source file with an `acyclic` import:\n\n[source,scala]\n----\npackage fail.simple\nimport acyclic.file\n\nclass A {\n  val b: B = null\n}\n----\n\nAnd attempting to compile these files together will then result in a compilation error:\n\n[source,scala]\n----\nerror: Unwanted cyclic dependency\n\nsrc/test/resources/fail/simple/B.scala:4:\n  val a1: A = new A\n                  ^\nsymbol: class A\nMore dependencies at lines 5\n\nsrc/test/resources/fail/simple/A.scala:6:\n  val b: B = null\n      ^\nsymbol: class B\n\n----\n\nThis applies to term-dependencies, type-dependencies, as well as cycles that span more than two files. Circular dependencies between files is something that people often don't want, but are difficult to avoid as introducing cycles is hard to detect while working or during code review. *Acyclic* is designed to help you guard against unwanted cycles at compile-time, and tells you exactly where the cycles are when they appear so you can deal with them.\n\nA more realistic example of a cycle that *Acyclic* may find is this one taken from a cycle in {link-utest}[uTest]:\n\n[source,scala]\n----\n[error] Circular dependency between acyclic files:\n[info] /Users/haoyi/Dropbox (Personal)/Workspace/utest/shared/main/scala/utest/Formatter.scala:58: acyclic\n[info]       val traceStr = r.value match{\n[info]                        ^\n[info] Other dependencies at lines: 20, 54, 44, 66, 40, 15, 67, 2, 45, 42\n[info] /Users/haoyi/Dropbox (Personal)/Workspace/utest/shared/main/scala/utest/framework/Model.scala:76:\n[info]           v.runAsync(onComplete, path :+ i, strPath :+ v.value.name, thisError)\n[info]           ^\n[info] Other dependencies at lines: 120\n[info] /Users/haoyi/Dropbox (Personal)/Workspace/utest/shared/main/scala/utest/package.scala:72:\n[info]   val TestSuite = framework.TestSuite\n[info]       ^\n[info] Other dependencies at lines: 73\n[info] /Users/haoyi/Dropbox (Personal)/Workspace/utest/shared/main/scala/utest/framework/TestSuite.scala:37:\n[info]         log(formatter.formatSingle(path, s))\n[info]                       ^\n[info] Other dependencies at lines: 41, 33\n----\n\nAs you can see, there is a dependency cycle between `Formatter.scala`, `Model.scala`, `package.scala` and `TestSuite.scala`. `package.scala` has been explicitly marked `acyclic`, and so compilation fails with an error. Apart from the line shown, *Acyclic* also gives other lines in the same file which contain dependencies contributing to this cycle.\n\nSpotting this dependency cycle spanning 4 different files, and knowing exactly which pieces of code are causing it, is something that is virtually impossible to do manually via inspection or code-review. Using *Acyclic*, there is no chance of accidentally introducing a dependency cycle you don't want, and even when you do, it shows you exactly what's causing the cycle that you need to fix to make it go away.\n\n== Package Cycles\n\n*Acyclic* also allows you to annotate entire packages as `acyclic` by placing a `import acyclic.pkg` inside the package object. Consider the following set of files:\n\n[source,scala]\n----\n// c/C1.scala\npackage fail.halfpackagecycle.c\n\nobject C1\n----\n\n[source,scala]\n----\n// c/C2.scala\npackage fail.halfpackagecycle\npackage c\n\nclass C2 {\n  lazy val b = new B\n}\n----\n\n[source,scala]\n----\n// c/package.scala\npackage fail.halfpackagecycle\n\npackage object c {\n  import acyclic.pkg\n}\n----\n\n[source,scala]\n----\n// A.scala\npackage fail.halfpackagecycle\n\nclass A {\n  val thing = c.C1\n}\n----\n\n[source,scala]\n----\n// B.scala\npackage fail.halfpackagecycle\n\nclass B extends A\n----\n\nThese 5 files do not have any file-level cycles, and form a nice linear dependency chain:\n\n----\nc/C2.scala -> B.scala -> A.scala -> c/C1.scala\n----\n\nHowever, we may want to preserve the invariant that the package `c` does not have any cyclic dependencies with other packages or files.. By annotating the package with `import acyclic.pkg` in its package objects as shown above, we can make this circular package dependency error out:\n\n[source,scala]\n----\nerror: Unwanted cyclic dependency\n\nsrc/test/resources/fail/halfpackagecycle/B.scala:3:\nclass B extends A\n        ^\nsymbol: constructor A\n\nsrc/test/resources/fail/halfpackagecycle/A.scala:4:\n  val thing = c.C1\n      ^\nsymbol: object C1\n\npackage fail.halfpackagecycle.c\nsrc/test/resources/fail/halfpackagecycle/c/C2.scala:5:\n  lazy val b = new B\n           ^\nsymbol: class B\n----\n\nSince, `c` as a whole must be acyclic, the dependency cycle between `c`, `B.scala` and `A.scala` is prohibited, and *Acyclic* errors out. As you can see, it tells you exactly where the dependencies are in the source files, giving you an opportunity to find and remove them. Here's a realistic example from Scala.Rx:\n\n[source,scala]\n----\n[error] Unwanted cyclic dependency\n[info]\n[info] /Users/haoyi/Dropbox (Personal)/Workspace/scala.rx/shared/main/scala/rx/core/Dynamic.scala:10:\n[info] import rx.ops.Spinlock\n[info]        ^\n[info] symbol: value <import>\n[info] More dependencies at lines 29 60 33 41 27 23\n[info]\n[info] package rx.ops\n[info] /Users/haoyi/Dropbox (Personal)/Workspace/scala.rx/shared/main/scala/rx/ops/Async.scala:78:\n[info]       super.ping(incoming)\n[info]             ^\n[info] symbol: method ping\n[info] More dependencies at lines 69 101 97 95 67\n----\n\nAs you can see, `Dynamic.scala` in `rx.core` was accidentally depending on `Spinlock` in `rx.ops`. That cross-module dependency from `rx.core` to `rx.ops` should not exist, and the proper solution was to move `Spinlock` over to `rx.core`. Without *Acyclic*, this circular dependency would likely have gone un-noticed.\n\n== How to Use\n\n=== Mill\n\nFor Mill, use the following:\n\n[source,scala,subs=\"attributes,verbatim\"]\n----\ndef compileIvyDeps = Agg(ivy\"com.lihaoyi:::acyclic:{version}\")\ndef scalacPluginIvyDeps = Agg(ivy\"com.lihaoyi:::acyclic:{version}\")\n----\n\n=== sbt\n\nTo use, add the following to your `build.sbt`:\n\n[source,scala,subs=\"attributes,verbatim\"]\n----\nlibraryDependencies += (\"com.lihaoyi\" %% \"acyclic\" % \"{version}\" cross (CrossVersion.full)) % \"provided\"\n\nautoCompilerPlugins := true\n\naddCompilerPlugin(\"com.lihaoyi\" %% \"acyclic\" % \"{version}\" cross (CrossVersion.full))\n----\n\n=== Force\n\nIf you want to enforce acyclicity across _all_ your files, you can pass in the\ncommand-line compiler flag:\n\n----\n-P:acyclic:force \n----\n\nOr via SBT:\n\n[source,scala]\n----\nscalacOptions += \"-P:acyclic:force\"\n----\n\nThis will make the acyclic plugin complain if _any_ file in your project is involved\nin an import cycle, without needing to annotate everything with\n`import acyclic.file`. If you want to white-list a small number of files whose\ncycles you've decided are OK, you can use\n\n[source,scala]\n----\nimport acyclic.skipped\n----\n\nto tell the acyclic plugin to ignore them.\n\n=== Warnings instead of errors\n\nIf you want the plugin to only emit warnings instead of errors, add `warn` to the plugin's flags.\n\n[source,scala]\n----\nscalacOptions += \"-P:acyclic:warn\"\n----\n\n== Who uses it?\n\n*Acyclic* is currently being used in {link-utest}[uTest], {link-scalatags}[Scalatags] and {link-scalarx}[Scala.Rx], and helped remove many cycle between files which had no good reason for being cyclic.\nIt is also being used to verify the acyclicity of {link-acyclic}/blob/main/acyclic/src/acyclic/plugin/PluginPhase.scala[its own code].\nIt works with Scala 2.11, 2.12 and 2.13.\n\nIf you're using incremental compilation, you may need to do a clean compile for *Acyclic* to find all unwanted cycles in the compilation run.\n\n\n== Limitations\n\nAcyclic has problems in a number of cases:\n\n* If you use curly-braced `package XXX {}` acyclic inside your source files, it does the wrong thing. Acyclic assumes all packages are listed in a sequence of statements at the top of each file\n* Under incremental compilation, Acyclic does not always find all possible cycles, since one cycles within the files currently getting compiled will get caught. A solution is to do a clean build every once in a while.\n\n== License\n\n*_Acyclic* is published under the MIT License:_\n\nThe MIT License (MIT)\n\nCopyright (c) 2014 Li Haoyi\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n\n== ChangeLog\n\n=== `main` branch\n\n* Add support for Scala 3.8.3\n\n=== 0.3.21\n\n* Add support for Scala 2.12.21, 3.3.7, 3.8.0, 3.8.1, 3.8.2\n* (2026-03-31) Back-published support for Scala 3.8.3\n\n=== 0.3.20\n\n* Added support for Scala 2.13.18 and 3.7.4\n* Back-published for Scala 2.12.21\n\n=== 0.3.19\n\n* Added support for Scala 2.13.17, 3.3.4, 3.3.5, 3.3.6, 3.7.0, 3.7.1, 3.7.2, 3.7.3\n\n=== 0.3.18\n\n* Added support for Scala 3.6.4\n\n=== 0.3.17\n\n* Added support for Scala 3.6.x\n\n=== 0.3.16\n\n* Added support for Scala 2.13.16\n\n=== 0.3.15\n\n* Scala 3.x support https://github.com/com-lihaoyi/acyclic/pull/136[#136]\n\n=== 0.3.14\n* Added support for Scala 2.13.15\n\n=== 0.3.13\n\n* Added support for Scala 2.12.20\n\n=== 0.3.12\n\n* Added support for Scala 2.13.14\n\n=== 0.3.11\n\n* Added support for Scala 2.13.13\n\n=== 0.3.10\n\n* Added suport for Scala 2.12.19\n\n=== 0.3.9\n\n* Added support for Scala 2.13.12\n\n=== 0.3.8\n\n* Added support for Scala 2.13.11\n\n=== 0.3.7\n\n* Added support for Scala 2.12.18\n\n=== 0.3.6\n\n* Added support for Scala 2.13.10\n\n=== 0.3.5\n\n* Added support for Scala 2.13.9\n\n=== 0.3.4\n\n* Added support for Scala 2.12.17\n\n=== 0.3.3\n\n* Added support for Scala 2.12.16\n\n=== 0.3.2\n\n* Added plugin option `warn` to emit compiler warnings instead of errors\n\n=== 0.3.1\n\n* Support for Scala 2.13.8\n\n=== 0.3.0\n\n* Cross-build across all scala point versions\n\n=== 0.2.0\n\n* Support for Scala 2.13.0 final\n\n=== 0.1.7\n\n* Fix `import acyclic.skipped`, which was broken in 0.1.6\n\n=== 0.1.6\n\n* You can now use the scalac option `-P:acyclic:force`\n(`scalaOptions += \"-P:acyclic:force\"` in SBT) to enforce acyclicity across\nyour entire codebase.\n\n=== 0.1.5\n\n* Scala 2.12.x support\n\n=== 0.1.4\n\n* Loosen restrictions on compiler plugin placement, to allow better\ninteractions with other plugins. Also, `acyclic.file` is now `@compileTimeOnly` to provide better errors\n\n=== 0.1.3\n\n* Ignore, but don't crash, on Java sources\n\n"
  }
]