Skip to content

Commit

Permalink
Replace import $file syntax with flat package namespacing (#4462)
Browse files Browse the repository at this point in the history
Fixes #4459
Fixes #3894

With this PR, all `.mill` files in a folder are in the same flat
`package` namespace, similar to how normal Scala files work. The
previous `import $file` syntax was inherited from Ammonite, and
unfortunately never took off across the Scala community. In effect this
makes `.mill` files behave almost exactly like `.scala` files, which is
part of the longer term goal of removing special handling for `.mill`
files.

The code generation for the helper files is not changed at all in this
PR, rather we just make use of Scala 3's `export` clauses to make them
available under the main `build.mill`/`package.mill` file as well

There is the caveat that the `package object`s in `.mill` files can be
referenced via `build.foo.bar` without the trailing `.package`.
Implementing this caveat is the cause of a lot of the hackiness in the
codegen, which will hopefully go away once the upstream language feature
lands scala/improvement-proposals#100

We leave the codegenned `import _root_.{build_ => $file}` in place so
`import $file` will continue to work to ease in the migration, but are
no longer the recommended way of referencing helper files
  • Loading branch information
lihaoyi authored Feb 4, 2025
1 parent 23563b4 commit 598aaea
Show file tree
Hide file tree
Showing 17 changed files with 50 additions and 103 deletions.
3 changes: 0 additions & 3 deletions docs/modules/ROOT/pages/large/multi-file-builds.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,5 @@ include::partial$example/large/multi/10-multi-file-builds.adoc[]

include::partial$example/large/multi/11-helper-files.adoc[]

== Legacy `.sc` extension

include::partial$example/large/multi/12-helper-files-sc.adoc[]


3 changes: 1 addition & 2 deletions example/large/multi/11-helper-files/build.mill
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
package build
import $packages._
import mill._, scalalib._
import $file.foo.versions
import $file.util.MyModule
import foo.versions

object `package` extends RootModule with MyModule {
def forkEnv = Map(
Expand Down
7 changes: 3 additions & 4 deletions example/large/multi/11-helper-files/foo/package.mill
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package build.foo
import mill._, scalalib._
import $file.util
import $file.foo.versions.myProjectVersion
object `package` extends RootModule with build_.util.MyModule {
import versions.myProjectVersion
object `package` extends RootModule with build.MyModule {
def forkEnv = Map(
"MY_SCALA_VERSION" -> util.myScalaVersion,
"MY_SCALA_VERSION" -> build.myScalaVersion,
"MY_PROJECT_VERSION" -> myProjectVersion
)
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package build
import $packages._
import mill._, scalalib._
import $file.foo.versions
import $file.util.MyModule

object `package` extends RootModule with MyModule {
def forkEnv = Map(
"MY_SCALA_VERSION" -> build.scalaVersion(),
"MY_PROJECT_VERSION" -> versions.myProjectVersion
"MY_PROJECT_VERSION" -> build.foo.myProjectVersion
)
}
///** See Also: util.mill.scala */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package build.foo
import mill._, scalalib._
object `package` extends RootModule with build.MyModule {
def forkEnv = Map(
"MY_SCALA_VERSION" -> build.myScalaVersion,
"MY_PROJECT_VERSION" -> myProjectVersion
)
}
35 changes: 0 additions & 35 deletions example/large/multi/12-helper-files-sc/build.sc

This file was deleted.

9 changes: 0 additions & 9 deletions example/large/multi/12-helper-files-sc/foo/package.sc

This file was deleted.

1 change: 0 additions & 1 deletion example/large/multi/12-helper-files-sc/foo/versions.sc

This file was deleted.

7 changes: 0 additions & 7 deletions example/large/multi/12-helper-files-sc/util.sc

This file was deleted.

This file was deleted.

This file was deleted.

6 changes: 0 additions & 6 deletions example/large/multi/13-helper-files-mill-scala/src/Main.scala

This file was deleted.

52 changes: 37 additions & 15 deletions runner/src/mill/runner/CodeGen.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ object CodeGen {
}
.distinct

val pkg = packageSegments.drop(1).dropRight(1)
val pkgSegments = packageSegments.drop(1).dropRight(1)

def pkgSelector0(pre: Option[String], s: Option[String]) =
(pre ++ pkg ++ s).map(backtickWrap).mkString(".")
(pre ++ pkgSegments ++ s).map(backtickWrap).mkString(".")
def pkgSelector2(s: Option[String]) = s"_root_.${pkgSelector0(Some(globalPackagePrefix), s)}"
val (childSels, childAliases0) = childNames
.map { c =>
Expand All @@ -67,7 +67,7 @@ object CodeGen {
}.unzip
val childAliases = childAliases0.mkString("\n")

val pkgLine = s"package ${pkgSelector0(Some(globalPackagePrefix), None)}"
val pkg = pkgSelector0(Some(globalPackagePrefix), None)

val aliasImports = Seq(
// `$file` as an alias for `build_` to make usage of `import $file` when importing
Expand All @@ -87,7 +87,7 @@ object CodeGen {

val parts =
if (!isBuildScript) {
s"""$pkgLine
s"""package $pkg
|$aliasImports
|object ${backtickWrap(scriptPath.last.split('.').head)} {
|$markerComment
Expand All @@ -103,11 +103,16 @@ object CodeGen {
scriptPath,
scriptFolderPath,
childAliases,
pkgLine,
pkg,
aliasImports,
scriptCode,
markerComment,
parser
parser,
scriptSources
.map(_.path)
.filter(_ != scriptPath)
.filter(p => (p / os.up) == (scriptPath / os.up))
.map(_.last.split('.').head)
)
}

Expand All @@ -124,19 +129,25 @@ object CodeGen {
scriptPath: os.Path,
scriptFolderPath: os.Path,
childAliases: String,
pkgLine: String,
pkg: String,
aliasImports: String,
scriptCode: String,
markerComment: String,
parser: MillScalaParser
parser: MillScalaParser,
siblingScripts: Seq[String]
) = {
val segments = scriptFolderPath.relativeTo(projectRoot).segments

val prelude = {
val exportSiblingScripts =
siblingScripts.map(s => s"export $pkg.${backtickWrap(s)}.*").mkString("\n")

val importSiblingScripts = siblingScripts
.map(s => s"import $pkg.${backtickWrap(s)}.*").mkString("\n")

val prelude =
s"""import MillMiscInfo._
|import _root_.mill.main.TokenReaders.given, _root_.mill.api.JsonFormatters.given
|""".stripMargin
}

val miscInfo =
if (segments.nonEmpty) subfolderMiscInfo(scriptFolderPath, segments)
Expand Down Expand Up @@ -196,23 +207,32 @@ object CodeGen {
newScriptCode = objectData.name.applyTo(newScriptCode, wrapperObjectName)
newScriptCode = objectData.obj.applyTo(newScriptCode, "abstract class")

s"""$pkgLine
s"""package $pkg
|$miscInfo
|$aliasImports
|$importSiblingScripts
|$prelude
|$markerComment
|$newScriptCode
|object $wrapperObjectName extends $wrapperObjectName {
| ${childAliases.linesWithSeparators.mkString(" ")}
| $exportSiblingScripts
| ${millDiscover(segments.nonEmpty)}
|}""".stripMargin

case None =>
s"""$pkgLine
s"""package $pkg
|$miscInfo
|$aliasImports
|$importSiblingScripts
|$prelude
|${topBuildHeader(segments, scriptFolderPath, millTopLevelProjectRoot, childAliases)}
|${topBuildHeader(
segments,
scriptFolderPath,
millTopLevelProjectRoot,
childAliases,
exportSiblingScripts
)}
|$markerComment
|$scriptCode
|}""".stripMargin
Expand Down Expand Up @@ -263,7 +283,8 @@ object CodeGen {
segments: Seq[String],
scriptFolderPath: os.Path,
millTopLevelProjectRoot: os.Path,
childAliases: String
childAliases: String,
exportSiblingScripts: String
): String = {
val extendsClause =
if (segments.nonEmpty) s"extends _root_.mill.main.SubfolderModule "
Expand All @@ -277,8 +298,9 @@ object CodeGen {
// path dependent types no longer match, e.g. for TokenReaders of custom types.
// perhaps we can patch mainargs to substitute prefixes when summoning TokenReaders?
// or, add an optional parameter to Discover.apply to substitute the outer class?
s"""object ${wrapperObjectName} extends $wrapperObjectName {
s"""object ${wrapperObjectName} extends $wrapperObjectName {
| ${childAliases.linesWithSeparators.mkString(" ")}
| $exportSiblingScripts
| ${millDiscover(segments.nonEmpty)}
|}
|abstract class $wrapperObjectName $extendsClause { this: $wrapperObjectName.type =>
Expand Down

0 comments on commit 598aaea

Please sign in to comment.