404 (if (remote_mac.isEmpty) bundle_info.fallback else None) getOrElse bundle_info.main |
393 (if (remote_mac.isEmpty) bundle_info.fallback else None) getOrElse bundle_info.main |
405 val bundle_archive = release.dist_dir + Path.explode(bundle) |
394 val bundle_archive = release.dist_dir + Path.explode(bundle) |
406 if (bundle_archive.is_file) |
395 if (bundle_archive.is_file) |
407 progress.echo_warning("Application bundle already exists: " + bundle_archive) |
396 progress.echo_warning("Application bundle already exists: " + bundle_archive) |
408 else { |
397 else { |
409 progress.echo( |
398 val isabelle_name = release.dist_name |
410 "\nApplication bundle for " + bundle_info.platform_family + ": " + bundle_archive) |
399 val platform = bundle_info.platform_family |
411 progress.bash( |
400 |
412 "isabelle makedist_bundle " + File.bash_path(release.isabelle_archive) + |
401 progress.echo("\nApplication bundle for " + platform + ": " + bundle_archive) |
413 " " + Bash.string(bundle_info.platform_family) + |
402 |
414 (if (remote_mac == "") "" else " " + Bash.string(remote_mac)), |
403 Isabelle_System.with_tmp_dir("build_release")(tmp_dir => |
415 echo = true).check |
404 { |
|
405 // release archive |
|
406 |
|
407 execute_tar(tmp_dir, "-xzf " + File.bash_path(release.isabelle_archive)) |
|
408 val other_isabelle = release.other_isabelle(tmp_dir) |
|
409 val isabelle_target = other_isabelle.isabelle_home |
|
410 |
|
411 |
|
412 // bundled components |
|
413 |
|
414 progress.echo("Bundled components:") |
|
415 |
|
416 val contrib_dir = Components.contrib(isabelle_target) |
|
417 |
|
418 val (components, jdk_component) = get_bundled_components(isabelle_target, platform) |
|
419 |
|
420 Components.resolve(other_isabelle.components_base(components_base), |
|
421 components, target_dir = Some(contrib_dir), progress = progress) |
|
422 |
|
423 Components.purge(contrib_dir, platform) |
|
424 |
|
425 activate_bundled_components(isabelle_target, platform) |
|
426 |
|
427 |
|
428 // Java parameters |
|
429 |
|
430 val java_options_title = "# Java runtime options" |
|
431 val java_options: List[String] = |
|
432 (for { |
|
433 variable <- |
|
434 List( |
|
435 "ISABELLE_JAVA_SYSTEM_OPTIONS", |
|
436 "JEDIT_JAVA_SYSTEM_OPTIONS", |
|
437 "JEDIT_JAVA_OPTIONS") |
|
438 opt <- Word.explode(other_isabelle.getenv(variable)) |
|
439 } yield opt) ::: List("-Disabelle.jedit_server=" + isabelle_name) |
|
440 |
|
441 val classpath: List[Path] = |
|
442 { |
|
443 val base = isabelle_target.absolute |
|
444 Path.split(other_isabelle.getenv("ISABELLE_CLASSPATH")).map(path => |
|
445 { |
|
446 val abs_path = path.absolute |
|
447 File.relative_path(base, abs_path) match { |
|
448 case Some(rel_path) => rel_path |
|
449 case None => error("Bad ISABELLE_CLASSPATH element: " + abs_path) |
|
450 } |
|
451 }) ::: List(Path.explode("src/Tools/jEdit/dist/jedit.jar")) |
|
452 } |
|
453 |
|
454 val jedit_props = Path.explode("src/Tools/jEdit/dist/properties/jEdit.props") |
|
455 |
|
456 |
|
457 // platform-specific setup (inside archive) |
|
458 |
|
459 if (platform == "linux") { |
|
460 File.write(isabelle_target + Path.explode(isabelle_name + ".options"), |
|
461 terminate_lines(java_options_title :: java_options)) |
|
462 |
|
463 val isabelle_run = isabelle_target + Path.explode(isabelle_name + ".run") |
|
464 File.write(isabelle_run, |
|
465 File.read(Path.explode("~~/Admin/Linux/Isabelle.run")) |
|
466 .replaceAllLiterally("{CLASSPATH}", |
|
467 classpath.map("$ISABELLE_HOME/" + _).mkString(":")) |
|
468 .replaceAllLiterally("/jdk/", "/" + jdk_component + "/")) |
|
469 Isabelle_System.bash("chmod +x " + File.bash_path(isabelle_run)).check |
|
470 |
|
471 val linux_app = isabelle_target + Path.explode("contrib/linux_app") |
|
472 File.move(linux_app + Path.explode("Isabelle"), |
|
473 isabelle_target + Path.explode(isabelle_name)) |
|
474 Isabelle_System.rm_tree(linux_app) |
|
475 } |
|
476 else if (platform == "macos") { |
|
477 File.move(isabelle_target + Path.explode("contrib/macos_app"), tmp_dir) |
|
478 File.write(isabelle_target + jedit_props, |
|
479 File.read(isabelle_target + jedit_props) |
|
480 .replaceAll("lookAndFeel=.*", "lookAndFeel=com.apple.laf.AquaLookAndFeel") |
|
481 .replaceAll("delete-line.shortcut=.*", "delete-line.shortcut=C+d") |
|
482 .replaceAll("delete.shortcut2=.*", "delete.shortcut2=A+d") |
|
483 .replaceAll("plugin-blacklist.MacOSX.jar=true", "plugin-blacklist.MacOSX.jar=")) |
|
484 } |
|
485 else if (platform == "windows") { |
|
486 val app_template = Path.explode("~~/Admin/Windows/launch4j") |
|
487 val cygwin_template = Path.explode("~~/Admin/Windows/Cygwin") |
|
488 |
|
489 File.move(isabelle_target + Path.explode("contrib/windows_app"), tmp_dir) |
|
490 |
|
491 File.write(isabelle_target + jedit_props, |
|
492 File.read(isabelle_target + jedit_props) |
|
493 .replaceAll("lookAndFeel=.*", |
|
494 "lookAndFeel=com.sun.java.swing.plaf.windows.WindowsLookAndFeel") |
|
495 .replaceAll("foldPainter=.*", "foldPainter=Square")) |
|
496 |
|
497 File.write(isabelle_target + Path.explode(isabelle_name + ".l4j.ini"), |
|
498 (java_options_title :: java_options).map(_ + "\r\n").mkString) |
|
499 |
|
500 val isabelle_xml = Path.explode("isabelle.xml") |
|
501 val isabelle_exe = Path.explode(isabelle_name + ".exe") |
|
502 |
|
503 File.write(tmp_dir + isabelle_xml, |
|
504 File.read(app_template + isabelle_xml) |
|
505 .replaceAllLiterally("{ISABELLE_NAME}", isabelle_name) |
|
506 .replaceAllLiterally("{OUTFILE}", File.platform_path(isabelle_target + isabelle_exe)) |
|
507 .replaceAllLiterally("{ICON}", |
|
508 File.platform_path(app_template + Path.explode("isabelle_transparent.ico"))) |
|
509 .replaceAllLiterally("{SPLASH}", |
|
510 File.platform_path(app_template + Path.explode("isabelle.bmp"))) |
|
511 .replaceAllLiterally("{CLASSPATH}", |
|
512 cat_lines(classpath.map(cp => |
|
513 " <cp>%EXEDIR%\\" + File.platform_path(cp).replace('/', '\\') + "</cp>"))) |
|
514 .replaceAllLiterally("\\jdk\\", "\\" + jdk_component + "\\")) |
|
515 |
|
516 Isabelle_System.bash( |
|
517 "\"windows_app/launch4j-${ISABELLE_PLATFORM_FAMILY}/launch4j\" isabelle.xml", |
|
518 cwd = tmp_dir.file).check |
|
519 |
|
520 File.copy(app_template + Path.explode("manifest.xml"), |
|
521 isabelle_target + isabelle_exe.ext("manifest")) |
|
522 |
|
523 |
|
524 File.copy(cygwin_template + Path.explode("Cygwin-Terminal.bat"), isabelle_target) |
|
525 |
|
526 val cygwin_mirror = |
|
527 File.read(isabelle_target + Path.explode("contrib/cygwin/isabelle/cygwin_mirror")) |
|
528 |
|
529 val cygwin_bat = Path.explode("Cygwin-Setup.bat") |
|
530 File.write(isabelle_target + cygwin_bat, |
|
531 File.read(cygwin_template + cygwin_bat) |
|
532 .replaceAllLiterally("{MIRROR}", cygwin_mirror)) |
|
533 |
|
534 Isabelle_System.bash("chmod +x " + File.bash_path(isabelle_target + cygwin_bat)).check |
|
535 |
|
536 for (name <- List("isabelle/postinstall", "isabelle/rebaseall")) { |
|
537 val path = Path.explode(name) |
|
538 File.copy(cygwin_template + path, |
|
539 isabelle_target + Path.explode("contrib/cygwin") + path) |
|
540 } |
|
541 |
|
542 Isabelle_System.bash("""find . -type f -not -name "*.exe" -not -name "*.dll" """ + |
|
543 (if (Platform.is_macos) "-perm +100" else "-executable") + |
|
544 " -print0 > contrib/cygwin/isabelle/executables", |
|
545 cwd = isabelle_target.file).check |
|
546 |
|
547 Isabelle_System.bash("""find . -type l -exec echo "{}" ";" -exec readlink "{}" ";" """ + |
|
548 """> contrib/cygwin/isabelle/symlinks""", |
|
549 cwd = isabelle_target.file).check |
|
550 |
|
551 Isabelle_System.bash("""find . -type l -exec rm "{}" ";" """, |
|
552 cwd = isabelle_target.file).check |
|
553 |
|
554 File.write(isabelle_target + Path.explode("contrib/cygwin/isabelle/uninitialized"), "") |
|
555 } |
|
556 |
|
557 |
|
558 // archive |
|
559 |
|
560 val archive_name = isabelle_name + "_" + platform + ".tar.gz" |
|
561 progress.echo("Packaging " + archive_name) |
|
562 execute_tar(tmp_dir, |
|
563 "-czf " + File.bash_path(release.dist_dir + Path.explode(archive_name)) + " " + |
|
564 Bash.string(isabelle_name)) |
|
565 |
|
566 |
|
567 // platform-specific application (outside archive) |
|
568 |
|
569 progress.echo("Application for " + platform) |
|
570 |
|
571 if (platform == "linux") { |
|
572 Isabelle_System.bash( |
|
573 "ln -s " + Bash.string(isabelle_name + "_linux.tar.gz") + " " + |
|
574 File.bash_path(release.dist_dir + Path.explode(isabelle_name + "_app.tar.gz"))).check |
|
575 } |
|
576 else if (platform == "macos") { |
|
577 val dmg_dir = tmp_dir + Path.explode("macos_app/dmg") |
|
578 val app_dir = dmg_dir + Path.explode(isabelle_name + ".app") |
|
579 File.move(dmg_dir + Path.explode("Isabelle.app"), app_dir) |
|
580 |
|
581 val app_contents = app_dir + Path.explode("Contents") |
|
582 val app_resources = app_contents + Path.explode("Resources") |
|
583 File.move(tmp_dir + Path.explode(isabelle_name), app_resources) |
|
584 |
|
585 File.write(app_contents + Path.explode("Info.plist"), |
|
586 File.read(Path.explode("~~/Admin/MacOS/Info.plist")) |
|
587 .replaceAllLiterally("{ISABELLE_NAME}", isabelle_name) |
|
588 .replaceAllLiterally("{JAVA_OPTIONS}", |
|
589 terminate_lines(java_options.map(opt => "<string>" + opt + "</string>")))) |
|
590 |
|
591 for (cp <- classpath) { |
|
592 Isabelle_System.bash( |
|
593 "ln -sf " + |
|
594 Bash.string("../Resources/" + isabelle_name + "/") + File.bash_path(cp) + " " + |
|
595 File.bash_path(app_contents + Path.explode("Java"))).check |
|
596 } |
|
597 |
|
598 Isabelle_System.bash("ln -sf ../Resources/" + Bash.string(isabelle_name) + |
|
599 "/contrib/" + Bash.string(jdk_component) + "/x86_64-darwin " + |
|
600 File.bash_path(app_contents + Path.explode("PlugIns/bundled.jdk"))).check |
|
601 |
|
602 Isabelle_System.bash("ln -sf ../../Info.plist " + |
|
603 File.bash_path(app_resources + Path.explode(isabelle_name) + |
|
604 Path.explode(isabelle_name + ".plist"))).check |
|
605 Isabelle_System.bash("ln -sf Contents/Resources/" + Bash.string(isabelle_name) + " " + |
|
606 File.bash_path(app_dir) + "/Isabelle").check |
|
607 |
|
608 val dmg = Path.explode(isabelle_name + ".dmg") |
|
609 (release.dist_dir + dmg).file.delete |
|
610 |
|
611 val dmg_archive = Path.explode(isabelle_name + "_dmg.tar.gz") |
|
612 execute_tar(dmg_dir, "-czf " + File.bash_path(release.dist_dir + dmg_archive) + " .") |
|
613 |
|
614 remote_mac match { |
|
615 case SSH.Target(user, host) => |
|
616 progress.echo("Building dmg on " + quote(host) + " ...") |
|
617 using(SSH.open_session(options, host = host, user = user))(ssh => |
|
618 { |
|
619 ssh.with_tmp_dir(remote_dir => |
|
620 { |
|
621 val cd = "cd " + ssh.bash_path(remote_dir) + "; " |
|
622 ssh.write_file(remote_dir + dmg_archive, release.dist_dir + dmg_archive) |
|
623 ssh.execute( |
|
624 cd + "mkdir root && tar -C root -xzf " + ssh.bash_path(dmg_archive)).check |
|
625 ssh.execute( |
|
626 cd + "hdiutil create -srcfolder root -volname Isabelle " + |
|
627 ssh.bash_path(dmg)).check |
|
628 ssh.read_file(remote_dir + dmg, release.dist_dir + dmg) |
|
629 }) |
|
630 }) |
|
631 case _ => |
|
632 } |
|
633 } |
|
634 else if (platform == "windows") { |
|
635 val exe_archive = tmp_dir + Path.explode(isabelle_name + ".7z") |
|
636 exe_archive.file.delete |
|
637 |
|
638 Isabelle_System.bash("7z -y -bd a " + File.bash_path(exe_archive) + " " + |
|
639 Bash.string(isabelle_name), cwd = tmp_dir.file).check |
|
640 if (!exe_archive.is_file) error("Failed to create archive: " + exe_archive) |
|
641 |
|
642 val isabelle_exe = Path.explode(isabelle_name + ".exe") |
|
643 val sfx_exe = tmp_dir + Path.explode("windows_app/7zsd_All_x64.sfx") |
|
644 val sfx_txt = |
|
645 File.read(Path.explode("~~/Admin/Windows/Installer/sfx.txt")). |
|
646 replaceAllLiterally("{ISABELLE_NAME}", isabelle_name) |
|
647 |
|
648 Bytes.write(release.dist_dir + isabelle_exe, |
|
649 Bytes.read(sfx_exe) + Bytes(sfx_txt) + Bytes.read(exe_archive)) |
|
650 |
|
651 Isabelle_System.bash("chmod +x " + (release.dist_dir + isabelle_exe)).check |
|
652 } |
|
653 }) |
416 } |
654 } |
417 } |
655 } |
418 |
656 |
419 |
657 |
420 /* minimal website */ |
658 /* minimal website */ |
522 "O" -> (_ => official_release = true), |
760 "O" -> (_ => official_release = true), |
523 "R:" -> (arg => proper_release_name = Some(arg)), |
761 "R:" -> (arg => proper_release_name = Some(arg)), |
524 "W:" -> (arg => website = Some(Path.explode(arg))), |
762 "W:" -> (arg => website = Some(Path.explode(arg))), |
525 "j:" -> (arg => parallel_jobs = Value.Int.parse(arg)), |
763 "j:" -> (arg => parallel_jobs = Value.Int.parse(arg)), |
526 "l" -> (_ => build_library = true), |
764 "l" -> (_ => build_library = true), |
|
765 "o:" -> (arg => options = options + arg), |
527 "p:" -> (arg => platform_families = space_explode(',', arg)), |
766 "p:" -> (arg => platform_families = space_explode(',', arg)), |
528 "r:" -> (arg => rev = arg)) |
767 "r:" -> (arg => rev = arg)) |
529 |
768 |
530 val more_args = getopts(args) |
769 val more_args = getopts(args) |
531 val base_dir = more_args match { case List(base_dir) => base_dir case _ => getopts.usage() } |
770 val base_dir = more_args match { case List(base_dir) => base_dir case _ => getopts.usage() } |
532 |
771 |
533 val progress = new Console_Progress() |
772 val progress = new Console_Progress() |
534 |
773 |
535 build_release(Path.explode(base_dir), components_base = components_base, progress = progress, |
774 build_release(Path.explode(base_dir), options, components_base = components_base, |
536 rev = rev, afp_rev = afp_rev, official_release = official_release, |
775 progress = progress, rev = rev, afp_rev = afp_rev, official_release = official_release, |
537 proper_release_name = proper_release_name, website = website, |
776 proper_release_name = proper_release_name, website = website, |
538 platform_families = |
777 platform_families = |
539 if (platform_families.isEmpty) default_platform_families |
778 if (platform_families.isEmpty) default_platform_families |
540 else platform_families, |
779 else platform_families, |
541 build_library = build_library, parallel_jobs = parallel_jobs, remote_mac = remote_mac) |
780 build_library = build_library, parallel_jobs = parallel_jobs, remote_mac = remote_mac) |