Logic around bootstrapping Mill, creating a MillBuildRootModule.BootstrapModule and compiling builds/meta-builds and classloading their RootModules so we can evaluate the requested tasks on the RootModule representing the user's build.mill file.
When Mill is run in daemon mode or with --watch, bootstrap state is cached in-memory across evaluations. The daemon-wide reusable bootstrap metadata lives in RunnerSharedState, while each launcher keeps its own evaluators and retained read leases in RunnerLauncherState.
When a subsequent evaluation happens, each meta-level attempts to re-use its corresponding cached frame to avoid unnecessary classloader recreation, worker churn, or re-evaluation. This should remain transparent to users while improving performance.
Handles the final evaluation of the user-provided tasks. Since there are no further levels to evaluate, we do not need to save a scriptImportGraph, classloader, or runClasspath.
Handles the final evaluation of the user-provided tasks. Since there are no further levels to evaluate, we do not need to save a scriptImportGraph, classloader, or runClasspath.
Handles the compilation of build.mill or one of the meta-builds. These cases all only need us to run evaluate runClasspath and scriptImportGraph to instantiate their classloader/RootModule to feed into the next level's Evaluator.
Handles the compilation of build.mill or one of the meta-builds. These cases all only need us to run evaluate runClasspath and scriptImportGraph to instantiate their classloader/RootModule to feed into the next level's Evaluator.
Note that if the runClasspath doesn't change, we re-use the previous classloader, saving us from having to re-instantiate it and for the code inside to be re-JITed