diff --git a/cli/src/main/scala/scala/scalanative/cli/ScalaNativeP.scala b/cli/src/main/scala/scala/scalanative/cli/ScalaNativeP.scala index 00b633a..b779ac4 100644 --- a/cli/src/main/scala/scala/scalanative/cli/ScalaNativeP.scala +++ b/cli/src/main/scala/scala/scalanative/cli/ScalaNativeP.scala @@ -9,6 +9,12 @@ import scala.scalanative.cli.options._ import scala.scalanative.nir.Global 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.scalanative.nir.Defn +import scala.annotation.tailrec +import java.nio.ByteBuffer object ScalaNativeP extends CaseApp[POptions] { @@ -19,7 +25,10 @@ object ScalaNativeP extends CaseApp[POptions] { } if (args.all.isEmpty) { - System.err.println("Required NIR file not specified.") + if (options.fromPath) + System.err.println("Required NIR file not specified.") + else + System.err.println("Required class/object not specified.") exit(1) } @@ -32,6 +41,14 @@ object ScalaNativeP extends CaseApp[POptions] { System.err.println(s"Ignoring non existing path: $path") } + if (options.fromPath) printFromFiles(classpath, args.all) + else printFromNames(classpath, args.all) + } + + private def printFromNames( + classpath: List[Path], + args: Seq[String] + ): Unit = { Scope { implicit scope => val classLoader = ClassLoader.fromDisk { @@ -39,22 +56,66 @@ object ScalaNativeP extends CaseApp[POptions] { } for { - fileName <- args.all + className <- args } { - classLoader.load(Global.Top(fileName)) match { - case Some(defns) => - defns - .sortBy(_.name.mangle) - .foreach { d => - println(d.show) - println() - } - case None => fail(s"Not found class/object with name `${fileName}`") + classLoader.load(Global.Top(className)) match { + case Some(defns) => printNIR(defns) + case None => fail(s"Not found class/object with name `${className}`") } } } } + private def printFromFiles( + classpath: List[Path], + args: Seq[String] + ): 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 findAndRead( + classpath: Stream[VirtualDirectory], + path: Path + ): Option[ByteBuffer] = { + classpath match { + case Stream.Empty => None + case dir #:: tail => + val found = dir.files + .find(virtualDirPathMatches(_, path)) + .map(dir.read(_)) + if (found.isEmpty) findAndRead(tail, path) + else found + } + } + for { + fileName <- args + path = Paths.get(fileName).normalize() + content <- findAndRead(cp, path) + .orElse(fail(s"Not found file with name `${fileName}`")) + } { + val defns = deserializeBinary(content, fileName) + printNIR(defns) + } + } + } + + private def printNIR(defns: Seq[Defn]) = + defns + .sortBy(_.name.mangle) + .foreach { d => + println(d.show) + println() + } + private def fail(msg: String): Nothing = { Console.err.println(msg) exit(1) diff --git a/cli/src/main/scala/scala/scalanative/cli/options/POptions.scala b/cli/src/main/scala/scala/scalanative/cli/options/POptions.scala index 1036331..079b89b 100644 --- a/cli/src/main/scala/scala/scalanative/cli/options/POptions.scala +++ b/cli/src/main/scala/scala/scalanative/cli/options/POptions.scala @@ -9,6 +9,10 @@ case class POptions( @ExtraName("cp") @ValueDescription("") classpath: List[String] = "." :: Nil, + @HelpMessage( + "Instead of passing class/object names, pass NIR file paths." + ) + fromPath: Boolean = false, @Recurse misc: MiscOptions ) diff --git a/cli/src/sbt-test/integration/standalone/test b/cli/src/sbt-test/integration/standalone/test index 71cf49a..175abc3 100755 --- a/cli/src/sbt-test/integration/standalone/test +++ b/cli/src/sbt-test/integration/standalone/test @@ -9,6 +9,13 @@ $ exists Foo$.nir -> runScript scala-native-p not.existing.Class +> runScript scala-native-p --from-path Foo.nir +-> runScript scala-native-p --from-path notExisting.nir + +> runExec jar cf inside.jar Foo.class Foo.nir Foo$.class Foo$.nir +> runScript scala-native-p --cp inside.jar --from-path Foo.nir Foo$.nir +-> runScript scala-native-p --cp inside.jar --from-path Main$.nir + > runScript scala-native-ld --main Main . -o scala-native-out.exe -v -v $ exists scala-native-out.exe > runExec ./scala-native-out.exe