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

scala.scalanative.cli.ScalaNativeP.scala Maven / Gradle / Ivy

package scala.scalanative.cli

import java.nio.file.Paths
import java.io.File
import scala.scalanative.util.Scope
import scala.scalanative.cli.options._
import scala.scalanative.nir._
import scala.scalanative.build.Config
import scala.scalanative.linker.ClassLoader
import java.nio.file.Path
import scala.scalanative.io.VirtualDirectory
import scala.scalanative.nir.serialization.deserializeBinary
import scala.annotation.tailrec

object ScalaNativeP {

  def main(args: Array[String]): Unit = {

    val parser = new scopt.OptionParser[PrinterOptions]("scala-native-p") {
      override def errorOnUnknownArgument = false

      head("scala-native-p", BuildInfo.nativeVersion)
      arg[String]("Class names")
        .hidden()
        .optional()
        .unbounded()
        .action((x, c) => c.copy(classNames = c.classNames :+ x))

      note("Help options:")
      help('h', "help")
        .text("Print this usage text and exit.")
      version("version")
        .text("Print scala-native-cli version and exit.")

      note("Other options:")
      PrinterOptions.set(this)
    }

    parser.parse(args, PrinterOptions()) match {
      case Some(config) =>
        runPrinter(config)
        sys.exit(0)
      case _ =>
        // arguments are of bad format, scopt will have displayed errors automatically
        sys.exit(1)
    }
  }

  private def runPrinter(options: PrinterOptions): Unit = {
    if (options.classNames.isEmpty) {
      if (options.fromPath)
        System.err.println("Required NIR file not specified.")
      else
        System.err.println("Required class/object not specified.")
      sys.exit(1)
    }

    val (classpath, ignoredPaths) =
      options.classpath
        .flatMap(_.split(File.pathSeparator))
        .map(Paths.get(_))
        .partition(_.toFile().exists())
    ignoredPaths.foreach { path =>
      System.err.println(s"Ignoring non existing path: $path")
    }

    if (options.fromPath) printFromFiles(classpath, options)
    else printFromNames(classpath, options)
  }

  private def printFromNames(
      classpath: List[Path],
      options: PrinterOptions
  ): Unit = {
    Scope { implicit scope =>
      val classLoader =
        ClassLoader.fromDisk {
          Config.empty.withClassPath(classpath)
        }

      for {
        className <- options.classNames
      } {
        classLoader.load(Global.Top(className)) match {
          case Some(defns) => printNIR(defns, options.verbose)
          case None => fail(s"Not found class/object with name `${className}`")
        }
      }
    }
  }

  private def printFromFiles(
      classpath: List[Path],
      options: PrinterOptions
  ): Unit = {

    Scope { implicit scope =>
      val cp = classpath.toStream.map(VirtualDirectory.real(_))
      def virtualDirPathMatches(
          virtualPath: Path,
          regularPath: Path
      ): Boolean = {
        // Paths in jars are always using Unix path denotation
        val relativeInJar = virtualPath.toString().stripPrefix("/")
        relativeInJar == regularPath.toString()
      }
      @tailrec
      def findInClasspathAndRead(
          classpath: Stream[VirtualDirectory],
          path: Path
      ): Option[(VirtualDirectory, Path)] = {
        classpath match {
          case dir #:: tail =>
            val found = dir.files
              .find(virtualDirPathMatches(_, path))
            if (found.isEmpty) findInClasspathAndRead(tail, path)
            else Some(dir -> path)
          case _ => None
        }
      }

      def tryReadFromPath(path: Path): Option[(VirtualDirectory, Path)] = {
        val file = path.toFile()
        val absPath = path.toAbsolutePath()
        // When classpath is explicitly provided don't try to read directly
        if (!options.usingDefaultClassPath || !file.exists()) None
        else
          Some(
            VirtualDirectory.real(absPath.getParent()) -> absPath.getFileName()
          )
      }

      for {
        fileName <- options.classNames
        path = Paths.get(fileName).normalize()
        (directory, dirPath) <-
          tryReadFromPath(path)
            .orElse(findInClasspathAndRead(cp, path))
            .orElse(fail(s"Not found file with name `${fileName}`"))
      } {
        val defns = deserializeBinary(directory, dirPath)
        printNIR(defns, options.verbose)
      }
    }
  }

  private def printNIR(defns: Seq[Defn], verbose: Boolean) =
    defns
      .map {
        case defn @ Defn.Define(attrs, name, ty, _, _) if !verbose =>
          Defn.Declare(attrs, name, ty)(defn.pos)
        case defn => defn
      }
      .sortBy(_.name.mangle)
      .foreach { d =>
        println(d.show)
        println()
      }

  private def fail(msg: String): Nothing = {
    Console.err.println(msg)
    sys.exit(1)
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy