# HG changeset patch # User wenzelm # Date 1625079458 -7200 # Node ID f627ffab387b6fcddf211504e8d4ce503cdfad5d # Parent 0dd54d6c974ab569cf53aed892de957fe5e039d6 support for Isabelle setup in pure Java; diff -r 0dd54d6c974a -r f627ffab387b Admin/components/components.sha1 --- a/Admin/components/components.sha1 Wed Jun 30 16:53:33 2021 +0200 +++ b/Admin/components/components.sha1 Wed Jun 30 20:57:38 2021 +0200 @@ -121,6 +121,7 @@ b166b4bd583b6442a5d75eab06f7adbb66919d6d isabelle_fonts-20210319.tar.gz 9467ad54a9ac10a6e7e8db5458d8d2a5516eba96 isabelle_fonts-20210321.tar.gz 1f7a0b9829ecac6552b21e995ad0f0ac168634f3 isabelle_fonts-20210322.tar.gz +916adccd2f40c55116b68b92ce1eccb24d4dd9a2 isabelle_setup-20210630.tar.gz 0b2206f914336dec4923dd0479d8cee4b904f544 jdk-11+28.tar.gz e12574d838ed55ef2845acf1152329572ab0cc56 jdk-11.0.10+9.tar.gz 3e05213cad47dbef52804fe329395db9b4e57f39 jdk-11.0.2+9.tar.gz diff -r 0dd54d6c974a -r f627ffab387b Admin/components/main --- a/Admin/components/main Wed Jun 30 16:53:33 2021 +0200 +++ b/Admin/components/main Wed Jun 30 20:57:38 2021 +0200 @@ -8,6 +8,7 @@ flatlaf-1.2 idea-icons-20210508 isabelle_fonts-20210322 +isabelle_setup-20210630 jdk-15.0.2+7 jedit_build-20210510-1 jfreechart-1.5.1 diff -r 0dd54d6c974a -r f627ffab387b Admin/lib/Tools/build_setup --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Admin/lib/Tools/build_setup Wed Jun 30 20:57:38 2021 +0200 @@ -0,0 +1,83 @@ +#!/usr/bin/env bash +# +# Author: Makarius +# +# DESCRIPTION: build component for Isabelle/Java setup tool + +## sources + +declare -a SOURCES=( + "Environment.java" + "Setup.java" +) + + +## usage + +PRG=$(basename "$0") + +function usage() +{ + echo + echo "Usage: isabelle $PRG [OPTIONS] COMPONENT_DIR" + echo + echo " Build component for Isabelle/Java setup tool." + echo + exit 1 +} + +function fail() +{ + echo "$1" >&2 + exit 2 +} + + +## process command line + +[ "$#" -ge 1 ] && { COMPONENT_DIR="$1"; shift; } +[ "$#" -ne 0 -o -z "$COMPONENT_DIR" ] && usage + + + +## main + +[ -d "$COMPONENT_DIR" ] && fail "Directory already exists: \"$COMPONENT_DIR\"" + + +# build jar + +TARGET_DIR="$COMPONENT_DIR/lib" +mkdir -p "$TARGET_DIR/isabelle/setup" + +declare -a ARGS=("-Xlint:unchecked") +for SRC in "${SOURCES[@]}" +do + ARGS["${#ARGS[@]}"]="$(platform_path "$ISABELLE_HOME/src/Tools/Setup/src/isabelle/setup/$SRC")" +done + +isabelle_jdk javac -d "$TARGET_DIR" "${ARGS[@]}" || \ + fail "Failed to compile sources" + +isabelle_jdk jar -c -f "$(platform_path "$TARGET_DIR/isabelle_setup.jar")" \ + -e "isabelle.setup.Setup" -C "$TARGET_DIR" isabelle || fail "Failed to produce jar" + +rm -rf "$TARGET_DIR/isabelle" + + +# etc/settings + +mkdir -p "$COMPONENT_DIR/etc" +cat > "$COMPONENT_DIR/etc/settings" < "$COMPONENT_DIR/README" < JList, Map => JMap} -import java.io.{File => JFile} -import java.nio.file.Files - - -object Isabelle_Env -{ - val is_windows: Boolean = System.getProperty("os.name", "").startsWith("Windows") - - def runtime_exception(message: String): Nothing = throw new RuntimeException(message) - - def quote(s: String): String = "\"" + s + "\"" - - - - /** bootstrap information **/ - - def bootstrap_directory( - preference: String, variable: String, property: String, description: String): String = - { - val a = preference // explicit argument - val b = System.getenv(variable) // e.g. inherited from running isabelle tool - val c = System.getProperty(property) // e.g. via JVM application boot process - val dir = - if (a != null && a.nonEmpty) a - else if (b != null && b.nonEmpty) b - else if (c != null && c.nonEmpty) c - else runtime_exception("Unknown " + description + " directory") - - if ((new JFile(dir)).isDirectory) dir - else runtime_exception("Bad " + description + " directory " + quote(dir)) - } - - - - /** Support for Cygwin as POSIX emulation on Windows **/ - - /* system path representations */ - - def standard_path(cygwin_root: String, platform_path: String): String = - if (is_windows) { - val backslashes = platform_path.replace('/', '\\') - def slashes(s: String): String = s.replace('\\', '/') - - val root_pattern = Pattern.compile("(?i)" + Pattern.quote(cygwin_root) + """(?:\\+|\z)(.*)""") - val root_matcher = root_pattern.matcher(backslashes) - - val drive_pattern = Pattern.compile("""([a-zA-Z]):\\*(.*)""") - val drive_matcher = drive_pattern.matcher(backslashes) - - if (root_matcher.matches) { - val rest = root_matcher.group(1) - "/" + slashes(rest) - } - else if (drive_matcher.matches) { - val letter = drive_matcher.group(1).toLowerCase(Locale.ROOT) - val rest = drive_matcher.group(2) - "/cygdrive/" + letter + (if (rest == "") "" else "/" + slashes(rest)) - } - else slashes(backslashes) - } - else platform_path - - def platform_path(cygwin_root: String, standard_path: String): String = - if (is_windows) { - val result_path = new StringBuilder - - val cygdrive_pattern = Pattern.compile("/cygdrive/([a-zA-Z])($|/.*)") - val cygdrive_matcher = cygdrive_pattern.matcher(standard_path) - - val named_root_pattern = Pattern.compile("//+([^/]*)(.*)") - val named_root_matcher = named_root_pattern.matcher(standard_path) - - val rest = - if (cygdrive_matcher.matches) { - val drive = cygdrive_matcher.group(1).toUpperCase(Locale.ROOT) - val rest = cygdrive_matcher.group(2) - result_path ++= drive - result_path ++= ":" - result_path ++= JFile.separator - rest - } - else if (named_root_matcher.matches) { - val root = named_root_matcher.group(1) - val rest = named_root_matcher.group(2) - result_path ++= JFile.separator - result_path ++= JFile.separator - result_path ++= root - rest - } - else { - if (standard_path.startsWith("/")) result_path ++= cygwin_root - standard_path - } - for (p <- rest.split("/", -1) if p != "") { - val len = result_path.length - if (len > 0 && result_path(len - 1) != JFile.separatorChar) - result_path += JFile.separatorChar - result_path ++= p - } - result_path.toString - } - else standard_path - - - /* symlink emulation */ - - def cygwin_link(content: String, target: JFile): Unit = - { - val target_path = target.toPath - Files.writeString(target_path, "!" + content + "\u0000") - Files.setAttribute(target_path, "dos:system", true) - } - - - /* init (e.g. after extraction via 7zip) */ - - def cygwin_init(isabelle_root: String, cygwin_root: String): Unit = - { - if (is_windows) { - def cygwin_exec(cmd: JList[String]): Unit = - { - val cwd = new JFile(isabelle_root) - val env = new HashMap(System.getenv()) - env.put("CYGWIN", "nodosfilewarning") - val res = exec_process(cmd, cwd = cwd, env = env, redirect = true) - if (!res.ok) runtime_exception(res.out) - } - - val uninitialized_file = new JFile(cygwin_root, "isabelle\\uninitialized") - val uninitialized = uninitialized_file.isFile && uninitialized_file.delete - - if (uninitialized) { - val symlinks_path = (new JFile(cygwin_root + "\\isabelle\\symlinks")).toPath - val symlinks = Files.readAllLines(symlinks_path).toArray(new Array[String](0)) - - // recover symlinks - var i = 0 - val m = symlinks.length - val n = if (m > 0 && symlinks(m - 1).isEmpty) m - 1 else m - while (i < n) { - if (i + 1 < n) { - val target = symlinks(i) - val content = symlinks(i + 1) - cygwin_link(content, new JFile(isabelle_root, target)) - i += 2 - } - else runtime_exception("Unbalanced symlinks list") - } - - cygwin_exec(JList.of(cygwin_root + "\\bin\\dash.exe", "/isabelle/rebaseall")) - cygwin_exec(JList.of(cygwin_root + "\\bin\\bash.exe", "/isabelle/postinstall")) - } - } - } - - - - /** raw process **/ - - /* raw process */ - - def process_builder(cmd: JList[String], - cwd: JFile = null, - env: JMap[String, String] = settings(), - redirect: Boolean = false): ProcessBuilder = - { - val builder = new ProcessBuilder - - // fragile on Windows: - // see https://docs.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=msvc-160 - builder.command(cmd) - - if (cwd != null) builder.directory(cwd) - if (env != null) { - builder.environment.clear() - builder.environment().putAll(env) - } - builder.redirectErrorStream(redirect) - } - - class Exec_Result(val rc: Int, val out: String, val err: String) - { - def ok: Boolean = rc == 0 - } - - def exec_process(command_line: JList[String], - cwd: JFile = null, - env: JMap[String, String] = settings(), - redirect: Boolean = false): Exec_Result = - { - val out_file = Files.createTempFile(null, null) - val err_file = Files.createTempFile(null, null) - try { - val proc = - { - val builder = process_builder(command_line, cwd = cwd, env = env, redirect = redirect) - builder.redirectOutput(out_file.toFile) - builder.redirectError(err_file.toFile) - builder.start() - } - proc.getOutputStream.close() - try { proc.waitFor() } - finally { - proc.getInputStream.close() - proc.getErrorStream.close() - proc.destroy() - Thread.interrupted() - } - - val rc = proc.exitValue() - val out = Files.readString(out_file) - val err = Files.readString(err_file) - new Exec_Result(rc, out, err) - } - finally { - Files.deleteIfExists(out_file) - Files.deleteIfExists(err_file) - } - } - - - - /** implicit settings environment **/ - - @volatile private var _settings: JMap[String, String] = null - - def settings(): JMap[String, String] = - { - if (_settings == null) init("", "") // unsynchronized check - _settings - } - - def init(_isabelle_root: String, _cygwin_root: String): Unit = synchronized - { - if (_settings == null) { - val isabelle_root = - bootstrap_directory(_isabelle_root, "ISABELLE_ROOT", "isabelle.root", "Isabelle root") - - val cygwin_root = - if (is_windows) { - val root = bootstrap_directory(_cygwin_root, "CYGWIN_ROOT", "cygwin.root", "Cygwin root") - cygwin_init(isabelle_root, root) - root - } - else "" - - val env = new HashMap(System.getenv()) - def env_default(a: String, b: String): Unit = if (b != "") env.putIfAbsent(a, b) - - env_default("CYGWIN_ROOT", cygwin_root) - env_default("TEMP_WINDOWS", - { - val temp = if (is_windows) System.getenv("TEMP") else null - if (temp != null && temp.contains('\\')) temp else "" - }) - env_default("ISABELLE_JDK_HOME", - standard_path(cygwin_root, System.getProperty("java.home", ""))) - env_default("HOME", System.getProperty("user.home", "")) - env_default("ISABELLE_APP", System.getProperty("isabelle.app", "")) - - val settings = new HashMap[String, String] - val settings_file = Files.createTempFile(null, null) - try { - val cmd = new LinkedList[String] - if (is_windows) { - cmd.add(cygwin_root + "\\bin\\bash") - cmd.add("-l") - cmd.add(standard_path(cygwin_root, isabelle_root + "\\bin\\isabelle")) - } else { - cmd.add(isabelle_root + "/bin/isabelle") - } - cmd.add("getenv") - cmd.add("-d") - cmd.add(settings_file.toString) - - val res = exec_process(cmd, env = env, redirect = true) - if (!res.ok) runtime_exception(res.out) - - for (s <- Files.readString(settings_file).split("\u0000", -1)) { - val i = s.indexOf('=') - if (i > 0) settings.put(s.substring(0, i), s.substring(i + 1)) - else if (i < 0 && s.nonEmpty) settings.put(s, "") - } - } - finally { Files.delete(settings_file) } - - if (is_windows) settings.put("CYGWIN_ROOT", cygwin_root) - - settings.put("PATH", settings.get("PATH_JVM")) - settings.remove("PATH_JVM") - - _settings = JMap.copyOf(settings) - } - } -} diff -r 0dd54d6c974a -r f627ffab387b src/Pure/System/isabelle_system.scala --- a/src/Pure/System/isabelle_system.scala Wed Jun 30 16:53:33 2021 +0200 +++ b/src/Pure/System/isabelle_system.scala Wed Jun 30 20:57:38 2021 +0200 @@ -18,7 +18,7 @@ { /* settings */ - def settings(): JMap[String, String] = isabelle.setup.Isabelle_Env.settings() + def settings(): JMap[String, String] = isabelle.setup.Environment.settings() def getenv(name: String, env: JMap[String, String] = settings()): String = Option(env.get(name)).getOrElse("") @@ -51,7 +51,7 @@ def init(isabelle_root: String = "", cygwin_root: String = ""): Unit = { - isabelle.setup.Isabelle_Env.init(isabelle_root, cygwin_root) + isabelle.setup.Environment.init(isabelle_root, cygwin_root) synchronized { if (_services.isEmpty) { val variable = "ISABELLE_SCALA_SERVICES" @@ -253,7 +253,7 @@ if (force) target.delete def cygwin_link(): Unit = - isabelle.setup.Isabelle_Env.cygwin_link(File.standard_path(src), target) + isabelle.setup.Environment.cygwin_link(File.standard_path(src), target) try { Files.createSymbolicLink(target.toPath, src_file.toPath) } catch { diff -r 0dd54d6c974a -r f627ffab387b src/Pure/System/platform.scala --- a/src/Pure/System/platform.scala Wed Jun 30 16:53:33 2021 +0200 +++ b/src/Pure/System/platform.scala Wed Jun 30 20:57:38 2021 +0200 @@ -11,7 +11,7 @@ { /* platform family */ - def is_windows: Boolean = isabelle.setup.Isabelle_Env.is_windows + val is_windows: Boolean = isabelle.setup.Environment.is_windows() val is_linux: Boolean = System.getProperty("os.name", "") == "Linux" val is_macos: Boolean = System.getProperty("os.name", "") == "Mac OS X" val is_unix: Boolean = is_linux || is_macos diff -r 0dd54d6c974a -r f627ffab387b src/Pure/build-jars --- a/src/Pure/build-jars Wed Jun 30 16:53:33 2021 +0200 +++ b/src/Pure/build-jars Wed Jun 30 20:57:38 2021 +0200 @@ -136,7 +136,6 @@ src/Pure/System/executable.scala src/Pure/System/getopts.scala src/Pure/System/isabelle_charset.scala - src/Pure/System/isabelle_env.scala src/Pure/System/isabelle_fonts.scala src/Pure/System/isabelle_platform.scala src/Pure/System/isabelle_process.scala diff -r 0dd54d6c974a -r f627ffab387b src/Tools/Setup/src/isabelle/setup/Environment.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Tools/Setup/src/isabelle/setup/Environment.java Wed Jun 30 20:57:38 2021 +0200 @@ -0,0 +1,336 @@ +/* Title: Pure/System/isabelle_env.scala + Author: Makarius + +Fundamental Isabelle system environment: quasi-static module with +optional init operation. +*/ + +package isabelle.setup; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +public class Environment +{ + /** Support for Cygwin as POSIX emulation on Windows **/ + + public static Boolean is_windows() + { + return System.getProperty("os.name", "").startsWith("Windows"); + } + + public static String quote(String s) + { + return "\"" + s + "\""; + } + + + + /* system path representations */ + + private static String slashes(String s) { return s.replace('\\', '/'); } + + public static String standard_path(String cygwin_root, String platform_path) + { + if (is_windows()) { + String backslashes = platform_path.replace('/', '\\'); + + Pattern root_pattern = + Pattern.compile("(?i)" + Pattern.quote(cygwin_root) + "(?:\\\\+|\\z)(.*)"); + Matcher root_matcher = root_pattern.matcher(backslashes); + + Pattern drive_pattern = Pattern.compile("([a-zA-Z]):\\\\*(.*)"); + Matcher drive_matcher = drive_pattern.matcher(backslashes); + + if (root_matcher.matches()) { + String rest = root_matcher.group(1); + return "/" + slashes(rest); + } + else if (drive_matcher.matches()) { + String letter = drive_matcher.group(1).toLowerCase(Locale.ROOT); + String rest = drive_matcher.group(2); + return "/cygdrive/" + letter + (rest.isEmpty() ? "" : "/" + slashes(rest)); + } + else { return slashes(backslashes); } + } + else { return platform_path; } + } + + public static String platform_path(String cygwin_root, String standard_path) + { + if (is_windows()) { + StringBuilder result_path = new StringBuilder(); + + Pattern cygdrive_pattern = Pattern.compile("/cygdrive/([a-zA-Z])($|/.*)"); + Matcher cygdrive_matcher = cygdrive_pattern.matcher(standard_path); + + Pattern named_root_pattern = Pattern.compile("//+([^/]*)(.*)"); + Matcher named_root_matcher = named_root_pattern.matcher(standard_path); + + String rest; + if (cygdrive_matcher.matches()) { + String drive = cygdrive_matcher.group(1).toUpperCase(Locale.ROOT); + rest = cygdrive_matcher.group(2); + result_path.append(drive); + result_path.append(':'); + result_path.append(File.separatorChar); + } + else if (named_root_matcher.matches()) { + String root = named_root_matcher.group(1); + rest = named_root_matcher.group(2); + result_path.append(File.separatorChar); + result_path.append(File.separatorChar); + result_path.append(root); + } + else { + if (standard_path.startsWith("/")) { result_path.append(cygwin_root); } + rest = standard_path; + } + + for (String p : rest.split("/", -1)) { + if (!p.isEmpty()) { + int len = result_path.length(); + if (len > 0 && result_path.charAt(len - 1) != File.separatorChar) { + result_path.append(File.separatorChar); + } + result_path.append(p); + } + } + + return result_path.toString(); + } + else { return standard_path; } + } + + + /* raw process */ + + public static ProcessBuilder process_builder( + List cmd, File cwd, Map env, boolean redirect) + { + ProcessBuilder builder = new ProcessBuilder(); + + // fragile on Windows: + // see https://docs.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=msvc-160 + builder.command(cmd); + + if (cwd != null) builder.directory(cwd); + if (env != null) { + builder.environment().clear(); + builder.environment().putAll(env); + } + builder.redirectErrorStream(redirect); + + return builder; + } + + public static class Exec_Result + { + private final int _rc; + private final String _out; + private final String _err; + + Exec_Result(int rc, String out, String err) + { + _rc = rc; + _out = out; + _err = err; + } + + public int rc() { return _rc; } + public boolean ok() { return _rc == 0; } + public String out() { return _out; } + public String err() { return _err; } + } + + public static Exec_Result exec_process( + List command_line, + File cwd, + Map env, + boolean redirect) throws IOException, InterruptedException + { + Path out_file = Files.createTempFile(null, null); + Path err_file = Files.createTempFile(null, null); + Exec_Result res; + try { + ProcessBuilder builder = process_builder(command_line, cwd, env, redirect); + builder.redirectOutput(out_file.toFile()); + builder.redirectError(err_file.toFile()); + + Process proc = builder.start(); + proc.getOutputStream().close(); + try { proc.waitFor(); } + finally { + proc.getInputStream().close(); + proc.getErrorStream().close(); + proc.destroy(); + Thread.interrupted(); + } + + int rc = proc.exitValue(); + String out = Files.readString(out_file); + String err = Files.readString(err_file); + res = new Exec_Result(rc, out, err); + } + finally { + Files.deleteIfExists(out_file); + Files.deleteIfExists(err_file); + } + return res; + } + + + /* init (e.g. after extraction via 7zip) */ + + private static String bootstrap_directory( + String preference, String variable, String property, String description) + { + String a = preference; // explicit argument + String b = System.getenv(variable); // e.g. inherited from running isabelle tool + String c = System.getProperty(property); // e.g. via JVM application boot process + String dir; + + if (a != null && !a.isEmpty()) { dir = a; } + else if (b != null && !b.isEmpty()) { dir = b; } + else if (c != null && !c.isEmpty()) { dir = c; } + else { throw new RuntimeException("Unknown " + description + " directory"); } + + if ((new File(dir)).isDirectory()) { return dir; } + else { throw new RuntimeException("Bad " + description + " directory " + quote(dir)); } + } + + private static void cygwin_exec(String isabelle_root, List cmd) + throws IOException, InterruptedException + { + File cwd = new File(isabelle_root); + Map env = new HashMap(System.getenv()); + env.put("CYGWIN", "nodosfilewarning"); + Exec_Result res = exec_process(cmd, cwd, env, true); + if (!res.ok()) throw new RuntimeException(res.out()); + } + + public static void cygwin_link(String content, File target) throws IOException + { + Path target_path = target.toPath(); + Files.writeString(target_path, "!" + content + "\u0000"); + Files.setAttribute(target_path, "dos:system", true); + } + + public static void cygwin_init(String isabelle_root, String cygwin_root) + throws IOException, InterruptedException + { + if (is_windows()) { + File uninitialized_file = new File(cygwin_root, "isabelle\\uninitialized"); + boolean uninitialized = uninitialized_file.isFile() && uninitialized_file.delete(); + + if (uninitialized) { + Path symlinks_path = (new File(cygwin_root + "\\isabelle\\symlinks")).toPath(); + String[] symlinks = Files.readAllLines(symlinks_path).toArray(new String[0]); + + // recover symlinks + int i = 0; + int m = symlinks.length; + int n = (m > 0 && symlinks[m - 1].isEmpty()) ? m - 1 : m; + while (i < n) { + if (i + 1 < n) { + String target = symlinks[i]; + String content = symlinks[i + 1]; + cygwin_link(content, new File(isabelle_root, target)); + i += 2; + } else { throw new RuntimeException("Unbalanced symlinks list"); } + } + + cygwin_exec(isabelle_root, + List.of(cygwin_root + "\\bin\\dash.exe", "/isabelle/rebaseall")); + cygwin_exec(isabelle_root, + List.of(cygwin_root + "\\bin\\bash.exe", "/isabelle/postinstall")); + } + } + } + + + /* implicit settings environment */ + + private static volatile Map _settings = null; + + public static Map settings() + throws IOException, InterruptedException + { + if (_settings == null) { init("", ""); } // unsynchronized check + return _settings; + } + + public static synchronized void init(String _isabelle_root, String _cygwin_root) + throws IOException, InterruptedException + { + if (_settings == null) { + String isabelle_root = + bootstrap_directory(_isabelle_root, "ISABELLE_ROOT", "isabelle.root", "Isabelle root"); + + String cygwin_root = ""; + if (is_windows()) { + cygwin_root = bootstrap_directory(_cygwin_root, "CYGWIN_ROOT", "cygwin.root", "Cygwin root"); + cygwin_init(isabelle_root, cygwin_root); + } + + Map env = new HashMap(System.getenv()); + + BiFunction env_default = + (String a, String b) -> { if (!b.isEmpty()) env.putIfAbsent(a, b); return null; }; + + String temp_windows = is_windows() ? System.getenv("TEMP") : null; + + env_default.apply("CYGWIN_ROOT", cygwin_root); + env_default.apply("TEMP_WINDOWS", + (temp_windows != null && temp_windows.contains("\\")) ? temp_windows : ""); + env_default.apply("ISABELLE_JDK_HOME", + standard_path(cygwin_root, System.getProperty("java.home", ""))); + env_default.apply("HOME", System.getProperty("user.home", "")); + env_default.apply("ISABELLE_APP", System.getProperty("isabelle.app", "")); + + Map settings = new HashMap(); + Path settings_file = Files.createTempFile(null, null); + try { + List cmd = new LinkedList(); + if (is_windows()) { + cmd.add(cygwin_root + "\\bin\\bash"); + cmd.add("-l"); + cmd.add(standard_path(cygwin_root, isabelle_root + "\\bin\\isabelle")); + } else { + cmd.add(isabelle_root + "/bin/isabelle"); + } + cmd.add("getenv"); + cmd.add("-d"); + cmd.add(settings_file.toString()); + + Exec_Result res = exec_process(cmd, null, env, true); + if (!res.ok()) throw new RuntimeException(res.out()); + + for (String s : Files.readString(settings_file).split("\u0000", -1)) { + int i = s.indexOf('='); + if (i > 0) { settings.put(s.substring(0, i), s.substring(i + 1)); } + else if (i < 0 && !s.isEmpty()) { settings.put(s, ""); } + } + } + finally { Files.delete(settings_file); } + + if (is_windows()) { settings.put("CYGWIN_ROOT", cygwin_root); } + + settings.put("PATH", settings.get("PATH_JVM")); + settings.remove("PATH_JVM"); + + _settings = Map.copyOf(settings); + } + } +}