Skip to content

Commit ebb97c4

Browse files
Merge pull request #125 from alexarchambault/multi-release-jar
Use Java FFI / FFM stuff on Java >= 22
2 parents 9ec7f39 + d0efaba commit ebb97c4

File tree

7 files changed

+540
-32
lines changed

7 files changed

+540
-32
lines changed

build.sc

Lines changed: 51 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import java.util.Locale
1111

1212
import scala.util.Properties
1313

14-
trait WindowsAnsiPublishModule extends PublishModule with Mima {
14+
trait WindowsAnsiPublishModule extends WindowsAnsiJavaModule with PublishModule with Mima {
1515
def pomSettings = PomSettings(
1616
description = artifactName(),
1717
organization = "io.github.alexarchambault.native-terminal",
@@ -43,54 +43,56 @@ trait WindowsAnsiPublishModule extends PublishModule with Mima {
4343
else value
4444
}
4545

46+
def mimaPreviousVersions: T[Seq[String]] = T.input {
47+
val current = os.proc("git", "describe", "--tags", "--match", "v*")
48+
.call()
49+
.out.trim()
50+
os.proc("git", "tag", "-l")
51+
.call()
52+
.out.lines()
53+
.filter(_ != current)
54+
.filter(_.startsWith("v"))
55+
.filter(!_.contains("-"))
56+
.map(_.stripPrefix("v"))
57+
.map(coursier.core.Version(_))
58+
.sorted
59+
.map(_.repr)
60+
}
61+
}
62+
63+
trait WindowsAnsiJavaModule extends JavaModule {
64+
def jvmRelease: T[String] = Task.Input("8")
4665
private def isArm64 =
4766
Option(System.getProperty("os.arch")).map(_.toLowerCase(Locale.ROOT)) match {
4867
case Some("aarch64" | "arm64") => true
4968
case _ => false
5069
}
5170
def javacSystemJvmId = T {
52-
if (Properties.isMac && isArm64) "zulu:8"
53-
else "adoptium:8"
71+
if (Properties.isMac && isArm64) s"zulu:${jvmRelease()}"
72+
else if (Properties.isWin && isArm64)
73+
if (jvmRelease() == "8") "https://github.com/adoptium/temurin8-binaries/releases/download/jdk8u442-b06/OpenJDK8U-jdk_x64_windows_hotspot_8u442b06.zip"
74+
else s"liberica:${jvmRelease()}"
75+
else s"adoptium:${jvmRelease()}"
5476
}
5577
def javacSystemJvm = T.source {
56-
val output = os.proc("cs", "java-home", "--jvm", javacSystemJvmId())
57-
.call(cwd = T.workspace)
58-
.out.trim()
59-
val javaHome = os.Path(output)
60-
assert(os.isDir(javaHome))
78+
val javaHome = os.Path(coursierapi.JvmManager.create().get(javacSystemJvmId()), os.pwd)
6179
PathRef(javaHome, quick = true)
6280
}
63-
// adds options equivalent to --release 8 + allowing access to unsupported JDK APIs
81+
// adds options equivalent to --release ${jvmRelease()} + allowing access to unsupported JDK APIs
6482
// (no more straightforward options to achieve that AFAIK)
65-
def maybeJdk8JavacOpt = T {
83+
def maybeJdkReleaseJavacOpt = T {
6684
val javaHome = javacSystemJvm().path
6785
val rtJar = javaHome / "jre/lib/rt.jar"
6886
val hasModules = os.isDir(javaHome / "jmods")
6987
val hasRtJar = os.isFile(rtJar)
7088
assert(hasModules || hasRtJar)
7189
if (hasModules)
72-
Seq("--system", javaHome.toString)
90+
Seq("--release", jvmRelease()) // FIXME javacSystemJvm is unused here
7391
else
74-
Seq("-source", "8", "-target", "8", "-bootclasspath", rtJar.toString)
92+
Seq("-source", jvmRelease(), "-target", jvmRelease(), "-bootclasspath", rtJar.toString)
7593
}
7694
def javacOptions = T {
77-
super.javacOptions() ++ maybeJdk8JavacOpt()
78-
}
79-
80-
def mimaPreviousVersions: T[Seq[String]] = T.input {
81-
val current = os.proc("git", "describe", "--tags", "--match", "v*")
82-
.call()
83-
.out.trim()
84-
os.proc("git", "tag", "-l")
85-
.call()
86-
.out.lines()
87-
.filter(_ != current)
88-
.filter(_.startsWith("v"))
89-
.filter(!_.contains("-"))
90-
.map(_.stripPrefix("v"))
91-
.map(coursier.core.Version(_))
92-
.sorted
93-
.map(_.repr)
95+
super.javacOptions() ++ maybeJdkReleaseJavacOpt()
9496
}
9597
}
9698

@@ -100,6 +102,26 @@ object native extends JavaModule with WindowsAnsiPublishModule {
100102
ivy"io.github.alexarchambault:is-terminal:0.1.2",
101103
ivy"org.jline:jline-native:3.29.0"
102104
)
105+
106+
def jdk22ClassesResources = T {
107+
val destDir = T.dest / "META-INF/versions/22"
108+
os.makeDir.all(destDir)
109+
for (elem <- os.list(jdk22.compile().classes.path))
110+
os.copy(elem, destDir / elem.last)
111+
PathRef(T.dest)
112+
}
113+
114+
def resources = T {
115+
T.sources(Seq(PathRef(millSourcePath / "resources")) ++ Seq(jdk22ClassesResources()))
116+
}
117+
def manifest = T {
118+
super.manifest().add("Multi-Release" -> "true")
119+
}
120+
121+
object jdk22 extends WindowsAnsiJavaModule {
122+
def jvmRelease: T[String] = Task.Input("22")
123+
def moduleDeps = Seq(native)
124+
}
103125
}
104126

105127
object `native-graalvm` extends JavaModule with WindowsAnsiPublishModule {
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// Initially adapted from https://github.com/jline/jline3/blob/114e9a8f86102245ed9e2e642603f97e11ac962b/terminal-ffm/src/main/java/org/jline/terminal/impl/ffm/CLibrary.java
2+
3+
/*
4+
* Copyright (c) 2022-2023, the original author(s).
5+
*
6+
* This software is distributable under the BSD license. See the terms of the
7+
* BSD license in the documentation provided with this software.
8+
*
9+
* https://opensource.org/licenses/BSD-3-Clause
10+
*/
11+
package io.github.alexarchambault.nativeterm.internal;
12+
13+
import java.lang.foreign.*;
14+
import java.lang.invoke.MethodHandle;
15+
import java.lang.invoke.MethodHandles;
16+
import java.lang.invoke.VarHandle;
17+
import java.lang.foreign.MemoryLayout.PathElement;
18+
19+
import io.github.alexarchambault.nativeterm.TerminalSize;
20+
21+
@SuppressWarnings("restricted")
22+
class CLibrary {
23+
24+
static VarHandle lookupVarHandle(PathElement... element) {
25+
VarHandle h = winsize.LAYOUT.varHandle(element);
26+
27+
// the last parameter of the VarHandle is additional offset, hardcode zero:
28+
h = MethodHandles.insertCoordinates(h, h.coordinateTypes().size() - 1, 0L);
29+
30+
return h;
31+
}
32+
33+
// Window sizes.
34+
// @see <a href="http://man7.org/linux/man-pages/man4/tty_ioctl.4.html">IOCTL_TTY(2) man-page</a>
35+
static class winsize {
36+
static final GroupLayout LAYOUT;
37+
private static final VarHandle ws_col;
38+
private static final VarHandle ws_row;
39+
40+
static {
41+
LAYOUT = MemoryLayout.structLayout(
42+
ValueLayout.JAVA_SHORT.withName("ws_row"),
43+
ValueLayout.JAVA_SHORT.withName("ws_col"),
44+
ValueLayout.JAVA_SHORT,
45+
ValueLayout.JAVA_SHORT);
46+
ws_row = lookupVarHandle(MemoryLayout.PathElement.groupElement("ws_row"));
47+
ws_col = lookupVarHandle(MemoryLayout.PathElement.groupElement("ws_col"));
48+
}
49+
50+
private final java.lang.foreign.MemorySegment seg;
51+
52+
winsize() {
53+
seg = java.lang.foreign.Arena.ofAuto().allocate(LAYOUT);
54+
}
55+
56+
java.lang.foreign.MemorySegment segment() {
57+
return seg;
58+
}
59+
60+
short ws_col() {
61+
return (short) ws_col.get(seg);
62+
}
63+
64+
short ws_row() {
65+
return (short) ws_row.get(seg);
66+
}
67+
}
68+
69+
static MethodHandle ioctl;
70+
static MethodHandle isatty;
71+
static MethodHandle tcsetattr;
72+
static MethodHandle tcgetattr;
73+
static MethodHandle ttyname_r;
74+
75+
static {
76+
// methods
77+
Linker linker = Linker.nativeLinker();
78+
SymbolLookup lookup = SymbolLookup.loaderLookup().or(linker.defaultLookup());
79+
// https://man7.org/linux/man-pages/man2/ioctl.2.html
80+
ioctl = linker.downcallHandle(
81+
lookup.find("ioctl").get(),
82+
FunctionDescriptor.of(
83+
ValueLayout.JAVA_INT, ValueLayout.JAVA_INT, ValueLayout.JAVA_LONG, ValueLayout.ADDRESS),
84+
Linker.Option.firstVariadicArg(2));
85+
// https://www.man7.org/linux/man-pages/man3/isatty.3.html
86+
isatty = linker.downcallHandle(
87+
lookup.find("isatty").get(), FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.JAVA_INT));
88+
// https://man7.org/linux/man-pages/man3/tcsetattr.3p.html
89+
tcsetattr = linker.downcallHandle(
90+
lookup.find("tcsetattr").get(),
91+
FunctionDescriptor.of(
92+
ValueLayout.JAVA_INT, ValueLayout.JAVA_INT, ValueLayout.JAVA_INT, ValueLayout.ADDRESS));
93+
// https://man7.org/linux/man-pages/man3/tcgetattr.3p.html
94+
tcgetattr = linker.downcallHandle(
95+
lookup.find("tcgetattr").get(),
96+
FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.JAVA_INT, ValueLayout.ADDRESS));
97+
// https://man7.org/linux/man-pages/man3/ttyname.3.html
98+
ttyname_r = linker.downcallHandle(
99+
lookup.find("ttyname_r").get(),
100+
FunctionDescriptor.of(
101+
ValueLayout.JAVA_INT, ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.JAVA_LONG));
102+
}
103+
104+
static TerminalSize getTerminalSize(int fd) {
105+
try {
106+
winsize ws = new winsize();
107+
int res = (int) ioctl.invoke(fd, (long) TIOCGWINSZ, ws.segment());
108+
return TerminalSize.of(ws.ws_col(), ws.ws_row());
109+
} catch (Throwable e) {
110+
throw new RuntimeException("Unable to call ioctl(TIOCGWINSZ)", e);
111+
}
112+
}
113+
114+
// CONSTANTS
115+
116+
private static final int TIOCGWINSZ;
117+
118+
static {
119+
String osName = System.getProperty("os.name");
120+
if (osName.startsWith("Linux")) {
121+
String arch = System.getProperty("os.arch");
122+
boolean isMipsPpcOrSparc = arch.equals("mips")
123+
|| arch.equals("mips64")
124+
|| arch.equals("mipsel")
125+
|| arch.equals("mips64el")
126+
|| arch.startsWith("ppc")
127+
|| arch.startsWith("sparc");
128+
TIOCGWINSZ = isMipsPpcOrSparc ? 0x40087468 : 0x00005413;
129+
} else if (osName.startsWith("Solaris") || osName.startsWith("SunOS")) {
130+
int _TIOC = ('T' << 8);
131+
TIOCGWINSZ = (_TIOC | 104);
132+
} else if (osName.startsWith("Mac") || osName.startsWith("Darwin")) {
133+
TIOCGWINSZ = 0x40087468;
134+
} else if (osName.startsWith("FreeBSD")) {
135+
TIOCGWINSZ = 0x40087468;
136+
} else {
137+
throw new UnsupportedOperationException();
138+
}
139+
}
140+
}

0 commit comments

Comments
 (0)