Raw evaluation time goes up, but overall startup time goes down.
On the sbt build, raw evaluation time goes from 1.8 s to 2.0 s
but overall startup from 12.2 s to 11.6 s. Warm evaluation
time goes from 0.25 to 0.5 s, but overall reload time is less
affected.
Presumably the reason for this is class loading and jit times.
The fix was made possible by the very helpful information provided by @retronym.
This commit does two key things:
1. changes the owner when splicing original trees into new trees
2. ensures the synthetic trees that get spliced into original trees do not need typechecking
Given this original source (from Defaults.scala):
...
lazy val sourceConfigPaths = Seq(
...
unmanagedSourceDirectories := Seq(scalaSource.value, javaSource.value),
...
)
...
After expansion of .value, this looks something like:
unmanagedSourceDirectories := Seq(
InputWrapper.wrapInit[File](scalaSource),
InputWrapper.wrapInit[File](javaSource)
)
where wrapInit is something like:
def wrapInit[T](a: Any): T
After expansion of := we have (approximately):
unmanagedSourceDirectories <<=
Instance.app( (scalaSource, javaSource) ) {
$p1: (File, File) =>
val $q4: File = $p1._1
val $q3: File = $p1._2
Seq($q3, $q4)
}
So,
a) `scalaSource` and `javaSource` are user trees that are spliced into a tuple constructor after being temporarily held in `InputWrapper.wrapInit`
b) the constructed tuple `(scalaSource, javaSource)` is passed as an argument to another method call (without going through a val or anything) and shouldn't need owner changing
c) the synthetic vals $q3 and $q4 need their owner properly set to the anonymous function
d) the references (Idents) $q3 and $q4 are spliced into the user tree `Seq(..., ...)` and their symbols need to be the Symbol for the referenced vals
e) generally, treeCopy needs to be used when substituting Trees in order to preserve attributes, like Types and Positions
changeOwner is called on the body `Seq($q3, $q4)` with the original owner sourceConfigPaths to be changed to the new anonymous function.
In this example, no owners are actually changed, but when the body contains vals or anonymous functions, they will.
An example of the compiler crash seen when the symbol of the references is not that of the vals:
symbol value $q3 does not exist in sbt.Defaults.sourceConfigPaths$lzycompute
at scala.reflect.internal.SymbolTable.abort(SymbolTable.scala:49)
at scala.tools.nsc.Global.abort(Global.scala:254)
at scala.tools.nsc.backend.icode.GenICode$ICodePhase.genLoadIdent$1(GenICode.scala:1038)
at scala.tools.nsc.backend.icode.GenICode$ICodePhase.scala$tools$nsc$backend$icode$GenICode$ICodePhase$$genLoad(GenICode.scala:1044)
at scala.tools.nsc.backend.icode.GenICode$ICodePhase$$anonfun$genLoadArguments$1.apply(GenICode.scala:1246)
at scala.tools.nsc.backend.icode.GenICode$ICodePhase$$anonfun$genLoadArguments$1.apply(GenICode.scala:1244)
...
Other problems with the synthetic tree when it is spliced under the original tree often result in type mismatches or some other compiler error that doesn't result in a crash.
If the owner is not changed correctly on the original tree that gets spliced under a synthetic tree, one way it can crash the compiler is:
java.lang.IllegalArgumentException: Could not find proxy for val $q23: java.io.File in List(value $q23, method apply, anonymous class $anonfun$globalCore$5, value globalCore, object Defaults, package sbt, package <root>) (currentOwner= value dir )
...
while compiling: /home/mark/code/sbt/main/src/main/scala/sbt/Defaults.scala
during phase: global=lambdalift, atPhase=constructors
...
last tree to typer: term $outer
symbol: value $outer (flags: <synthetic> <paramaccessor> <triedcooking> private[this])
symbol definition: private[this] val $outer: sbt.BuildCommon
tpe: <notype>
symbol owners: value $outer -> anonymous class $anonfun$87 -> value x$298 -> method derive -> class BuildCommon$class -> package sbt
context owners: value dir -> value globalCore -> object Defaults -> package sbt
...
The problem here is the difference between context owners and the proxy search chain.
As pointed out by @harrah in #705, both beginSource and endSource are
not used in sbt internally for anything meaningful.
We've discussed an option of deprecating those methods but since they
are not doing anything meaningful Mark prefers to have compile-time
error in case somebody implements or calls those methods. I agree with
that hence removal.
Equality is reference equality for classes that have lazy members
(currently Structure and ClassLike) and member equality for everything else.
This avoids the circularity issue due to lazy members.
Forces each class to be either abstract or final, to ensure that
the equals implementation is always correct.
Fixes toString to avoid infinite recursion.
* when mvn does a local 'install', it doesn't update the pom.xml last modified time if the pom.xml content hasn't changed
* an ivy.xml includes publicationDate, so an ivy.xml will always be touched even if the other content hasn't changed
* when Ivy checks if a snapshot is uptodate
+ it sees a SNAPSHOT, so it knows the module metadata and artifacts might change
+ it then checks the lastModified time of the metadata
+ if unchanged, it uses the cached information
+ if useOrigin is effectively false (either it is explicitly false or a resource is remote/isLocal=false),
this means that a new artifact won't be retrieved
* the Ivy IBiblioResolver
+ must be used for Maven repositories for proper behavior (no FileResolver, for example)
+ only returns URLResources, even for file: URLs
+ a FileResource is needed in combination with useOrigin to avoid copying artifacts from .m2/repository/
This commit fixes the above by setting a custom URLRepository on a constructed IBiblioResolver.
This URLRepository returns FileResources for file: URLs and standard URLResources for others.
The returned FileResource has isLocal=true and sbt sets useOrigin=true by default, so the artifacts
are used from the origin.
If it turns out a similar situation happens when mvn publishes to remote repositories, it is likely the fix for
that would be to figure out how to disable the lastModified check on the metadata and always download the metadata.
This would be slower, however.
The startup script should set sbt.cygwin=true if running from cygwin.
This will set the terminal type properly for JLine if not already set.
If sbt.cygwin=false or unset and os.name includes "windows", JAnsi is
downloaded by the launcher and installed on standard out/err.
The value for jline.terminal is transformed from explicit jline.X to
the basic types "windows", "unix", or "none". Now that sbt uses JLine
2.0, these types are understood by both sbt's JLine and Scala's.
Older Scala versions shaded the classes but not the terminal property
so both couldn't be configured with a class name at the same time.