-
-
Notifications
You must be signed in to change notification settings - Fork 353
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Overhaul build file management with new build.mill
/package.mill
format
#3426
Conversation
80ec89e
to
5b46280
Compare
build.mill
/package.mill
format
Going to merge this and proceed with the dogfooding, rebootstrapping, and cutting of RC1. There's definitely issues we'll discover, and further things we need to do. But that can happen during the RC period |
Have you considered something like This way most tools (e.g. |
That's a great idea! Otherwise, even the most basic syntax highlighter needs to know what Mill is. This happens already for sbt, but I see the value of hiding Scala to Java developers, but it is no doubt that the build file is a Scala file and to master it you need to know some Scala :) Gradle started out with |
I considered I think net-net, the cost of adding a new file extension is not that large. Sure, initially you get zero support and everything sucks, but most editors allow you to associate a file extension manually, and it's usually a trivial PR to each of the various editors or environments (e.g. Github linguist) to associate the new extension. The major thing that I think we need to be more careful about is the semantics. Teaching an editor a a file extension is trivial; teaching an editor custom file-type identification logic or scoping/identifier-resolution logic (which is what would be needed to properly support the old Overall, a new file extension seems big, but I bet in the next week or so we'll be able to send PRs to most of the major ones, and that's that. By the time 0.12.0-RC1 is even cut it should already be a non-issue. Whether we use |
I guess that's because You make it sound easy, but making a new file extension recognized is hard, esp. if there are already other file extensions for the same type. (I know many editors prefer to guide the user to use one standard instead of many, e.g While |
// Use Fastparse's Instrument API to identify top-level `object`s during a parse | ||
// and fish out the start/end indices and text for parts of the code that we need | ||
// to mangle and replace | ||
private class ObjectDataInstrument(scriptCode: String) extends fastparse.internal.Instrument { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CC @bishabosha this parsing logic may need to be ported to the Scala 3 port. Not sure how the Scala 3 import $ivy
parser works, but I assume you have some kind of AST, which may make things a lot easier than the visitor-style parsing I have to do here using ScalaParse
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, this is probably next for me
I think this equation is wrong. What I know, avoiding the |
This PR overhauls how build files are handled, especially in subfolders. The goal is to come up with an approach that is (1) scalable to large builds and (2) works intuitively with how people expect things to work and (3) plays well with IDEs (4) converges with the Scala language to reduce the special casing we have to do.
There's a bunch of hackiness in the implementation, but the end result is that when I load the updated
10-multi-file-build
project into IntelliJ, navigation around the multi-file project works seamlessly (after adding the.mill
file association):VSCode works as well, though it only understands
.scala
extensions and will likely need to be patched to work with.mill
This means that to begin with, all the IDE tooling around Mill just works, with the IDE being blissfully unaware of the nasty code transformations and other things Mill does. From that base, we can slowly look into removing boilerplate in an IDE/language-compliant way, and removing the backend hackiness while keeping the user experience mostly unchanged
Major user-facing changes:
build.sc
is nowbuild.mill
, sub-folders can define modules inpackage.mill
files. Helper libraries live in files likefoo/bar.mill
.sc
is overloaded to work with Ammonite/Scala-CLI scripts. That means IDEs like IntelliJ treat them specially, in ways that are incompatible with Mill (e.g. IDEs don't understand importing between script files)..sc
is still supported for migration/compatibility reasons. Editors don't currently associate.mill
with Mill Scala code, but it's a few clicks for the end user and a trivial PR to send to IntelliJ and Metals to support it.scala
as an extension could possibly work from a technical level, but there will still be user-facing confusion if we want Mill to be able to target non-Scala developers, and possibly technical confusion for tools that can't differentiate between Mill build files and application source filesAll
.mill
files need apackage
declaration at the top, e.g.build.mill
needspackage build
,foo/bar/qux.sc
needspackage build.foo.bar
. I left it optional for the rootbuild.mill
for migration purposesimport $file
, but they never were super robust, andimport $file
is itself a hack we've discussed getting rid ofNo more
import $file
: any files with.mill
(or.sc
) extensions adjacent to abuild.mill
orpackage.mill
are treated as Mill filesMajor internal changes:
We move the handling of
RootModule
from runtime reflection/classpath-scanning/custom-resolve-logic to code-generation logic that "unpacks" anyobject module
,object build
, orobject foo extends RootModule
into the enclosing wrapper classRootModule
s are handled uniformly from Scala code and from the command line, where previously Scala code would need to writebar.qux.module.mytask
suffix while the CLI would just writebar.qux.mytask
Replace the
package object
s previously generated for root modules with normalobject
s namedbuild
ormodule
(reflecting the file names)package object
s are on their way out in Scala 3 https://docs.scala-lang.org/scala3/reference/dropped-features/package-objects.html. No concrete timeline, but good to stop using them. They also are a common source of bugs around compilation, incremental compilation, etc. since even their present-day implementation isn't terribly robustbuild
asbuild.package
since it is now a normal objectGenerate
final def
aliases in the generatedobject
s in order to make code references and module discovery work.build
or.module
suffix, as well as allow theResolve.scala
logic to workpackage object
/object
s, which seemed pretty flakyLimitations:
Partially migrating subfolders out of a parent
build.sc
into a childmodule.sc
no longer works. The aliases we generate for subfoldermodule.sc
files always conflicts at compile time with any locally-defined modulesReferences to helper files e.g.
foo/bar.sc
withdef qux
would get referenced viabuild_.foo.bar.qux
is kind of awkward. I expect this to go away once we get Scala 3 support, and we can useexport
clauses to allow referencing them viabuild.foo.qux
, which is more in line with how Scala code normally works where different files in the same folder are all part of the same combined namespace.To work around
package object
weirdness e.g. needing to reference things viafoo.bar.package
, we build our own parallel object hierarchy via codegen and provide that to the user. This is awkward, but it looks similar enough people shouldn't notice it, and we can look into somehow fixing thepackage object
issues upstream in scala/scala3 in futureThe
package build
prefix is kind of verbose, but it's necessary so we have some way of reliably referencing other files by their fully qualified names. e.g. a file inutil/package.mill
can be referenced bybuild.util.*
, but cannot be referenced byutil.*
becauseimport mill._
pulls inmill.util
which shadows it.