Skip to content
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

Universal Main #1729

Merged
merged 5 commits into from
Feb 5, 2022
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
23 changes: 20 additions & 3 deletions build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ object main extends MillModule {
s"""
|package mill
|
|/** Generated by mill. */
|object BuildInfo {
| /** Scala version used to compile mill core. */
| val scalaVersion = "$scalaVersion"
Expand Down Expand Up @@ -341,6 +342,21 @@ object main extends MillModule {
override def ivyDeps = Agg(
Deps.ipcsocketExcludingJna
)
def generatedBuildInfo = T{
val dest = T.dest
val code =
s"""package mill.main.client;
|
|/** Generated by mill. */
|public class BuildInfo {
| /** Mill version. */
| public static String millVersion() { return "${millVersion()}"; }
|}
|""".stripMargin
os.write(dest / "mill" / "main" / "client" / "BuildInfo.java", code, createFolders = true)
Seq(PathRef(dest))
}
override def generatedSources: T[Seq[PathRef]] = super.generatedSources() ++ generatedBuildInfo()
object test extends Tests with TestModule.Junit4 {
override def ivyDeps = Agg(Deps.junitInterface, Deps.lambdaTest)
}
Expand Down Expand Up @@ -841,7 +857,7 @@ def launcherScript(
shellClassPath: Agg[String],
cmdClassPath: Agg[String]
) = {
val millMainClass = "mill.MillMain"
val millMainClass = "mill.main.client.MillClientMain"
val millClientMainClass = "mill.main.client.MillClientMain"

mill.modules.Jvm.universalScript(
Expand Down Expand Up @@ -875,6 +891,7 @@ def launcherScript(
| "-X"*) mill_jvm_opts="$${mill_jvm_opts} $$line"
| esac
| done <"$$mill_jvm_opts_file"
| mill_jvm_opts="$${mill_jvm_opts} -Dmill.jvm_opts_applied=true"
| fi
|}
|
Expand Down Expand Up @@ -1023,7 +1040,7 @@ object dev extends MillModule {

def run(args: String*) = T.command {
args match {
case Nil => mill.eval.Result.Failure("Need to pass in cwd as first argument to dev.run")
case Nil => mill.api.Result.Failure("Need to pass in cwd as first argument to dev.run")
case wd0 +: rest =>
val wd = os.Path(wd0, os.pwd)
os.makeDir.all(wd)
Expand All @@ -1032,7 +1049,7 @@ object dev extends MillModule {
forkEnv(),
workingDir = wd
)
mill.eval.Result.Success(())
mill.api.Result.Success(())
}

}
Expand Down
79 changes: 79 additions & 0 deletions main/client/src/mill/main/client/IsolatedMillMainLoader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package mill.main.client;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

class IsolatedMillMainLoader {

public static class LoadResult {

public final Optional<Method> millMainMethod;
public final boolean canLoad;
public final long loadTime;

public LoadResult(Optional<Method> millMainMethod, final long loadTime) {
this.millMainMethod = millMainMethod;
this.canLoad = millMainMethod.isPresent();
this.loadTime = loadTime;
}
}

private static Optional<LoadResult> canLoad = Optional.empty();

public static LoadResult load() {
if (canLoad.isPresent()) {
return canLoad.get();
} else {
long startTime = System.currentTimeMillis();
Optional<Method> millMainMethod = Optional.empty();
try {
Class<?> millMainClass = IsolatedMillMainLoader.class.getClassLoader().loadClass("mill.MillMain");
Method mainMethod = millMainClass.getMethod("main", String[].class);
millMainMethod = Optional.of(mainMethod);
} catch (ClassNotFoundException | NoSuchMethodException e) {
millMainMethod = Optional.empty();
}

long loadTime = System.currentTimeMillis() - startTime;
LoadResult result = new LoadResult(millMainMethod, loadTime);
canLoad = Optional.of(result);
return result;
}
}

public static void runMain(String[] args) throws Exception {
LoadResult loadResult = load();
if (loadResult.millMainMethod.isPresent()) {
if (!MillEnv.millJvmOptsAlreadyApplied() && MillEnv.millJvmOptsFile().exists()) {
System.err.println("Launching Mill as sub-process ...");
int exitVal = launchMillAsSubProcess(args);
System.exit(exitVal);
} else {
// launch mill in-process
// it will call System.exit for us
Method mainMethod = loadResult.millMainMethod.get();
mainMethod.invoke(null, new Object[]{args});
}
} else {
throw new RuntimeException("Cannot load mill.MillMain class");
}
}

private static int launchMillAsSubProcess(String[] args) throws Exception {
boolean setJnaNoSys = System.getProperty("jna.nosys") == null;

List<String> l = new ArrayList<>();
l.addAll(MillEnv.millLaunchJvmCommand(setJnaNoSys));
l.add("mill.MillMain");
l.addAll(Arrays.asList(args));

Process running = new ProcessBuilder()
.command(l)
.inheritIO()
.start();
return running.waitFor();
}
}
Loading