Skip to content

Commit

Permalink
Fix init command and support running Mill without existing build.sc
Browse files Browse the repository at this point in the history
I first added an integration test for the init command to reproduce the issues and make sure the feature now works.

Instead of failing in case there is no `build.sc`, we continue with an empty setup.

Only when we discover an error afterwards, we add a message about the missing `build.sc` to the underlying error message.
  • Loading branch information
lefou committed Jul 21, 2023
1 parent 7794f34 commit 7f3d8c7
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 12 deletions.
16 changes: 16 additions & 0 deletions integration/feature/init/test/src/MillInitTests.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package mill.integration
package local
import mill.bsp.Constants
import utest._

object MillInitTests extends IntegrationTestSuite {

def tests: Tests = Tests {
test("Mill init works") {
val workspacePath = initWorkspace()
eval("init", "com-lihaoyi/mill-scala-hello.g8", "--name=example") ==> true
val projFile = workspacePath / "example" / "build.sc"
assert(os.exists(projFile))
}
}
}
15 changes: 8 additions & 7 deletions runner/src/mill/runner/FileImportGraph.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ case class FileImportGraph(
*/
@internal
object FileImportGraph {
def backtickWrap(s: String) = if (encode(s) == s) s else "`" + s + "`"
def backtickWrap(s: String): String = if (encode(s) == s) s else "`" + s + "`"

import mill.api.JsonFormatters.pathReadWrite
implicit val readWriter: upickle.default.ReadWriter[FileImportGraph] = upickle.default.macroRW
Expand All @@ -38,13 +38,14 @@ object FileImportGraph {
val errors = mutable.Buffer.empty[String]
var millImport = false

def walkScripts(s: os.Path): Unit = {
def walkScripts(s: os.Path, useDummy: Boolean = false): Unit = {
importGraphEdges(s) = Nil

if (!seenScripts.contains(s)) {
val readFileEither = scala.util.Try(
Parsers.splitScript(os.read(s), s.relativeTo(topLevelProjectRoot).toString)
) match {
val readFileEither = scala.util.Try {
val content = if (useDummy) "" else os.read(s)
Parsers.splitScript(content, s.relativeTo(topLevelProjectRoot).toString)
} match {
case scala.util.Failure(ex) => Left(ex.getClass.getName + " " + ex.getMessage)
case scala.util.Success(value) => value
}
Expand All @@ -63,7 +64,7 @@ object FileImportGraph {
walkStmt(s, stmt0, importTrees, fileImports, transformedStmts)
}
seenScripts(s) = transformedStmts.mkString
fileImports.foreach(walkScripts)
fileImports.foreach(walkScripts(_))
}
}
}
Expand Down Expand Up @@ -116,7 +117,7 @@ object FileImportGraph {
transformedStmts.append(stmt)
}

walkScripts(projectRoot / "build.sc")
walkScripts(projectRoot / "build.sc", !os.exists(projectRoot / "build.sc"))
new FileImportGraph(
seenScripts.toMap,
seenRepo.toSeq,
Expand Down
16 changes: 12 additions & 4 deletions runner/src/mill/runner/MillBuildBootstrap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,24 @@ class MillBuildBootstrap(
}

def evaluateRec(depth: Int): RunnerState = {
// println(s"+evaluateRec($depth) " + recRoot(projectRoot, depth))
println(s"+evaluateRec($depth) " + recRoot(projectRoot, depth))
val prevFrameOpt = prevRunnerState.frames.lift(depth)
val prevOuterFrameOpt = prevRunnerState.frames.lift(depth - 1)

val nestedState =
if (depth == 0) {
if (os.exists(recRoot(projectRoot, depth) / "build.sc")) evaluateRec(depth + 1)
// On this level we typically want assume a Mill project, which means we want to require an existing `build.sc`.
// Unfortunately, some targets also make sense without a `build.sc`, e.g. the `init` command.
// Hence we only report a missing `build.sc` as an problem if the command itself does not succeed.
val state = evaluateRec(depth + 1)
if (os.exists(recRoot(projectRoot, depth) / "build.sc")) state
else {
val msg = "build.sc file not found. Are you in a Mill project folder?"
RunnerState(None, Nil, Some(msg))
state match {
case RunnerState(bootstrapModuleOpt, frames, Some(error)) =>
val msg = "build.sc file not found. Are you in a Mill project folder?\n"
RunnerState(bootstrapModuleOpt, frames, Some(msg + error))
case state => state
}
}
} else {
val parsedScriptFiles = FileImportGraph.parseBuildFiles(
Expand Down
5 changes: 4 additions & 1 deletion runner/src/mill/runner/RunnerState.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ case class RunnerState(
frames: Seq[RunnerState.Frame],
errorOpt: Option[String]
) {
def add(frame: RunnerState.Frame = RunnerState.Frame.empty, errorOpt: Option[String] = None) = {
def add(
frame: RunnerState.Frame = RunnerState.Frame.empty,
errorOpt: Option[String] = None
): RunnerState = {
this.copy(frames = Seq(frame) ++ frames, errorOpt = errorOpt)
}
}
Expand Down

0 comments on commit 7f3d8c7

Please sign in to comment.