Skip to content

Commit

Permalink
Upstream Mill's dist.launcherScript into `mill.util.Jvm.launcherUni…
Browse files Browse the repository at this point in the history
…versalScript` (#4333)

Fixes #4302

We largely copy the existing implementation `dist/package.mill`. Once we
rebootstrap, we can cut over to the new `Jvm.*` implementation and
remove the version in `dist/`. Added some unit tests to exercise the
`JAVA_HOME`/`JAVA_OPTS` handling
  • Loading branch information
lihaoyi authored Jan 16, 2025
1 parent 93ec017 commit 8f256fd
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 15 deletions.
11 changes: 6 additions & 5 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Uncommment this to replace the rest of the file when you want to debug stuff in CI

#
#
#name: Run Debug
#
Expand All @@ -16,19 +16,20 @@
# - uses: actions/checkout@v4
# with: { fetch-depth: 1 }
#
# - run: ./mill 'example.scalalib.basic[1-simple].packaged.fork.test'
# - run: ./mill scalalib.test mill.scalalib.LauncherTests.launcher
# env:
# COURSIER_ARCHIVE_CACHE: "C:/coursier-arc"


name: Run Tests
#

# We run full CI on push builds to main and on all pull requests
#
# To maximize bug-catching changes while keeping CI times reasonable, we run
# all tests on Linux, scattered between Java 11/17, except for one job run
# on MacOS instead and a subset of jobs also run on windows


name: Run Tests

on:
push:
pull_request:
Expand Down
50 changes: 41 additions & 9 deletions main/util/src/mill/util/Jvm.scala
Original file line number Diff line number Diff line change
Expand Up @@ -565,25 +565,57 @@ object Jvm extends CoursierSupport {
).filterNot(_.isEmpty).mkString("\n")
}

@deprecated("Use the other override instead", "0.12.6")
def launcherUniversalScript(
mainClass: String,
shellClassPath: Agg[String],
cmdClassPath: Agg[String],
jvmArgs: Seq[String],
shebang: Boolean = false
shebang: Boolean
): String = {
launcherUniversalScript(mainClass, shellClassPath, cmdClassPath, jvmArgs, shebang, Nil, Nil)
}

def launcherUniversalScript(
mainClass: String,
shellClassPath: Agg[String],
cmdClassPath: Agg[String],
jvmArgs: Seq[String],
shebang: Boolean = false,
shellJvmArgs: Seq[String] = Nil,
cmdJvmArgs: Seq[String] = Nil
): String = {

universalScript(
shellCommands =
s"""exec java ${jvmArgs.mkString(" ")} $$JAVA_OPTS -cp "${shellClassPath.iterator.mkString(
":"
)}" '$mainClass' "$$@"""",
cmdCommands =
s"""java ${jvmArgs.mkString(" ")} %JAVA_OPTS% -cp "${cmdClassPath.iterator.mkString(
";"
)}" $mainClass %*""",
shellCommands = {
val jvmArgsStr = (jvmArgs ++ shellJvmArgs).mkString(" ")
val classpathStr = shellClassPath.mkString(":")

s"""if [ -z "$$JAVA_HOME" ] ; then
| JAVACMD="java"
|else
| JAVACMD="$$JAVA_HOME/bin/java"
|fi
|
|exec "$$JAVACMD" $jvmArgsStr $$JAVA_OPTS -cp "$classpathStr" $mainClass "$$@"
|""".stripMargin
},
cmdCommands = {
val jvmArgsStr = (jvmArgs ++ cmdJvmArgs).mkString(" ")
val classpathStr = cmdClassPath.mkString(";")
s"""setlocal EnableDelayedExpansion
|set "JAVACMD=java.exe"
|if not "%JAVA_HOME%"=="" set "JAVACMD=%JAVA_HOME%\\bin\\java.exe"
|
|"%JAVACMD%" $jvmArgsStr %JAVA_OPTS% -cp "$classpathStr" $mainClass %*
|
|endlocal
|""".stripMargin
},
shebang = shebang
)
}

def createLauncher(mainClass: String, classPath: Agg[os.Path], jvmArgs: Seq[String])(implicit
ctx: Ctx.Dest
): PathRef = {
Expand Down
21 changes: 20 additions & 1 deletion readme.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ https://www.patreon.com/lihaoyi[image:https://img.shields.io/badge/patreon-spons
Mill is a fast JVM build tool that supports Java and Scala. Mill aims to make your
project’s build process performant, maintainable, and flexible.

== Documentation

If you want to use Mill in your own projects, check out our documentation:

Expand All @@ -51,6 +50,9 @@ If you use Mill and like it, you will probably enjoy the following book by the A

* https://www.handsonscala.com/[_Hands-on Scala Programming_]
== Developer Documentation

The remainder of this readme is developer-documentation targeted at people who wish to work
on Mill's own codebase. The developer docs assume you have read through the user-facing
documentation linked above. It's also worth spending a few minutes reading the following
Expand Down Expand Up @@ -281,6 +283,23 @@ To run all autofixes and autoformatters:
./mill __.fix + mill.javalib.palantirformat.PalantirFormatModule/ + mill.scalalib.scalafmt.ScalafmtModule/ + mill.kotlinlib.ktlint.KtlintModule/
```

== Continuous Integration & Testing

* Mill's pull-request validation runs with
https://mill-build.org/mill/large/selective-execution.html[Selective Test Execution]
enabled; this automatically selects the tests to run based on the code or build configuration
that changed in that PR. To disable this, you can label your PR with `run-all-tests`, which
will run all tests on a PR regardless of what code was changed

* Mill tests draft PRs _on contributor forks_ of the repository, so please make sure Github
Actions are enabled on your fork. Once you are happy with your draft, mark it `ready_for_review`
and it will run CI on Mill's repository before merging

* If you need to debug things in CI, you can comment/uncomment the two sections of
`.github/workflows/run-tests.yml` in order to skip the main CI jobs and only run the command(s)
you need, on the OS you want to test on. This can greatly speed up the debugging process
compared to running the full suite every time you make a change.

== Project Layout

The Mill project is organized roughly as follows:
Expand Down
7 changes: 7 additions & 0 deletions scalalib/test/resources/launcher/src/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package launcher;
public class Main {
public static void main(String[] args) {
System.out.println("test.property " + System.getProperty("test.property"));
System.out.println("java.home " + System.getProperty("java.home"));
}
}
56 changes: 56 additions & 0 deletions scalalib/test/src/mill/scalalib/LauncherTests.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package mill.scalalib

import mill.testkit.{TestBaseModule, UnitTester}
import utest._

object LauncherTests extends TestSuite {

val customJavaVersion = "19.0.2"
object HelloJava extends TestBaseModule with JavaModule {
object ZincWorkerJava extends ZincWorkerModule {
def jvmId = s"temurin:$customJavaVersion"
}

def javacOptions = Seq("-target", "1.8", "-source", "1.8")
}

val resourcePath = os.Path(sys.env("MILL_TEST_RESOURCE_DIR")) / "launcher"

def tests: Tests = Tests {
def check(executableTask: mill.define.Target[mill.api.PathRef], copyBat: Boolean = false) = {
val eval = UnitTester(HelloJava, resourcePath)

val Right(result1) = eval.apply(executableTask)

val executable =
if (mill.util.Util.windowsPlatform && copyBat) {
val prev = result1.value.path
val next = prev / ".." / s"${prev.baseName}.bat"
os.copy(prev, next)
next
} else result1.value.path

val text = os.call(executable).out.text()
assert(text.contains("test.property null"))
assert(text.contains("java.home"))
assert(!text.contains(customJavaVersion))

val text2 = os
.call(executable, env = Map("JAVA_OPTS" -> "-Dtest.property=123"))
.out.text()
assert(text2.contains("test.property 123"))
assert(!text2.contains(customJavaVersion))
val Right(javaHome) = eval.apply(HelloJava.ZincWorkerJava.javaHome)

val text3 = os
.call(executable, env = Map("JAVA_HOME" -> javaHome.value.get.path.toString))
.out.text()
assert(text3.contains("java.home"))
assert(text3.contains(customJavaVersion))
}

test("launcher") - check(HelloJava.launcher)
// Windows requires that you copy the `.jar` file to a `.bat` extension before running it
test("assembly") - check(HelloJava.assembly, copyBat = true)
}
}

0 comments on commit 8f256fd

Please sign in to comment.