All Downloads are FREE. Search and download functionalities are using the official Maven repository.

bisabelle_2.12.1.0.0.source-code.Platform.scala Maven / Gradle / Ivy

There is a newer version: 1.1.0-RC3
Show newest version
package info.hupel.isabelle

import java.io.File
import java.net.URL
import java.nio.channels.{FileChannel, FileLock}
import java.nio.file._

import scala.concurrent.{ExecutionContext, Future}
import scala.util.control.NonFatal
import scala.sys.Prop

import org.apache.commons.lang3.SystemUtils

import org.log4s._

import info.hupel.isabelle.api.{BuildInfo, Version}

/**
 * Detection of the machine's [[Platform platform]].
 */
object Platform {

  /** Universal Linux platform for both 32- and 64-bit machines. */
  case object Linux extends OfficialPlatform("linux") {
    def localStorage =
      Prop[File]("user.home").value.toPath.resolve(".local/share/libisabelle").toAbsolutePath
  }

  /** Windows platform for both 32- and 64-bit machines. */
  case object Windows extends OfficialPlatform("windows") {
    def localStorage =
      Paths.get(sys.env("LOCALAPPDATA")).resolve("libisabelle").toAbsolutePath
  }

  /** Universal OS X platform for both 32- and 64-bit machines. */
  case object OSX extends OfficialPlatform("macos") {
    def localStorage =
      Prop[File]("user.home").value.toPath.resolve("Library/Preferences/libisabelle").toAbsolutePath
  }

  /**
   * Arbitrary platform based on a [[Platform#localStorage local storage]]
   * path.
   *
   * The main purpose is when using a custom (unmanaged) Isabelle installation.
   */
  def genericPlatform(localStorage0: Path): Platform =
    new Platform {
      val localStorage = localStorage0.toAbsolutePath
      def withLocalStorage(path: Path) = genericPlatform(path)
    }

  /**
   * Make an educated guess at the [[Platform platform]], not guaranteed to be
   * correct.
   */
  def guess: Option[OfficialPlatform] =
    if (SystemUtils.IS_OS_LINUX)
      Some(Platform.Linux)
    else if (SystemUtils.IS_OS_WINDOWS)
      Some(Platform.Windows)
    else if (SystemUtils.IS_OS_MAC_OSX)
      Some(Platform.OSX)
    else
      None

}

/**
 * The underlying operating system platform with knowlege of a
 * [[localStorage local storage]] path.
 *
 * It is recommended to obtain instances via the
 * [[Platform$ companion object]].
 */
sealed abstract class Platform {

  private val logger = getLogger

  /** Path where `libisabelle` stores files downloaded from the Internet. */
  def localStorage: Path

  def withLocalStorage(path: Path): Platform

  final def withIsabelleVersion(path: Path, version: Version, resolved: Boolean): Path = version match {
    case Version.Stable(_) if !resolved => path
    case Version.Stable(identifier) => path.resolve(s"Isabelle${identifier}")
    case Version.Devel(identifier) => path.resolve("devel").resolve(identifier) // always resolve devel
  }

  final def setupStorage: Path =
    localStorage.resolve("setups")

  final def setupStorage(version: Version, resolved: Boolean): Path =
    withIsabelleVersion(setupStorage, version, resolved)

  final def versionedStorage: Path =
    localStorage.resolve(s"v${BuildInfo.version}")

  final def resourcesStorage(version: Version): Path =
    withIsabelleVersion(versionedStorage.resolve("resources"), version, true)

  final def lockFile: Path =
    localStorage.resolve(".lock")

  final def userStorage(version: Version): Path =
    withIsabelleVersion(localStorage.resolve("user"), version, true)

  private def acquireLock(): Option[FileLock] = {
    if (!Files.isDirectory(localStorage)) // https://github.com/larsrh/libisabelle/issues/71
      Files.createDirectories(localStorage)
    FileChannel.open(lockFile, StandardOpenOption.CREATE, StandardOpenOption.WRITE).tryLock() match {
      case null =>
        logger.warn("lock could not be acquired")
        None
      case lock =>
        Some(lock)
    }
  }

  def withLock[A](f: () => A): Option[A] = acquireLock().map { lock =>
    try {
      f()
    }
    finally {
      lock.close()
    }
  }

  def withAsyncLock[A](f: () => Future[A])(implicit ec: ExecutionContext): Future[A] = acquireLock() match {
    case None =>
      Future.failed(new RuntimeException("lock could not be acquired"))
    case Some(lock) =>
      f().map { a =>
        try { lock.close() } catch { case NonFatal(_) => }
        a
      }
    }

}

/**
 * A `[[Platform]]` with known archive location.
 *
 * Official platforms can be installed and bootstrapped automatically via the
 * appropriate methods in [[info.hupel.isabelle.setup.Setup$ `Setup`]].
 */
sealed abstract class OfficialPlatform private[isabelle](val name: String) extends Platform {

  /** Default base URL pointing to the standard Isabelle server. */
  protected def baseURL(version: Version.Stable) =
    s"https://isabelle.in.tum.de/website-Isabelle${version.identifier}/dist/Isabelle${version.identifier}"

  /**
   * HTTP location containing an archive of the requested
   * [[info.hupel.isabelle.api.Version version]] for this platform.
   */
  def url(version: Version.Stable): URL =
    new URL(s"${baseURL(version)}_$name.tar.gz")

  final def withLocalStorage(path: Path): OfficialPlatform =
    new OfficialPlatform(name) {
      val localStorage = path
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy