src/Pure/Admin/build_release.scala
changeset 69424 840f0cadeba8
parent 69417 469724b2cb5d
child 69425 94f6ca69d983
equal deleted inserted replaced
69422:472af2d7835d 69424:840f0cadeba8
    38         progress = progress)
    38         progress = progress)
    39 
    39 
    40     def bundle_info(platform: Platform.Family.Value): Bundle_Info =
    40     def bundle_info(platform: Platform.Family.Value): Bundle_Info =
    41       platform match {
    41       platform match {
    42         case Platform.Family.linux =>
    42         case Platform.Family.linux =>
    43           Bundle_Info(platform, "Linux", dist_name + "_app.tar.gz", None)
    43           Bundle_Info(platform, "Linux", dist_name + "_linux.tar.gz", None)
    44         case Platform.Family.macos =>
    44         case Platform.Family.macos =>
    45           Bundle_Info(platform, "Mac OS X", dist_name + ".dmg", Some(dist_name + "_dmg.tar.gz"))
    45           Bundle_Info(platform, "Mac OS X", dist_name + ".dmg", Some(dist_name + "_macos.tar.gz"))
    46         case Platform.Family.windows =>
    46         case Platform.Family.windows =>
    47           Bundle_Info(platform, "Windows", dist_name + ".exe", None)
    47           Bundle_Info(platform, "Windows", dist_name + ".exe", None)
    48       }
    48       }
    49   }
    49   }
    50 
    50 
   402         progress.echo_warning("Application bundle already exists: " + bundle_archive)
   402         progress.echo_warning("Application bundle already exists: " + bundle_archive)
   403       else {
   403       else {
   404         val isabelle_name = release.dist_name
   404         val isabelle_name = release.dist_name
   405         val platform = bundle_info.platform
   405         val platform = bundle_info.platform
   406 
   406 
   407         progress.echo("\nApplication bundle for " + platform + ": " + bundle_archive)
   407         progress.echo("\nApplication bundle for " + platform)
   408 
   408 
   409         Isabelle_System.with_tmp_dir("build_release")(tmp_dir =>
   409         Isabelle_System.with_tmp_dir("build_release")(tmp_dir =>
   410         {
   410         {
   411           // release archive
   411           // release archive
   412 
   412 
   462           }
   462           }
   463 
   463 
   464           val jedit_props = Path.explode("src/Tools/jEdit/dist/properties/jEdit.props")
   464           val jedit_props = Path.explode("src/Tools/jEdit/dist/properties/jEdit.props")
   465 
   465 
   466 
   466 
   467           // platform-specific setup (inside archive)
   467           // application bundling
   468 
   468 
   469           platform match {
   469           platform match {
   470             case Platform.Family.linux =>
   470             case Platform.Family.linux =>
   471               File.write(isabelle_target + Path.explode(isabelle_name + ".options"),
   471               File.write(isabelle_target + Path.explode(isabelle_name + ".options"),
   472                 terminate_lines(java_options_title :: java_options))
   472                 terminate_lines(java_options_title :: java_options))
   482               val linux_app = isabelle_target + Path.explode("contrib/linux_app")
   482               val linux_app = isabelle_target + Path.explode("contrib/linux_app")
   483               File.move(linux_app + Path.explode("Isabelle"),
   483               File.move(linux_app + Path.explode("Isabelle"),
   484                 isabelle_target + Path.explode(isabelle_name))
   484                 isabelle_target + Path.explode(isabelle_name))
   485               Isabelle_System.rm_tree(linux_app)
   485               Isabelle_System.rm_tree(linux_app)
   486 
   486 
       
   487               val archive_name = isabelle_name + "_linux.tar.gz"
       
   488               progress.echo("Packaging " + archive_name + " ...")
       
   489               execute_tar(tmp_dir,
       
   490                 "-czf " + File.bash_path(release.dist_dir + Path.explode(archive_name)) + " " +
       
   491                 Bash.string(isabelle_name))
       
   492 
   487             case Platform.Family.macos =>
   493             case Platform.Family.macos =>
   488               File.move(isabelle_target + Path.explode("contrib/macos_app"), tmp_dir)
       
   489               File.write(isabelle_target + jedit_props,
   494               File.write(isabelle_target + jedit_props,
   490                 File.read(isabelle_target + jedit_props)
   495                 File.read(isabelle_target + jedit_props)
   491                   .replaceAll("lookAndFeel=.*", "lookAndFeel=com.apple.laf.AquaLookAndFeel")
   496                   .replaceAll("lookAndFeel=.*", "lookAndFeel=com.apple.laf.AquaLookAndFeel")
   492                   .replaceAll("delete-line.shortcut=.*", "delete-line.shortcut=C+d")
   497                   .replaceAll("delete-line.shortcut=.*", "delete-line.shortcut=C+d")
   493                   .replaceAll("delete.shortcut2=.*", "delete.shortcut2=A+d")
   498                   .replaceAll("delete.shortcut2=.*", "delete.shortcut2=A+d")
   494                   .replaceAll("plugin-blacklist.MacOSX.jar=true", "plugin-blacklist.MacOSX.jar="))
   499                   .replaceAll("plugin-blacklist.MacOSX.jar=true", "plugin-blacklist.MacOSX.jar="))
   495 
   500 
       
   501 
       
   502               // MacOS application bundle
       
   503 
       
   504               File.move(isabelle_target + Path.explode("contrib/macos_app"), tmp_dir)
       
   505               val dmg_dir = tmp_dir + Path.explode("macos_app/dmg")
       
   506 
       
   507               val isabelle_app = Path.explode(isabelle_name + ".app")
       
   508               val app_dir = dmg_dir + isabelle_app
       
   509               File.move(dmg_dir + Path.explode("Isabelle.app"), app_dir)
       
   510 
       
   511               val app_contents = app_dir + Path.explode("Contents")
       
   512               val app_resources = app_contents + Path.explode("Resources")
       
   513               File.move(tmp_dir + Path.explode(isabelle_name), app_resources)
       
   514 
       
   515               File.write(app_contents + Path.explode("Info.plist"),
       
   516                 File.read(Path.explode("~~/Admin/MacOS/Info.plist"))
       
   517                   .replaceAllLiterally("{ISABELLE_NAME}", isabelle_name)
       
   518                   .replaceAllLiterally("{JAVA_OPTIONS}",
       
   519                     terminate_lines(java_options.map(opt => "<string>" + opt + "</string>"))))
       
   520 
       
   521               for (cp <- classpath) {
       
   522                 File.link(
       
   523                   Path.explode("../Resources/" + isabelle_name + "/") + cp,
       
   524                   app_contents + Path.explode("Java"),
       
   525                   force = true)
       
   526               }
       
   527 
       
   528               File.link(
       
   529                 Path.explode("../Resources/" + isabelle_name + "/contrib/" +
       
   530                   jdk_component + "/x86_64-darwin"),
       
   531                 app_contents + Path.explode("PlugIns/bundled.jdk"),
       
   532                 force = true)
       
   533 
       
   534               File.link(
       
   535                 Path.explode("../../Info.plist"),
       
   536                 app_resources + Path.explode(isabelle_name + "/" + isabelle_name + ".plist"),
       
   537                 force = true)
       
   538 
       
   539               File.link(
       
   540                 Path.explode("Contents/Resources/" + isabelle_name),
       
   541                 app_dir + Path.explode("Isabelle"),
       
   542                 force = true)
       
   543 
       
   544 
       
   545               // application archive: dmg or .tar.gz
       
   546 
       
   547               val isabelle_dmg = Path.explode(isabelle_name + ".dmg")
       
   548               (release.dist_dir + isabelle_dmg).file.delete
       
   549 
       
   550               remote_mac match {
       
   551                 case SSH.Target(user, host) =>
       
   552                   progress.echo("Packaging " + isabelle_dmg + " (via host " + quote(host) + ") ...")
       
   553                   using(SSH.open_session(options, host = host, user = user))(ssh =>
       
   554                   {
       
   555                     val dmg_archive = Path.explode("dmg.tar")
       
   556                     execute_tar(dmg_dir, "-cf " + File.bash_path(tmp_dir + dmg_archive) + " .")
       
   557 
       
   558                     ssh.with_tmp_dir(remote_dir =>
       
   559                     {
       
   560                       val cd = "cd " + ssh.bash_path(remote_dir) + "; "
       
   561                       ssh.write_file(remote_dir + dmg_archive, tmp_dir + dmg_archive)
       
   562                       ssh.execute(
       
   563                         cd + "mkdir dmg && tar -C dmg -xf " + ssh.bash_path(dmg_archive)).check
       
   564                       ssh.execute(
       
   565                         cd + "hdiutil create -srcfolder dmg -volname Isabelle " +
       
   566                         ssh.bash_path(isabelle_dmg)).check
       
   567                       ssh.read_file(remote_dir + isabelle_dmg, release.dist_dir + isabelle_dmg)
       
   568                     })
       
   569                   })
       
   570                 case _ =>
       
   571                   val archive_name = isabelle_name + "_macos.tar.gz"
       
   572                   progress.echo("Packaging " + archive_name + " ...")
       
   573                   execute_tar(dmg_dir,
       
   574                     "-czf " + File.bash_path(release.dist_dir + Path.explode(archive_name)) + " " +
       
   575                     File.bash_path(isabelle_app))
       
   576               }
       
   577 
   496             case Platform.Family.windows =>
   578             case Platform.Family.windows =>
   497               val app_template = Path.explode("~~/Admin/Windows/launch4j")
       
   498               val cygwin_template = Path.explode("~~/Admin/Windows/Cygwin")
       
   499 
       
   500               File.move(isabelle_target + Path.explode("contrib/windows_app"), tmp_dir)
       
   501 
       
   502               File.write(isabelle_target + jedit_props,
   579               File.write(isabelle_target + jedit_props,
   503                 File.read(isabelle_target + jedit_props)
   580                 File.read(isabelle_target + jedit_props)
   504                   .replaceAll("lookAndFeel=.*",
   581                   .replaceAll("lookAndFeel=.*",
   505                     "lookAndFeel=com.sun.java.swing.plaf.windows.WindowsLookAndFeel")
   582                     "lookAndFeel=com.sun.java.swing.plaf.windows.WindowsLookAndFeel")
   506                   .replaceAll("foldPainter=.*", "foldPainter=Square"))
   583                   .replaceAll("foldPainter=.*", "foldPainter=Square"))
       
   584 
       
   585 
       
   586               // application launcher
       
   587 
       
   588               File.move(isabelle_target + Path.explode("contrib/windows_app"), tmp_dir)
       
   589 
       
   590               val app_template = Path.explode("~~/Admin/Windows/launch4j")
   507 
   591 
   508               File.write(isabelle_target + Path.explode(isabelle_name + ".l4j.ini"),
   592               File.write(isabelle_target + Path.explode(isabelle_name + ".l4j.ini"),
   509                 (java_options_title :: java_options).map(_ + "\r\n").mkString)
   593                 (java_options_title :: java_options).map(_ + "\r\n").mkString)
   510 
   594 
   511               val isabelle_xml = Path.explode("isabelle.xml")
   595               val isabelle_xml = Path.explode("isabelle.xml")
   530 
   614 
   531               File.copy(app_template + Path.explode("manifest.xml"),
   615               File.copy(app_template + Path.explode("manifest.xml"),
   532                 isabelle_target + isabelle_exe.ext("manifest"))
   616                 isabelle_target + isabelle_exe.ext("manifest"))
   533 
   617 
   534 
   618 
       
   619               // Cygwin setup
       
   620 
       
   621               val cygwin_template = Path.explode("~~/Admin/Windows/Cygwin")
       
   622 
   535               File.copy(cygwin_template + Path.explode("Cygwin-Terminal.bat"), isabelle_target)
   623               File.copy(cygwin_template + Path.explode("Cygwin-Terminal.bat"), isabelle_target)
   536 
   624 
   537               val cygwin_mirror =
   625               val cygwin_mirror =
   538                 File.read(isabelle_target + Path.explode("contrib/cygwin/isabelle/cygwin_mirror"))
   626                 File.read(isabelle_target + Path.explode("contrib/cygwin/isabelle/cygwin_mirror"))
   539 
   627 
   559                 """> contrib/cygwin/isabelle/symlinks""")
   647                 """> contrib/cygwin/isabelle/symlinks""")
   560 
   648 
   561               execute(isabelle_target, """find . -type l -exec rm "{}" ";" """)
   649               execute(isabelle_target, """find . -type l -exec rm "{}" ";" """)
   562 
   650 
   563               File.write(isabelle_target + Path.explode("contrib/cygwin/isabelle/uninitialized"), "")
   651               File.write(isabelle_target + Path.explode("contrib/cygwin/isabelle/uninitialized"), "")
   564           }
   652 
   565 
   653 
   566 
   654               // executable archive (self-extracting 7z)
   567           // archive
   655 
   568 
       
   569           val archive_name = isabelle_name + "_" + platform + ".tar.gz"
       
   570           progress.echo("Packaging " + archive_name + " ...")
       
   571           execute_tar(tmp_dir,
       
   572             "-czf " + File.bash_path(release.dist_dir + Path.explode(archive_name)) + " " +
       
   573             Bash.string(isabelle_name))
       
   574 
       
   575 
       
   576           // platform-specific application (outside archive)
       
   577 
       
   578           progress.echo("Application for " + platform + " ...")
       
   579 
       
   580           platform match {
       
   581             case Platform.Family.linux =>
       
   582               File.link(
       
   583                 Path.explode(isabelle_name + "_linux.tar.gz"),
       
   584                 release.dist_dir + Path.explode(isabelle_name + "_app.tar.gz"),
       
   585                 force = true)
       
   586 
       
   587             case Platform.Family.macos =>
       
   588               val dmg_dir = tmp_dir + Path.explode("macos_app/dmg")
       
   589               val app_dir = dmg_dir + Path.explode(isabelle_name + ".app")
       
   590               File.move(dmg_dir + Path.explode("Isabelle.app"), app_dir)
       
   591 
       
   592               val app_contents = app_dir + Path.explode("Contents")
       
   593               val app_resources = app_contents + Path.explode("Resources")
       
   594               File.move(tmp_dir + Path.explode(isabelle_name), app_resources)
       
   595 
       
   596               File.write(app_contents + Path.explode("Info.plist"),
       
   597                 File.read(Path.explode("~~/Admin/MacOS/Info.plist"))
       
   598                   .replaceAllLiterally("{ISABELLE_NAME}", isabelle_name)
       
   599                   .replaceAllLiterally("{JAVA_OPTIONS}",
       
   600                     terminate_lines(java_options.map(opt => "<string>" + opt + "</string>"))))
       
   601 
       
   602               for (cp <- classpath) {
       
   603                 File.link(
       
   604                   Path.explode("../Resources/" + isabelle_name + "/") + cp,
       
   605                   app_contents + Path.explode("Java"),
       
   606                   force = true)
       
   607               }
       
   608 
       
   609               File.link(
       
   610                 Path.explode("../Resources/" + isabelle_name + "/contrib/" +
       
   611                   jdk_component + "/x86_64-darwin"),
       
   612                 app_contents + Path.explode("PlugIns/bundled.jdk"),
       
   613                 force = true)
       
   614 
       
   615               File.link(
       
   616                 Path.explode("../../Info.plist"),
       
   617                 app_resources + Path.explode(isabelle_name + "/" + isabelle_name + ".plist"),
       
   618                 force = true)
       
   619 
       
   620               File.link(
       
   621                 Path.explode("Contents/Resources/" + isabelle_name),
       
   622                 app_dir + Path.explode("Isabelle"),
       
   623                 force = true)
       
   624 
       
   625               val dmg = Path.explode(isabelle_name + ".dmg")
       
   626               (release.dist_dir + dmg).file.delete
       
   627 
       
   628               val dmg_archive = Path.explode(isabelle_name + "_dmg.tar.gz")
       
   629               execute_tar(dmg_dir, "-czf " + File.bash_path(release.dist_dir + dmg_archive) + " .")
       
   630 
       
   631               remote_mac match {
       
   632                 case SSH.Target(user, host) =>
       
   633                   progress.echo("Building dmg on " + quote(host) + " ...")
       
   634                   using(SSH.open_session(options, host = host, user = user))(ssh =>
       
   635                   {
       
   636                     ssh.with_tmp_dir(remote_dir =>
       
   637                     {
       
   638                       val cd = "cd " + ssh.bash_path(remote_dir) + "; "
       
   639                       ssh.write_file(remote_dir + dmg_archive, release.dist_dir + dmg_archive)
       
   640                       ssh.execute(
       
   641                         cd + "mkdir root && tar -C root -xzf " + ssh.bash_path(dmg_archive)).check
       
   642                       ssh.execute(
       
   643                         cd + "hdiutil create -srcfolder root -volname Isabelle " +
       
   644                         ssh.bash_path(dmg)).check
       
   645                       ssh.read_file(remote_dir + dmg, release.dist_dir + dmg)
       
   646                     })
       
   647                   })
       
   648                 case _ =>
       
   649               }
       
   650 
       
   651             case Platform.Family.windows =>
       
   652               val exe_archive = tmp_dir + Path.explode(isabelle_name + ".7z")
   656               val exe_archive = tmp_dir + Path.explode(isabelle_name + ".7z")
   653               exe_archive.file.delete
   657               exe_archive.file.delete
   654 
   658 
   655               execute(tmp_dir,
   659               execute(tmp_dir,
   656                 "7z -y -bd a " + File.bash_path(exe_archive) + " " + Bash.string(isabelle_name))
   660                 "7z -y -bd a " + File.bash_path(exe_archive) + " " + Bash.string(isabelle_name))
   657               if (!exe_archive.is_file) error("Failed to create archive: " + exe_archive)
   661               if (!exe_archive.is_file) error("Failed to create archive: " + exe_archive)
   658 
   662 
   659               val isabelle_exe = Path.explode(isabelle_name + ".exe")
       
   660               val sfx_exe = tmp_dir + Path.explode("windows_app/7zsd_All_x64.sfx")
   663               val sfx_exe = tmp_dir + Path.explode("windows_app/7zsd_All_x64.sfx")
   661               val sfx_txt =
   664               val sfx_txt =
   662                 File.read(Path.explode("~~/Admin/Windows/Installer/sfx.txt")).
   665                 File.read(Path.explode("~~/Admin/Windows/Installer/sfx.txt")).
   663                   replaceAllLiterally("{ISABELLE_NAME}", isabelle_name)
   666                   replaceAllLiterally("{ISABELLE_NAME}", isabelle_name)
   664 
   667