| author | wenzelm | 
| Tue, 13 Oct 2020 20:28:43 +0200 | |
| changeset 72470 | e2e9ef9aa2df | 
| parent 72376 | 04bce3478688 | 
| child 72498 | d59242549b7f | 
| permissions | -rw-r--r-- | 
| 64929 | 1 | /* Title: Pure/Admin/build_jdk.scala | 
| 2 | Author: Makarius | |
| 3 | ||
| 4 | Build Isabelle jdk component from original platform installations. | |
| 5 | */ | |
| 6 | ||
| 7 | package isabelle | |
| 8 | ||
| 9 | ||
| 69186 
573b7fbd96a8
updated to jdk-11+28 from https://adoptopenjdk.net -- with proper font rendering on Linux;
 wenzelm parents: 
69128diff
changeset | 10 | import java.io.{File => JFile}
 | 
| 64933 | 11 | import java.nio.file.Files | 
| 64934 | 12 | import java.nio.file.attribute.PosixFilePermission | 
| 64933 | 13 | |
| 64929 | 14 | import scala.util.matching.Regex | 
| 15 | ||
| 16 | ||
| 17 | object Build_JDK | |
| 18 | {
 | |
| 19 | /* version */ | |
| 20 | ||
| 69128 | 21 | def detect_version(s: String): String = | 
| 64929 | 22 |   {
 | 
| 69754 | 23 | val Version_Dir_Entry = """^jdk-([0-9.]+\+\d+)$""".r | 
| 64929 | 24 |     s match {
 | 
| 69128 | 25 | case Version_Dir_Entry(version) => version | 
| 64929 | 26 |       case _ => error("Cannot detect JDK version from " + quote(s))
 | 
| 27 | } | |
| 28 | } | |
| 29 | ||
| 30 | ||
| 31 | /* platform */ | |
| 32 | ||
| 69128 | 33 | sealed case class JDK_Platform(name: String, home: String, exe: String, regex: Regex) | 
| 64929 | 34 |   {
 | 
| 35 | override def toString: String = name | |
| 36 | ||
| 37 | def detect(jdk_dir: Path): Boolean = | |
| 38 |     {
 | |
| 39 | val path = jdk_dir + Path.explode(exe) | |
| 40 |       if (path.is_file) {
 | |
| 41 |         val file_descr = Isabelle_System.bash("file -b " + File.bash_path(path)).check.out
 | |
| 42 | regex.pattern.matcher(file_descr).matches | |
| 43 | } | |
| 44 | else false | |
| 45 | } | |
| 46 | } | |
| 47 | val jdk_platforms = | |
| 66906 | 48 | List( | 
| 69128 | 49 |       JDK_Platform("x86_64-linux", ".", "bin/java", """.*ELF 64-bit.*x86[-_]64.*""".r),
 | 
| 50 |       JDK_Platform("x86_64-windows", ".", "bin/java.exe", """.*PE32\+ executable.*x86[-_]64.*""".r),
 | |
| 51 |       JDK_Platform("x86_64-darwin", "Contents/Home", "Contents/Home/bin/java",
 | |
| 52 | """.*Mach-O 64-bit.*x86[-_]64.*""".r)) | |
| 64929 | 53 | |
| 54 | ||
| 55 | /* README */ | |
| 56 | ||
| 69128 | 57 | def readme(version: String): String = | 
| 58 | """This is OpenJDK """ + version + """ as required for Isabelle. | |
| 64929 | 59 | |
| 69186 
573b7fbd96a8
updated to jdk-11+28 from https://adoptopenjdk.net -- with proper font rendering on Linux;
 wenzelm parents: 
69128diff
changeset | 60 | See https://adoptopenjdk.net for the original downloads, which are covered | 
| 69128 | 61 | the GPL2 (with various liberal exceptions, see legal/*). | 
| 64929 | 62 | |
| 63 | Linux, Windows, Mac OS X all work uniformly, depending on certain | |
| 64 | platform-specific subdirectories. | |
| 65 | """ | |
| 66 | ||
| 67 | ||
| 68 | /* settings */ | |
| 69 | ||
| 70 | val settings = | |
| 71 | """# -*- shell-script -*- :mode=shellscript: | |
| 72 | ||
| 73 | case "$ISABELLE_PLATFORM_FAMILY" in | |
| 74 | linux) | |
| 66906 | 75 | ISABELLE_JAVA_PLATFORM="$ISABELLE_PLATFORM64" | 
| 64929 | 76 | ISABELLE_JDK_HOME="$COMPONENT/$ISABELLE_JAVA_PLATFORM" | 
| 77 | ;; | |
| 78 | windows) | |
| 66906 | 79 | ISABELLE_JAVA_PLATFORM="$ISABELLE_WINDOWS_PLATFORM64" | 
| 64929 | 80 | ISABELLE_JDK_HOME="$COMPONENT/$ISABELLE_JAVA_PLATFORM" | 
| 81 | ;; | |
| 82 | macos) | |
| 66906 | 83 | ISABELLE_JAVA_PLATFORM="$ISABELLE_PLATFORM64" | 
| 84 | ISABELLE_JDK_HOME="$COMPONENT/$ISABELLE_JAVA_PLATFORM/Contents/Home" | |
| 64929 | 85 | ;; | 
| 86 | esac | |
| 87 | """ | |
| 88 | ||
| 89 | ||
| 90 | /* extract archive */ | |
| 91 | ||
| 69186 
573b7fbd96a8
updated to jdk-11+28 from https://adoptopenjdk.net -- with proper font rendering on Linux;
 wenzelm parents: 
69128diff
changeset | 92 |   private def suppress_name(name: String): Boolean = name.startsWith("._")
 | 
| 
573b7fbd96a8
updated to jdk-11+28 from https://adoptopenjdk.net -- with proper font rendering on Linux;
 wenzelm parents: 
69128diff
changeset | 93 | |
| 69128 | 94 | def extract_archive(dir: Path, archive: Path): (String, JDK_Platform) = | 
| 64929 | 95 |   {
 | 
| 96 |     try {
 | |
| 72376 | 97 |       val tmp_dir = Isabelle_System.make_directory(dir + Path.explode("tmp"))
 | 
| 69128 | 98 | |
| 69367 | 99 |       if (archive.get_ext == "zip") {
 | 
| 69128 | 100 | Isabelle_System.bash( | 
| 101 | "unzip -x " + File.bash_path(archive.absolute), cwd = tmp_dir.file).check | |
| 102 | } | |
| 103 |       else {
 | |
| 69425 | 104 |         Isabelle_System.gnutar("-xzf " + File.bash_path(archive), dir = tmp_dir).check
 | 
| 69128 | 105 | } | 
| 106 | ||
| 64929 | 107 | val dir_entry = | 
| 71601 | 108 |         File.read_dir(tmp_dir).filterNot(suppress_name) match {
 | 
| 64929 | 109 | case List(s) => s | 
| 110 |           case _ => error("Archive contains multiple directories")
 | |
| 111 | } | |
| 112 | val version = detect_version(dir_entry) | |
| 113 | ||
| 114 | val jdk_dir = tmp_dir + Path.explode(dir_entry) | |
| 115 | val platform = | |
| 116 |         jdk_platforms.find(_.detect(jdk_dir)) getOrElse error("Failed to detect JDK platform")
 | |
| 117 | ||
| 118 | val platform_dir = dir + Path.explode(platform.name) | |
| 119 |       if (platform_dir.is_dir) error("Directory already exists: " + platform_dir)
 | |
| 69128 | 120 | |
| 69402 
61f4c406d727
more direct File.link operation: avoid external process;
 wenzelm parents: 
69395diff
changeset | 121 |       File.link(Path.current, jdk_dir + Path.explode(platform.home) + Path.explode("jre"))
 | 
| 69128 | 122 | |
| 64930 | 123 | File.move(jdk_dir, platform_dir) | 
| 64929 | 124 | |
| 125 | (version, platform) | |
| 126 | } | |
| 127 |     catch { case ERROR(msg) => cat_error(msg, "The error(s) above occurred for " + archive) }
 | |
| 128 | } | |
| 129 | ||
| 130 | ||
| 131 | /* build jdk */ | |
| 132 | ||
| 133 | def build_jdk( | |
| 134 | archives: List[Path], | |
| 71726 
a5fda30edae2
clarified signature: more uniform treatment of stopped/interrupted state;
 wenzelm parents: 
71602diff
changeset | 135 | progress: Progress = new Progress, | 
| 64929 | 136 | target_dir: Path = Path.current) | 
| 137 |   {
 | |
| 138 |     if (Platform.is_windows) error("Cannot build jdk on Windows")
 | |
| 139 | ||
| 140 |     Isabelle_System.with_tmp_dir("jdk")(dir =>
 | |
| 141 |       {
 | |
| 142 |         progress.echo("Extracting ...")
 | |
| 143 | val extracted = archives.map(extract_archive(dir, _)) | |
| 144 | ||
| 145 | val version = | |
| 71602 | 146 |           extracted.map(_._1).distinct match {
 | 
| 64929 | 147 | case List(version) => version | 
| 148 |             case Nil => error("No archives")
 | |
| 149 | case versions => | |
| 69128 | 150 |               error("Archives contain multiple JDK versions: " + commas_quote(versions))
 | 
| 64929 | 151 | } | 
| 152 | ||
| 153 | val missing_platforms = | |
| 154 |           jdk_platforms.filterNot(p1 => extracted.exists({ case (_, p2) => p1.name == p2.name }))
 | |
| 155 | if (missing_platforms.nonEmpty) | |
| 156 |           error("Missing platforms: " + commas_quote(missing_platforms.map(_.name)))
 | |
| 157 | ||
| 69128 | 158 | val jdk_name = "jdk-" + version | 
| 64929 | 159 | val jdk_path = Path.explode(jdk_name) | 
| 160 | val component_dir = dir + jdk_path | |
| 161 | ||
| 72375 | 162 |         Isabelle_System.make_directory(component_dir + Path.explode("etc"))
 | 
| 69395 
d1c4a1dee9e7
more explicit support for Isabelle system components;
 wenzelm parents: 
69367diff
changeset | 163 | File.write(Components.settings(component_dir), settings) | 
| 64929 | 164 |         File.write(component_dir + Path.explode("README"), readme(version))
 | 
| 165 | ||
| 64930 | 166 | for ((_, platform) <- extracted) | 
| 167 | File.move(dir + Path.explode(platform.name), component_dir) | |
| 64929 | 168 | |
| 64933 | 169 |         for (file <- File.find_files(component_dir.file, include_dirs = true)) {
 | 
| 170 | val path = file.toPath | |
| 171 | val perms = Files.getPosixFilePermissions(path) | |
| 64934 | 172 | perms.add(PosixFilePermission.OWNER_READ) | 
| 173 | perms.add(PosixFilePermission.GROUP_READ) | |
| 174 | perms.add(PosixFilePermission.OTHERS_READ) | |
| 175 | perms.add(PosixFilePermission.OWNER_WRITE) | |
| 64933 | 176 |           if (file.isDirectory) {
 | 
| 64934 | 177 | perms.add(PosixFilePermission.OWNER_WRITE) | 
| 178 | perms.add(PosixFilePermission.OWNER_EXECUTE) | |
| 179 | perms.add(PosixFilePermission.GROUP_EXECUTE) | |
| 180 | perms.add(PosixFilePermission.OTHERS_EXECUTE) | |
| 64933 | 181 | } | 
| 182 | Files.setPosixFilePermissions(path, perms) | |
| 183 | } | |
| 64931 | 184 | |
| 185 |         File.find_files((component_dir + Path.explode("x86_64-darwin")).file,
 | |
| 69186 
573b7fbd96a8
updated to jdk-11+28 from https://adoptopenjdk.net -- with proper font rendering on Linux;
 wenzelm parents: 
69128diff
changeset | 186 | file => suppress_name(file.getName)).foreach(_.delete) | 
| 64929 | 187 | |
| 188 |         progress.echo("Sharing ...")
 | |
| 64934 | 189 | val main_dir :: other_dirs = | 
| 190 | jdk_platforms.map(platform => (component_dir + Path.explode(platform.name)).file.toPath) | |
| 191 |         for {
 | |
| 192 | file1 <- File.find_files(main_dir.toFile).iterator | |
| 193 | path1 = file1.toPath | |
| 194 | dir2 <- other_dirs.iterator | |
| 195 |         } {
 | |
| 196 | val path2 = dir2.resolve(main_dir.relativize(path1)) | |
| 197 | val file2 = path2.toFile | |
| 65879 
a43a079156a6
avoid mixture of symlinks and hardlinks, which causes problems with BSD tar on macOS Sierra;
 wenzelm parents: 
65873diff
changeset | 198 |           if (!Files.isSymbolicLink(path2) && file2.isFile && File.eq_content(file1, file2)) {
 | 
| 64934 | 199 | file2.delete | 
| 200 | Files.createLink(path2, path1) | |
| 201 | } | |
| 202 | } | |
| 64929 | 203 | |
| 204 |         progress.echo("Archiving ...")
 | |
| 69425 | 205 | Isabelle_System.gnutar( | 
| 206 |           "-czf " + File.bash_path(target_dir + jdk_path.ext("tar.gz")) + " " + jdk_name,
 | |
| 207 | dir = dir).check | |
| 64929 | 208 | }) | 
| 209 | } | |
| 210 | ||
| 211 | ||
| 212 | /* Isabelle tool wrapper */ | |
| 213 | ||
| 214 | val isabelle_tool = | |
| 69128 | 215 |     Isabelle_Tool("build_jdk", "build Isabelle jdk component from original archives",
 | 
| 64929 | 216 | args => | 
| 217 |     {
 | |
| 218 | var target_dir = Path.current | |
| 219 | ||
| 220 |       val getopts = Getopts("""
 | |
| 65873 | 221 | Usage: isabelle build_jdk [OPTIONS] ARCHIVES... | 
| 64929 | 222 | |
| 223 | Options are: | |
| 224 | -D DIR target directory (default ".") | |
| 225 | ||
| 69128 | 226 | Build jdk component from tar.gz archives, with original jdk archives | 
| 66906 | 227 | for x86_64 Linux, Windows, Mac OS X. | 
| 64929 | 228 | """, | 
| 229 | "D:" -> (arg => target_dir = Path.explode(arg))) | |
| 230 | ||
| 231 | val more_args = getopts(args) | |
| 232 | if (more_args.isEmpty) getopts.usage() | |
| 233 | ||
| 71601 | 234 | val archives = more_args.map(Path.explode) | 
| 64929 | 235 | val progress = new Console_Progress() | 
| 236 | ||
| 237 | build_jdk(archives = archives, progress = progress, target_dir = target_dir) | |
| 69277 
258bef08b31e
support for user-defined Isabelle/Scala command-line tools;
 wenzelm parents: 
69186diff
changeset | 238 | }) | 
| 64929 | 239 | } |