src/Pure/Admin/build_release.scala
changeset 69410 c071fcec4323
parent 69406 5e5f1109c783
child 69411 c84ff2f2d8a3
equal deleted inserted replaced
69409:e7a5340128f0 69410:c071fcec4323
    10 object Build_Release
    10 object Build_Release
    11 {
    11 {
    12   /** release info **/
    12   /** release info **/
    13 
    13 
    14   sealed case class Bundle_Info(
    14   sealed case class Bundle_Info(
    15     platform_family: String,
    15     platform: Platform.Family.Value,
    16     platform_description: String,
    16     platform_description: String,
    17     main: String,
    17     main: String,
    18     fallback: Option[String])
    18     fallback: Option[String])
    19   {
    19   {
    20     def names: List[String] = main :: fallback.toList
    20     def names: List[String] = main :: fallback.toList
    35     def other_isabelle(dir: Path): Other_Isabelle =
    35     def other_isabelle(dir: Path): Other_Isabelle =
    36       Other_Isabelle(dir + Path.explode(dist_name),
    36       Other_Isabelle(dir + Path.explode(dist_name),
    37         isabelle_identifier = dist_name + "-build",
    37         isabelle_identifier = dist_name + "-build",
    38         progress = progress)
    38         progress = progress)
    39 
    39 
    40     def bundle_info(platform_family: String): Bundle_Info =
    40     def bundle_info(platform: Platform.Family.Value): Bundle_Info =
    41       platform_family match {
    41       platform match {
    42         case "linux" => Bundle_Info("linux", "Linux", dist_name + "_app.tar.gz", None)
    42         case Platform.Family.linux =>
    43         case "windows" => Bundle_Info("windows", "Windows", dist_name + ".exe", None)
    43           Bundle_Info(platform, "Linux", dist_name + "_app.tar.gz", None)
    44         case "macos" =>
    44         case Platform.Family.macos =>
    45           Bundle_Info("macos", "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 + "_dmg.tar.gz"))
    46         case _ => error("Unknown platform family " + quote(platform_family))
    46         case Platform.Family.windows =>
       
    47           Bundle_Info(platform, "Windows", dist_name + ".exe", None)
    47       }
    48       }
    48   }
    49   }
    49 
    50 
    50 
    51 
    51 
    52 
   133   }
   134   }
   134 
   135 
   135 
   136 
   136   /* bundled components */
   137   /* bundled components */
   137 
   138 
   138   class Bundled(platform: String = "")
   139   class Bundled(platform: Option[Platform.Family.Value] = None)
   139   {
   140   {
   140     def detect(s: String): Boolean =
   141     def detect(s: String): Boolean =
   141       s.startsWith("#bundled") && !s.startsWith("#bundled ")
   142       s.startsWith("#bundled") && !s.startsWith("#bundled ")
   142 
   143 
   143     def apply(name: String): String =
   144     def apply(name: String): String =
   144       "#bundled" + (if (platform == "") "" else "-" + platform) + ":" + name
   145       "#bundled" + (platform match { case None => "" case Some(plat) => "-" + plat }) + ":" + name
   145 
   146 
   146     private val Pattern1 = ("""^#bundled:(.*)$""").r
   147     private val Pattern1 = ("""^#bundled:(.*)$""").r
   147     private val Pattern2 = ("""^#bundled-(.*):(.*)$""").r
   148     private val Pattern2 = ("""^#bundled-(.*):(.*)$""").r
   148 
   149 
   149     def unapply(s: String): Option[String] =
   150     def unapply(s: String): Option[String] =
   150       s match {
   151       s match {
   151         case Pattern1(name) => Some(name)
   152         case Pattern1(name) => Some(name)
   152         case Pattern2(platform1, name) if platform == platform1 => Some(name)
   153         case Pattern2(Platform.Family(plat), name) if platform == Some(plat) => Some(name)
   153         case _ => None
   154         case _ => None
   154       }
   155       }
   155   }
   156   }
   156 
   157 
   157   def record_bundled_components(dir: Path)
   158   def record_bundled_components(dir: Path)
   158   {
   159   {
   159     val catalogs =
   160     val catalogs =
   160       List("main", "bundled").map((_, new Bundled())) :::
   161       List("main", "bundled").map((_, new Bundled())) :::
   161       default_platform_families.flatMap(platform =>
   162       default_platform_families.flatMap(platform =>
   162         List(platform, "bundled-" + platform).map((_, new Bundled(platform = platform))))
   163         List(platform.toString, "bundled-" + platform.toString).
       
   164           map((_, new Bundled(platform = Some(platform)))))
   163 
   165 
   164     File.append(Components.components(dir),
   166     File.append(Components.components(dir),
   165       terminate_lines("#bundled components" ::
   167       terminate_lines("#bundled components" ::
   166         (for {
   168         (for {
   167           (catalog, bundled) <- catalogs.iterator
   169           (catalog, bundled) <- catalogs.iterator
   170           line <- split_lines(File.read(path))
   172           line <- split_lines(File.read(path))
   171           if line.nonEmpty && !line.startsWith("#") && !line.startsWith("jedit_build")
   173           if line.nonEmpty && !line.startsWith("#") && !line.startsWith("jedit_build")
   172         } yield bundled(line)).toList))
   174         } yield bundled(line)).toList))
   173   }
   175   }
   174 
   176 
   175   def get_bundled_components(dir: Path, platform: String): (List[String], String) =
   177   def get_bundled_components(dir: Path, platform: Platform.Family.Value): (List[String], String) =
   176   {
   178   {
   177     val Bundled = new Bundled(platform)
   179     val Bundled = new Bundled(platform = Some(platform))
   178     val components =
   180     val components =
   179       for {
   181       for {
   180         Bundled(name) <- Components.read_components(dir)
   182         Bundled(name) <- Components.read_components(dir)
   181         if !name.startsWith("jedit_build")
   183         if !name.startsWith("jedit_build")
   182       } yield name
   184       } yield name
   183     val jdk_component =
   185     val jdk_component =
   184       components.find(_.startsWith("jdk")) getOrElse error("Missing jdk component")
   186       components.find(_.startsWith("jdk")) getOrElse error("Missing jdk component")
   185     (components, jdk_component)
   187     (components, jdk_component)
   186   }
   188   }
   187 
   189 
   188   def activate_bundled_components(dir: Path, platform: String)
   190   def activate_bundled_components(dir: Path, platform: Platform.Family.Value)
   189   {
   191   {
   190     val Bundled = new Bundled(platform)
   192     val Bundled = new Bundled(platform = Some(platform))
   191     Components.write_components(dir,
   193     Components.write_components(dir,
   192       Components.read_components(dir).flatMap(line =>
   194       Components.read_components(dir).flatMap(line =>
   193         line match {
   195         line match {
   194           case Bundled(name) =>
   196           case Bundled(name) =>
   195             if (Components.check_dir(Components.contrib(dir, name)))
   197             if (Components.check_dir(Components.contrib(dir, name)))
   220     Isabelle_System.gnutar(args, cwd = dir.file).check
   222     Isabelle_System.gnutar(args, cwd = dir.file).check
   221 
   223 
   222   private def tar_options: String =
   224   private def tar_options: String =
   223     if (Platform.is_macos) "--owner=root --group=staff" else "--owner=root --group=root"
   225     if (Platform.is_macos) "--owner=root --group=staff" else "--owner=root --group=root"
   224 
   226 
   225   private val default_platform_families = List("linux", "windows", "macos")
   227   private val default_platform_families =
       
   228     List(Platform.Family.linux, Platform.Family.windows, Platform.Family.macos)
   226 
   229 
   227   def build_release(base_dir: Path,
   230   def build_release(base_dir: Path,
   228     options: Options,
   231     options: Options,
   229     components_base: Option[Path] = None,
   232     components_base: Option[Path] = None,
   230     progress: Progress = No_Progress,
   233     progress: Progress = No_Progress,
   231     rev: String = "",
   234     rev: String = "",
   232     afp_rev: String = "",
   235     afp_rev: String = "",
   233     official_release: Boolean = false,
   236     official_release: Boolean = false,
   234     proper_release_name: Option[String] = None,
   237     proper_release_name: Option[String] = None,
   235     platform_families: List[String] = default_platform_families,
   238     platform_families: List[Platform.Family.Value] = default_platform_families,
   236     website: Option[Path] = None,
   239     website: Option[Path] = None,
   237     build_library: Boolean = false,
   240     build_library: Boolean = false,
   238     parallel_jobs: Int = 1,
   241     parallel_jobs: Int = 1,
   239     remote_mac: String = ""): Release =
   242     remote_mac: String = ""): Release =
   240   {
   243   {
   394       val bundle_archive = release.dist_dir + Path.explode(bundle)
   397       val bundle_archive = release.dist_dir + Path.explode(bundle)
   395       if (bundle_archive.is_file)
   398       if (bundle_archive.is_file)
   396         progress.echo_warning("Application bundle already exists: " + bundle_archive)
   399         progress.echo_warning("Application bundle already exists: " + bundle_archive)
   397       else {
   400       else {
   398         val isabelle_name = release.dist_name
   401         val isabelle_name = release.dist_name
   399         val platform = bundle_info.platform_family
   402         val platform = bundle_info.platform
   400 
   403 
   401         progress.echo("\nApplication bundle for " + platform + ": " + bundle_archive)
   404         progress.echo("\nApplication bundle for " + platform + ": " + bundle_archive)
   402 
   405 
   403         Isabelle_System.with_tmp_dir("build_release")(tmp_dir =>
   406         Isabelle_System.with_tmp_dir("build_release")(tmp_dir =>
   404         {
   407         {
   454           val jedit_props = Path.explode("src/Tools/jEdit/dist/properties/jEdit.props")
   457           val jedit_props = Path.explode("src/Tools/jEdit/dist/properties/jEdit.props")
   455 
   458 
   456 
   459 
   457           // platform-specific setup (inside archive)
   460           // platform-specific setup (inside archive)
   458 
   461 
   459           if (platform == "linux") {
   462           if (platform == Platform.Family.linux) {
   460             File.write(isabelle_target + Path.explode(isabelle_name + ".options"),
   463             File.write(isabelle_target + Path.explode(isabelle_name + ".options"),
   461               terminate_lines(java_options_title :: java_options))
   464               terminate_lines(java_options_title :: java_options))
   462 
   465 
   463             val isabelle_run = isabelle_target + Path.explode(isabelle_name + ".run")
   466             val isabelle_run = isabelle_target + Path.explode(isabelle_name + ".run")
   464             File.write(isabelle_run,
   467             File.write(isabelle_run,
   471             val linux_app = isabelle_target + Path.explode("contrib/linux_app")
   474             val linux_app = isabelle_target + Path.explode("contrib/linux_app")
   472             File.move(linux_app + Path.explode("Isabelle"),
   475             File.move(linux_app + Path.explode("Isabelle"),
   473               isabelle_target + Path.explode(isabelle_name))
   476               isabelle_target + Path.explode(isabelle_name))
   474             Isabelle_System.rm_tree(linux_app)
   477             Isabelle_System.rm_tree(linux_app)
   475           }
   478           }
   476           else if (platform == "macos") {
   479           else if (platform == Platform.Family.macos) {
   477             File.move(isabelle_target + Path.explode("contrib/macos_app"), tmp_dir)
   480             File.move(isabelle_target + Path.explode("contrib/macos_app"), tmp_dir)
   478             File.write(isabelle_target + jedit_props,
   481             File.write(isabelle_target + jedit_props,
   479               File.read(isabelle_target + jedit_props)
   482               File.read(isabelle_target + jedit_props)
   480                 .replaceAll("lookAndFeel=.*", "lookAndFeel=com.apple.laf.AquaLookAndFeel")
   483                 .replaceAll("lookAndFeel=.*", "lookAndFeel=com.apple.laf.AquaLookAndFeel")
   481                 .replaceAll("delete-line.shortcut=.*", "delete-line.shortcut=C+d")
   484                 .replaceAll("delete-line.shortcut=.*", "delete-line.shortcut=C+d")
   482                 .replaceAll("delete.shortcut2=.*", "delete.shortcut2=A+d")
   485                 .replaceAll("delete.shortcut2=.*", "delete.shortcut2=A+d")
   483                 .replaceAll("plugin-blacklist.MacOSX.jar=true", "plugin-blacklist.MacOSX.jar="))
   486                 .replaceAll("plugin-blacklist.MacOSX.jar=true", "plugin-blacklist.MacOSX.jar="))
   484           }
   487           }
   485           else if (platform == "windows") {
   488           else if (platform == Platform.Family.windows) {
   486             val app_template = Path.explode("~~/Admin/Windows/launch4j")
   489             val app_template = Path.explode("~~/Admin/Windows/launch4j")
   487             val cygwin_template = Path.explode("~~/Admin/Windows/Cygwin")
   490             val cygwin_template = Path.explode("~~/Admin/Windows/Cygwin")
   488 
   491 
   489             File.move(isabelle_target + Path.explode("contrib/windows_app"), tmp_dir)
   492             File.move(isabelle_target + Path.explode("contrib/windows_app"), tmp_dir)
   490 
   493 
   563 
   566 
   564           // platform-specific application (outside archive)
   567           // platform-specific application (outside archive)
   565 
   568 
   566           progress.echo("Application for " + platform + " ...")
   569           progress.echo("Application for " + platform + " ...")
   567 
   570 
   568           if (platform == "linux") {
   571           if (platform == Platform.Family.linux) {
   569             File.link(
   572             File.link(
   570               Path.explode(isabelle_name + "_linux.tar.gz"),
   573               Path.explode(isabelle_name + "_linux.tar.gz"),
   571               release.dist_dir + Path.explode(isabelle_name + "_app.tar.gz"),
   574               release.dist_dir + Path.explode(isabelle_name + "_app.tar.gz"),
   572               force = true)
   575               force = true)
   573           }
   576           }
   574           else if (platform == "macos") {
   577           else if (platform == Platform.Family.macos) {
   575             val dmg_dir = tmp_dir + Path.explode("macos_app/dmg")
   578             val dmg_dir = tmp_dir + Path.explode("macos_app/dmg")
   576             val app_dir = dmg_dir + Path.explode(isabelle_name + ".app")
   579             val app_dir = dmg_dir + Path.explode(isabelle_name + ".app")
   577             File.move(dmg_dir + Path.explode("Isabelle.app"), app_dir)
   580             File.move(dmg_dir + Path.explode("Isabelle.app"), app_dir)
   578 
   581 
   579             val app_contents = app_dir + Path.explode("Contents")
   582             val app_contents = app_dir + Path.explode("Contents")
   633                   })
   636                   })
   634                 })
   637                 })
   635               case _ =>
   638               case _ =>
   636             }
   639             }
   637           }
   640           }
   638           else if (platform == "windows") {
   641           else if (platform == Platform.Family.windows) {
   639             val exe_archive = tmp_dir + Path.explode(isabelle_name + ".7z")
   642             val exe_archive = tmp_dir + Path.explode(isabelle_name + ".7z")
   640             exe_archive.file.delete
   643             exe_archive.file.delete
   641 
   644 
   642             execute(tmp_dir,
   645             execute(tmp_dir,
   643               "7z -y -bd a " + File.bash_path(exe_archive) + " " + Bash.string(isabelle_name))
   646               "7z -y -bd a " + File.bash_path(exe_archive) + " " + Bash.string(isabelle_name))
   692         progress.echo_warning("Library archive already exists: " + release.isabelle_library_archive)
   695         progress.echo_warning("Library archive already exists: " + release.isabelle_library_archive)
   693       }
   696       }
   694       else {
   697       else {
   695         Isabelle_System.with_tmp_dir("build_release")(tmp_dir =>
   698         Isabelle_System.with_tmp_dir("build_release")(tmp_dir =>
   696           {
   699           {
   697             val platform = Isabelle_System.getenv_strict("ISABELLE_PLATFORM_FAMILY")
       
   698             val bundle =
   700             val bundle =
   699               release.dist_dir + Path.explode(release.dist_name + "_" + platform + ".tar.gz")
   701               release.dist_dir + Path.explode(release.dist_name + "_" + Platform.family + ".tar.gz")
   700             execute_tar(tmp_dir, "-xzf " + File.bash_path(bundle))
   702             execute_tar(tmp_dir, "-xzf " + File.bash_path(bundle))
   701 
   703 
   702             val other_isabelle = release.other_isabelle(tmp_dir)
   704             val other_isabelle = release.other_isabelle(tmp_dir)
   703 
   705 
   704             Isabelle_System.mkdirs(other_isabelle.etc)
   706             Isabelle_System.mkdirs(other_isabelle.etc)
   765         "R:" -> (arg => proper_release_name = Some(arg)),
   767         "R:" -> (arg => proper_release_name = Some(arg)),
   766         "W:" -> (arg => website = Some(Path.explode(arg))),
   768         "W:" -> (arg => website = Some(Path.explode(arg))),
   767         "j:" -> (arg => parallel_jobs = Value.Int.parse(arg)),
   769         "j:" -> (arg => parallel_jobs = Value.Int.parse(arg)),
   768         "l" -> (_ => build_library = true),
   770         "l" -> (_ => build_library = true),
   769         "o:" -> (arg => options = options + arg),
   771         "o:" -> (arg => options = options + arg),
   770         "p:" -> (arg => platform_families = space_explode(',', arg)),
   772         "p:" -> (arg => platform_families = space_explode(',', arg).map(Platform.Family.parse)),
   771         "r:" -> (arg => rev = arg))
   773         "r:" -> (arg => rev = arg))
   772 
   774 
   773       val more_args = getopts(args)
   775       val more_args = getopts(args)
   774       val base_dir = more_args match { case List(base_dir) => base_dir case _ => getopts.usage() }
   776       val base_dir = more_args match { case List(base_dir) => base_dir case _ => getopts.usage() }
   775 
   777