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

coursier.cli.install.Install.scala Maven / Gradle / Ivy

There is a newer version: 2.1.13
Show newest version
package coursier.cli.install

import java.io.File
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.time.Instant

import caseapp.core.app.CaseApp
import caseapp.core.RemainingArgs
import coursier.cli.setup.MaybeSetupPath
import coursier.cli.Util.ValidatedExitOnError
import coursier.install.{Channels, InstallDir, RawSource}
import coursier.launcher.internal.Windows
import coursier.paths.Util
import coursier.util.Sync

import scala.concurrent.duration.Duration

object Install extends CaseApp[InstallOptions] {

  def run(options: InstallOptions, args: RemainingArgs): Unit = {

    val params = InstallParams(options, args.all.nonEmpty).exitOnError()

    if (params.output.verbosity >= 1)
      System.err.println(s"Using install directory ${params.shared.dir}")

    if (Files.exists(params.shared.dir)) {
      if (params.output.verbosity >= 0 && !Files.isDirectory(params.shared.dir))
        System.err.println(s"Warning: ${params.shared.dir} doesn't seem to be a directory")
    } else
      Util.createDirectories(params.shared.dir)

    val pool = Sync.fixedThreadPool(params.cache.parallel)
    val cache = params.cache.cache(pool, params.output.logger())
    val noUpdateCoursierCache = params.cache.cache(pool, params.output.logger(), overrideTtl = Some(Duration.Inf))

    val graalvmHome = { version: String =>
      params.sharedJava.javaHome(cache, noUpdateCoursierCache, params.output.verbosity)
        .get(s"graalvm:$version")
    }

    val installDir = params.shared.installDir(cache)
      .withVerbosity(params.output.verbosity)
      .withNativeImageJavaHome(Some(graalvmHome))

    if (params.installChannels.nonEmpty) {

      // TODO Move to install module

      val configDir = coursier.paths.CoursierPaths.defaultConfigDirectory()
      val channelDir = new File(configDir, "channels")

      // FIXME May not be fine with concurrency (two process doing this in parallel)
      val f = Stream.from(1)
        .map { n =>
          new File(channelDir, s"channels-$n")
        }
        .filter(!_.exists())
        .head

      if (params.output.verbosity >= 1)
        System.err.println(s"Writing $f")
      Util.createDirectories(f.toPath.getParent)
      Files.write(f.toPath, params.installChannels.map(_ + "\n").mkString.getBytes(StandardCharsets.UTF_8))
    } else if (params.env.env)
      println(installDir.envUpdate.script)
    else if (params.env.disableEnv) {
      // TODO Move that to InstallDir?
      val dir = installDir.baseDir.toAbsolutePath.toString
      val updatedPath = Option(System.getenv("PATH")).flatMap { strPath =>
        val path = strPath.split(File.pathSeparator)
        if (path.contains(dir))
          Some(path.filter(_ != dir).mkString(File.pathSeparator)) // FIXME Only remove first one?
        else
          None
      }
      val script = updatedPath.fold("") { s =>
        // FIXME Escaping in s
        s"""export PATH="$s"""" + "\n"
      }
      print(script)
    } else if (params.env.setup) {
      val task = params.env.setupTask(
        installDir.envUpdate,
        params.env.envVarUpdater,
        params.output.verbosity,
        MaybeSetupPath.headerComment
      )
      task.unsafeRun()(cache.ec)
    } else {

      if (args.all.isEmpty) {
        if (params.output.verbosity >= 0 && params.installChannels.isEmpty)
          System.err.println("Nothing to install")
        sys.exit(0)
      }

      val channels = Channels(params.channels, params.shared.repositories, cache)
        .withVerbosity(params.output.verbosity)

      try {
        for (id <- args.all) {

          val appInfo = channels.appDescriptor(id).attempt.unsafeRun()(cache.ec) match {
            case Left(err: Channels.ChannelsException) =>
              System.err.println(err.getMessage)
              sys.exit(1)
            case Left(err) => throw err
            case Right(appInfo) => appInfo
          }

          val wroteSomethingOpt = installDir.createOrUpdate(
            appInfo,
            Instant.now(),
            force = params.force
          )

          wroteSomethingOpt match {
            case Some(true) =>
              if (params.output.verbosity >= 0)
                System.err.println(s"Wrote ${appInfo.source.id}")
            case Some(false) =>
              if (params.output.verbosity >= 1)
                System.err.println(s"${appInfo.source.id} doesn't need updating")
            case None =>
              if (params.output.verbosity >= 0)
                System.err.println(s"Could not install ${appInfo.source.id} (concurrent operation ongoing)")
          }
        }
      } catch {
        case e: InstallDir.InstallDirException =>
          System.err.println(e.getMessage)
          if (params.output.verbosity >= 2)
            throw e
          else
            sys.exit(1)
      }

      if (params.output.verbosity >= 0) {
        val path = Option(System.getenv("PATH"))
          .toSeq
          .flatMap(_.split(File.pathSeparatorChar).toSeq)
          .toSet

        if (!path(params.shared.dir.toAbsolutePath.toString)) {
          System.err.println(s"Warning: ${params.shared.dir} is not in your PATH")
          if (!Windows.isWindows)
            System.err.println(
              s"""To fix that, add the following line to ${ShellUtil.rcFileOpt.getOrElse("your shell configuration file")}
                 |
                 |export PATH="$$PATH:${params.shared.dir.toAbsolutePath}"""".stripMargin
            )
        }
      }
    }
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy