Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion bsp/worker/src/mill/bsp/worker/State.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ private class State(projectRoot: os.Path, baseLogger: ColorLogger, debug: String
threadCount = None,
targetsAndParams = Seq("resolve", "_"),
prevRunnerState = mill.runner.RunnerState.empty,
logger = baseLogger
logger = baseLogger,
needBuildSc = true
).evaluate()

val rootModules0 = evaluated.result.frames
Expand Down
Empty file.
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))
}
}
}
18 changes: 11 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 @@ -58,12 +59,14 @@ object FileImportGraph {

case Right(stmts) =>
val fileImports = mutable.Set.empty[os.Path]
// we don't expect any new imports when using an empty dummy
if (useDummy) assert(fileImports.isEmpty)
val transformedStmts = mutable.Buffer.empty[String]
for ((stmt0, importTrees) <- Parsers.parseImportHooksWithIndices(stmts)) {
walkStmt(s, stmt0, importTrees, fileImports, transformedStmts)
}
seenScripts(s) = transformedStmts.mkString
fileImports.foreach(walkScripts)
fileImports.foreach(walkScripts(_))
}
}
}
Expand Down Expand Up @@ -116,7 +119,8 @@ object FileImportGraph {
transformedStmts.append(stmt)
}

walkScripts(projectRoot / "build.sc")
val useDummy = !os.exists(projectRoot / "build.sc")
walkScripts(projectRoot / "build.sc", useDummy)
new FileImportGraph(
seenScripts.toMap,
seenRepo.toSeq,
Expand Down
20 changes: 17 additions & 3 deletions runner/src/mill/runner/MillBuildBootstrap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ class MillBuildBootstrap(
threadCount: Option[Int],
targetsAndParams: Seq[String],
prevRunnerState: RunnerState,
logger: ColorLogger
logger: ColorLogger,
needBuildSc: Boolean
) {
import MillBuildBootstrap._

Expand Down Expand Up @@ -66,10 +67,23 @@ class MillBuildBootstrap(

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.
lazy 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))
if (needBuildSc) {
RunnerState(None, Nil, Some(msg))
} else {
state match {
case RunnerState(bootstrapModuleOpt, frames, Some(error)) =>
// Add a potential clue (missing build.sc) to the underlying error message
RunnerState(bootstrapModuleOpt, frames, Some(msg + "\n" + error))
case state => state
}
}
}
} else {
val parsedScriptFiles = FileImportGraph.parseBuildFiles(
Expand Down
22 changes: 21 additions & 1 deletion runner/src/mill/runner/MillMain.scala
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,8 @@ object MillMain {
threadCount = threadCount,
targetsAndParams = targetsAndParams,
prevRunnerState = prevState.getOrElse(stateCache),
logger = logger
logger = logger,
needBuildSc = needBuildSc(config)
).evaluate()
}
)
Expand Down Expand Up @@ -262,6 +263,25 @@ object MillMain {
logger
}

/**
* Determine, whether we need a `build.sc` or not.
*/
private def needBuildSc(config: MillCliConfig): Boolean = {
// Tasks, for which running Mill without an existing buildfile is allowed.
val noBuildFileTaskWhitelist = Seq(
"init",
"version",
"mill.scalalib.giter8.Giter8Module/init"
)
val targetsAndParams = config.leftoverArgs.value
val whitelistMatch =
targetsAndParams.nonEmpty && noBuildFileTaskWhitelist.exists(targetsAndParams.head == _)
// Has the user additional/extra imports
// (which could provide additional commands that could make sense without a build.sc)
val extraPlugins = config.imports.nonEmpty
!(whitelistMatch || extraPlugins)
}

def checkMillVersionFromFile(projectDir: os.Path, stderr: PrintStream) = {
Seq(
projectDir / ".config" / "mill-version",
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