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

Fix Windows launcher #791

Merged
merged 8 commits into from
Apr 13, 2020
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
58 changes: 28 additions & 30 deletions build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -620,27 +620,13 @@ object dev extends MillModule{
)
).distinct


// Pass dev.assembly VM options via file in Windows due to small max args limit
def windowsVmOptions(taskName: String, batch: os.Path, args: Seq[String])
(implicit ctx: mill.util.Ctx) = {
if (System.getProperty("java.specification.version").startsWith("1.")) {
throw new Error(s"$taskName in Windows is only supported using Java 9 or above")
}
val vmOptionsFile = T.ctx.dest / "mill.vmoptions"
T.ctx.log.info(s"Generated $vmOptionsFile; it should be kept in the same directory as $taskName's ${batch.last}")
os.write(vmOptionsFile, args.mkString("\r\n"))
}

def launcher = T{
val isWin = scala.util.Properties.isWin
val outputPath = T.ctx.dest / (if (isWin) "run.bat" else "run")

os.write(outputPath, prependShellScript())

if (isWin) {
windowsVmOptions("dev.launcher", outputPath, forkArgs())
} else {
if (!isWin) {
os.perms.set(outputPath, "rwxrwxrwx")
}
PathRef(outputPath)
Expand All @@ -650,30 +636,43 @@ object dev extends MillModule{
val isWin = scala.util.Properties.isWin
val millPath = T.ctx.dest / (if (isWin) "mill.bat" else "mill")
os.move(super.assembly().path, millPath)
if (isWin) windowsVmOptions("dev.launcher", millPath, forkArgs())
PathRef(millPath)
}

def prependShellScript = T{
val (millArgs, otherArgs) = forkArgs().partition(arg => arg.startsWith("-DMILL") && !arg.startsWith("-DMILL_VERSION"))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We pass the MILL_SOMETHING properties via file, so that Windows doesn't complain!
Everything except MILL_VERSION because it's used in lots of places.

// Pass Mill options via file, due to small max args limit in Windows
val vmOptionsFile = T.ctx.dest / "mill.properties"
val millOptionsContent = millArgs.map(_.drop(2).replace("\\", "/")).mkString("\r\n") // drop -D prefix, replace \ with /
os.write(vmOptionsFile, millOptionsContent)
val jvmArgs = otherArgs ++ List(s"-DMILL_OPTIONS_PATH=$vmOptionsFile")
val classpath = runClasspath().map(_.path.toString)
val args = forkArgs()
val (shellArgs, cmdArgs) =
if (!scala.util.Properties.isWin) (
args,
args
)
else (
Seq("""-XX:VMOptionsFile="$( dirname "$0" )"/mill.vmoptions"""),
Seq("""-XX:VMOptionsFile=%~dp0\mill.vmoptions""")
)
launcherScript(
shellArgs,
cmdArgs,
jvmArgs,
jvmArgs,
classpath,
classpath
Agg(pathingJar().path.toString) // TODO not working yet on Windows! see #791
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On Windows, the classpath is only the "pathingJar", it contains all the dependencies.
It works in batch mode, interactive needs bit more debugging.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lihaoyi this only relates to the dev.assembly version..

)
}

def pathingJar = T{
// see http://todayguesswhat.blogspot.com/2011/03/jar-manifestmf-class-path-referencing.html
// for more detailed explanation
val isWin = scala.util.Properties.isWin
val classpath = runClasspath().map{ pathRef =>
val path = if (isWin) "/" + pathRef.path.toString.replace("\\", "/")
else pathRef.path.toString
if (path.endsWith(".jar")) path
else path + "/"
}.mkString(" ")
val manifestEntries = Map[String,String](
java.util.jar.Attributes.Name.MANIFEST_VERSION.toString -> "1.0",
"Created-By" -> "Scala mill",
"Class-Path" -> classpath
)
mill.modules.Jvm.createJar(Agg(), mill.modules.Jvm.JarManifest(manifestEntries))
}

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")
Expand All @@ -691,7 +690,6 @@ object dev extends MillModule{
}
}


def assembly = T{

val version = publishVersion()._2
Expand Down
2 changes: 1 addition & 1 deletion contrib/bsp/src/mill/contrib/BSP.scala
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ object BSP extends ExternalModule {

// creates a Json with the BSP connection details
def createBspConnectionJson(): String = {
val millPath = scala.sys.props.get("MILL_CLASSPATH").getOrElse(System.getProperty("MILL_CLASSPATH"))
val millPath = Option(mill.modules.Util.millProperty("MILL_CLASSPATH")).getOrElse(System.getProperty("MILL_CLASSPATH"))
val millVersion = scala.sys.props.get("MILL_VERSION").getOrElse(System.getProperty("MILL_VERSION"))
write(BspConfigJson("mill-bsp",
List(whichJava,
Expand Down
47 changes: 29 additions & 18 deletions main/client/src/mill/main/client/MillClientMain.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,43 @@ public class MillClientMain {
public static final int ExitServerCodeWhenVersionMismatch() { return 101; }

static void initServer(String lockBase, boolean setJnaNoSys) throws IOException,URISyntaxException{
String[] selfJars = System.getProperty("MILL_CLASSPATH").split(",");

List<String> l = new ArrayList<>();

String selfJars = "";
List<String> vmOptions = new ArrayList<>();
l.add(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java");
final Properties props = System.getProperties();
for(final String k: props.stringPropertyNames()){
String millOptionsPath = System.getProperty("MILL_OPTIONS_PATH");
if(millOptionsPath != null) {
// read MILL_CLASSPATH from file MILL_OPTIONS_PATH
Properties millProps = new Properties();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On Windows we read the classpath from the MILL_OPTIONS_PATH file instead of env var..

millProps.load(new FileInputStream(millOptionsPath));
for(final String k: millProps.stringPropertyNames()){
String propValue = millProps.getProperty(k);
if("MILL_CLASSPATH".equals(k)){
selfJars = propValue;
}
}
} else {
// read MILL_CLASSPATH from file sys props
selfJars = System.getProperty("MILL_CLASSPATH");
}

final Properties sysProps = System.getProperties();
for(final String k: sysProps.stringPropertyNames()){
if (k.startsWith("MILL_") && !"MILL_CLASSPATH".equals(k)) {
vmOptions.add("-D" + k + "=" + props.getProperty(k));
vmOptions.add("-D" + k + "=" + sysProps.getProperty(k));
}
}
if(selfJars == null || selfJars.trim().isEmpty()) {
throw new RuntimeException("MILL_CLASSPATH is empty!");
}
if (setJnaNoSys) {
vmOptions.add("-Djna.nosys=true");
}
if(!Util.isWindows){
l.addAll(vmOptions);
} else {
final File vmOptionsFile = new File(lockBase, "vmoptions");
try (PrintWriter out = new PrintWriter(vmOptionsFile)) {
for(String opt: vmOptions)
out.println(opt);
}
l.add("-XX:VMOptionsFile=" + vmOptionsFile.getCanonicalPath());
}

List<String> l = new ArrayList<>();
l.add(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java");
l.addAll(vmOptions);
l.add("-cp");
l.add(String.join(File.pathSeparator, selfJars));
l.add(String.join(File.pathSeparator, selfJars.split(",")));
l.add("mill.main.MillServerMain");
l.add(lockBase);

Expand Down
2 changes: 2 additions & 0 deletions main/src/modules/Jvm.scala
Original file line number Diff line number Diff line change
Expand Up @@ -347,8 +347,10 @@ object Jvm {
Seq(
"",
":BOF",
"setlocal",
"@echo off",
cmdCommands.replaceAll("\r\n|\n", "\r\n"),
"endlocal",
"exit /B %errorlevel%",
""
).mkString("\r\n")
Expand Down
19 changes: 17 additions & 2 deletions main/src/modules/Util.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ import mill.api.Loose


object Util {

private val LongMillProps = new java.util.Properties()

{
val millOptionsPath = sys.props("MILL_OPTIONS_PATH")
if(millOptionsPath != null)
LongMillProps.load(new java.io.FileInputStream(millOptionsPath))
}

def cleanupScaladoc(v: String) = {
v.linesIterator.map(
_.dropWhile(_.isWhitespace)
Expand Down Expand Up @@ -55,7 +64,7 @@ object Util {
repositories: Seq[Repository],
resolveFilter: os.Path => Boolean = _ => true,
artifactSuffix: String = "_2.12") = {
val localPath = sys.props(key)
val localPath = millProperty(key)
if (localPath != null) {
mill.api.Result.Success(
mill.api.Loose.Agg.from(localPath.split(',').map(p => PathRef(os.Path(p), quick = true)))
Expand All @@ -66,11 +75,17 @@ object Util {
Seq(
coursier.Dependency(
coursier.Module(coursier.Organization("com.lihaoyi"), coursier.ModuleName(artifact + artifactSuffix)),
sys.props("MILL_VERSION")
millProperty("MILL_VERSION")
)
),
Nil
).map(_.filter(x => resolveFilter(x.path)))
}
}

def millProperty(key: String): String = {
val sysPropValue = sys.props(key)
if(sysPropValue != null) sysPropValue // system property has priority
else LongMillProps.getProperty(key)
}
}
2 changes: 1 addition & 1 deletion scalalib/src/GenIdeaImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ case class GenIdeaImpl(evaluator: Evaluator,

val buildLibraryPaths: immutable.Seq[Path] =
if (!fetchMillModules) Nil
else sys.props.get("MILL_BUILD_LIBRARIES") match {
else Option(mill.modules.Util.millProperty("MILL_BUILD_LIBRARIES")) match {
case Some(found) => found.split(',').map(os.Path(_)).distinct.toList
case None =>
val repos = modules.foldLeft(Set.empty[Repository]) { _ ++ _._2.repositories } ++ Set(LocalRepositories.ivy2Local, Repositories.central)
Expand Down