1/* Title: Pure/Admin/build_jdk.scala 2 Author: Makarius 3 4Build Isabelle jdk component from original platform installations. 5*/ 6 7package isabelle 8 9 10import java.nio.file.Files 11import java.nio.file.attribute.PosixFilePermission 12 13import scala.util.matching.Regex 14 15 16object Build_JDK 17{ 18 /* version */ 19 20 sealed case class Version(short: String, full: String) 21 22 def detect_version(s: String): Version = 23 { 24 val Version_Dir_Entry = """^jdk1\.(\d+)\.0_(\d+)(?:\.jdk)?$""".r 25 s match { 26 case Version_Dir_Entry(a, b) => Version(a + "u" + b, "1." + a + ".0_" + b) 27 case _ => error("Cannot detect JDK version from " + quote(s)) 28 } 29 } 30 31 32 /* platform */ 33 34 sealed case class JDK_Platform(name: String, exe: String, regex: Regex) 35 { 36 override def toString: String = name 37 38 def detect(jdk_dir: Path): Boolean = 39 { 40 val path = jdk_dir + Path.explode(exe) 41 if (path.is_file) { 42 val file_descr = Isabelle_System.bash("file -b " + File.bash_path(path)).check.out 43 regex.pattern.matcher(file_descr).matches 44 } 45 else false 46 } 47 } 48 val jdk_platforms = 49 List( 50 JDK_Platform("x86_64-linux", "bin/java", """.*ELF 64-bit.*x86[-_]64.*""".r), 51 JDK_Platform("x86_64-windows", "bin/java.exe", """.*PE32\+ executable.*x86[-_]64.*""".r), 52 JDK_Platform("x86_64-darwin", "Contents/Home/bin/java", """.*Mach-O 64-bit.*x86[-_]64.*""".r)) 53 54 55 /* README */ 56 57 def readme(version: Version): String = 58"""This is JDK/JRE """ + version.full + """ as required for Isabelle. 59 60See https://www.oracle.com/technetwork/java/javase/downloads/index.html 61for the original downloads, which are covered by the Oracle Binary 62Code License Agreement for Java SE. 63 64Linux, Windows, Mac OS X all work uniformly, depending on certain 65platform-specific subdirectories. 66""" 67 68 69 /* settings */ 70 71 val settings = 72"""# -*- shell-script -*- :mode=shellscript: 73 74case "$ISABELLE_PLATFORM_FAMILY" in 75 linux) 76 ISABELLE_JAVA_PLATFORM="$ISABELLE_PLATFORM64" 77 ISABELLE_JDK_HOME="$COMPONENT/$ISABELLE_JAVA_PLATFORM" 78 ;; 79 windows) 80 ISABELLE_JAVA_PLATFORM="$ISABELLE_WINDOWS_PLATFORM64" 81 ISABELLE_JDK_HOME="$COMPONENT/$ISABELLE_JAVA_PLATFORM" 82 ;; 83 macos) 84 ISABELLE_JAVA_PLATFORM="$ISABELLE_PLATFORM64" 85 ISABELLE_JDK_HOME="$COMPONENT/$ISABELLE_JAVA_PLATFORM/Contents/Home" 86 ;; 87esac 88""" 89 90 91 /* extract archive */ 92 93 def extract_archive(dir: Path, archive: Path): (Version, JDK_Platform) = 94 { 95 try { 96 val tmp_dir = dir + Path.explode("tmp") 97 Isabelle_System.mkdirs(tmp_dir) 98 Isabelle_System.gnutar( 99 "-C " + File.bash_path(tmp_dir) + " -xzf " + File.bash_path(archive)).check 100 val dir_entry = 101 File.read_dir(tmp_dir) match { 102 case List(s) => s 103 case _ => error("Archive contains multiple directories") 104 } 105 val version = detect_version(dir_entry) 106 107 val jdk_dir = tmp_dir + Path.explode(dir_entry) 108 val platform = 109 jdk_platforms.find(_.detect(jdk_dir)) getOrElse error("Failed to detect JDK platform") 110 111 val platform_dir = dir + Path.explode(platform.name) 112 if (platform_dir.is_dir) error("Directory already exists: " + platform_dir) 113 File.move(jdk_dir, platform_dir) 114 115 (version, platform) 116 } 117 catch { case ERROR(msg) => cat_error(msg, "The error(s) above occurred for " + archive) } 118 } 119 120 121 /* build jdk */ 122 123 def build_jdk( 124 archives: List[Path], 125 progress: Progress = No_Progress, 126 target_dir: Path = Path.current) 127 { 128 if (Platform.is_windows) error("Cannot build jdk on Windows") 129 130 Isabelle_System.with_tmp_dir("jdk")(dir => 131 { 132 progress.echo("Extracting ...") 133 val extracted = archives.map(extract_archive(dir, _)) 134 135 val version = 136 extracted.map(_._1).toSet.toList match { 137 case List(version) => version 138 case Nil => error("No archives") 139 case versions => 140 error("Archives contain multiple JDK versions: " + 141 commas_quote(versions.map(_.short))) 142 } 143 144 val missing_platforms = 145 jdk_platforms.filterNot(p1 => extracted.exists({ case (_, p2) => p1.name == p2.name })) 146 if (missing_platforms.nonEmpty) 147 error("Missing platforms: " + commas_quote(missing_platforms.map(_.name))) 148 149 val jdk_name = "jdk-" + version.short 150 val jdk_path = Path.explode(jdk_name) 151 val component_dir = dir + jdk_path 152 153 Isabelle_System.mkdirs(component_dir + Path.explode("etc")) 154 File.write(component_dir + Path.explode("etc/settings"), settings) 155 File.write(component_dir + Path.explode("README"), readme(version)) 156 157 for ((_, platform) <- extracted) 158 File.move(dir + Path.explode(platform.name), component_dir) 159 160 for (file <- File.find_files(component_dir.file, include_dirs = true)) { 161 val path = file.toPath 162 val perms = Files.getPosixFilePermissions(path) 163 perms.add(PosixFilePermission.OWNER_READ) 164 perms.add(PosixFilePermission.GROUP_READ) 165 perms.add(PosixFilePermission.OTHERS_READ) 166 perms.add(PosixFilePermission.OWNER_WRITE) 167 if (file.isDirectory) { 168 perms.add(PosixFilePermission.OWNER_WRITE) 169 perms.add(PosixFilePermission.OWNER_EXECUTE) 170 perms.add(PosixFilePermission.GROUP_EXECUTE) 171 perms.add(PosixFilePermission.OTHERS_EXECUTE) 172 } 173 Files.setPosixFilePermissions(path, perms) 174 } 175 176 File.find_files((component_dir + Path.explode("x86_64-darwin")).file, 177 file => file.getName.startsWith("._")).foreach(_.delete) 178 179 progress.echo("Sharing ...") 180 val main_dir :: other_dirs = 181 jdk_platforms.map(platform => (component_dir + Path.explode(platform.name)).file.toPath) 182 for { 183 file1 <- File.find_files(main_dir.toFile).iterator 184 path1 = file1.toPath 185 dir2 <- other_dirs.iterator 186 } { 187 val path2 = dir2.resolve(main_dir.relativize(path1)) 188 val file2 = path2.toFile 189 if (!Files.isSymbolicLink(path2) && file2.isFile && File.eq_content(file1, file2)) { 190 file2.delete 191 Files.createLink(path2, path1) 192 } 193 } 194 195 progress.echo("Archiving ...") 196 Isabelle_System.gnutar("--owner=root --group=root -C " + File.bash_path(dir) + 197 " -czf " + File.bash_path(target_dir + jdk_path.ext("tar.gz")) + " " + jdk_name).check 198 }) 199 } 200 201 202 /* Isabelle tool wrapper */ 203 204 val isabelle_tool = 205 Isabelle_Tool("build_jdk", "build Isabelle jdk component from original platform installations", 206 args => 207 { 208 var target_dir = Path.current 209 210 val getopts = Getopts(""" 211Usage: isabelle build_jdk [OPTIONS] ARCHIVES... 212 213 Options are: 214 -D DIR target directory (default ".") 215 216 Build jdk component from tar.gz archives, with original jdk installations 217 for x86_64 Linux, Windows, Mac OS X. 218""", 219 "D:" -> (arg => target_dir = Path.explode(arg))) 220 221 val more_args = getopts(args) 222 if (more_args.isEmpty) getopts.usage() 223 224 val archives = more_args.map(Path.explode(_)) 225 val progress = new Console_Progress() 226 227 build_jdk(archives = archives, progress = progress, target_dir = target_dir) 228 }, admin = true) 229} 230