Skip to content

Commit

Permalink
Use external binary to extract class name from stdin Java sources
Browse files Browse the repository at this point in the history
  • Loading branch information
alexarchambault committed Jun 2, 2022
1 parent 5ab04a5 commit 80a6ecf
Show file tree
Hide file tree
Showing 15 changed files with 166 additions and 120 deletions.
32 changes: 2 additions & 30 deletions build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ object `cli-options` extends CliOptions
object `build-macros` extends Cross[BuildMacros](Scala.mainVersions: _*)
object options extends Cross[Options](Scala.mainVersions: _*)
object scalaparse extends ScalaParse
object javaparse extends JavaParse
object directives extends Cross[Directives](Scala.mainVersions: _*)
object core extends Cross[Core](Scala.mainVersions: _*)
object `build-module` extends Cross[Build](Scala.mainVersions: _*)
Expand Down Expand Up @@ -400,6 +399,7 @@ class Core(val crossScalaVersion: String) extends BuildLikeModule {
| def defaultGraalVMVersion = "${deps.graalVmVersion}"
|
| def scalaCliSigningVersion = "${Deps.signingCli.dep.version}"
| def javaClassNameVersion = "${Deps.javaClassName.dep.version}"
|
| def libsodiumVersion = "${deps.libsodiumVersion}"
| def libsodiumjniVersion = "${Deps.libsodiumjni.dep.version}"
Expand Down Expand Up @@ -498,34 +498,6 @@ trait ScalaParse extends SbtModule with ScalaCliPublishModule with ScalaCliCompi
def scalaVersion = Scala.scala213
}

trait JavaParse extends SbtModule with ScalaCliPublishModule with ScalaCliCompile {
def ivyDeps = super.ivyDeps() ++ Agg(Deps.scala3Compiler(scalaVersion()))

// pin scala3-library suffix, so that 2.13 modules can have us as moduleDep fine
def mandatoryIvyDeps = T {
super.mandatoryIvyDeps().map { dep =>
val isScala3Lib =
dep.dep.module.organization.value == "org.scala-lang" &&
dep.dep.module.name.value == "scala3-library" &&
(dep.cross match {
case _: CrossVersion.Binary => true
case _ => false
})
if (isScala3Lib)
dep.copy(
dep = dep.dep.withModule(
dep.dep.module.withName(
coursier.ModuleName(dep.dep.module.name.value + "_3")
)
),
cross = CrossVersion.empty(dep.cross.platformed)
)
else dep
}
}
def scalaVersion = Scala.scala3
}

trait Scala3Runtime extends SbtModule with ScalaCliPublishModule with ScalaCliCompile {
def ivyDeps = super.ivyDeps()
def scalaVersion = Scala.scala3
Expand Down Expand Up @@ -561,7 +533,6 @@ class Build(val crossScalaVersion: String) extends BuildLikeModule {
def moduleDeps = Seq(
options(),
scalaparse,
javaparse,
directives(),
`scala-cli-bsp`,
`test-runner`(),
Expand All @@ -578,6 +549,7 @@ class Build(val crossScalaVersion: String) extends BuildLikeModule {
def ivyDeps = super.ivyDeps() ++ Agg(
Deps.asm,
Deps.collectionCompat,
Deps.javaClassName,
Deps.jsoniterCore,
Deps.nativeTestRunner,
Deps.osLib,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package scala.build.internal;

import com.oracle.svm.core.annotate.Substitute;
import com.oracle.svm.core.annotate.TargetClass;

@TargetClass(className = "scala.build.internal.JavaParserProxyMaker")
public final class JavaParserProxyMakerSubst {
@Substitute
public JavaParserProxy get() {
return new JavaParserProxy();
}
}
3 changes: 2 additions & 1 deletion modules/build/src/main/scala/scala/build/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,8 @@ object Build {
CrossSources.forInputs(
inputs,
Sources.defaultPreprocessors(
options.scriptOptions.codeWrapper.getOrElse(CustomCodeWrapper)
options.scriptOptions.codeWrapper.getOrElse(CustomCodeWrapper),
options.archiveCache
),
logger
)
Expand Down
10 changes: 8 additions & 2 deletions modules/build/src/main/scala/scala/build/Sources.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package scala.build

import coursier.cache.ArchiveCache
import coursier.util.Task

import scala.build.internal.CodeWrapper
import scala.build.options.{BuildOptions, Scope}
import scala.build.preprocessing.*
Expand Down Expand Up @@ -69,10 +72,13 @@ object Sources {
topWrapperLen: Int
)

def defaultPreprocessors(codeWrapper: CodeWrapper): Seq[Preprocessor] =
def defaultPreprocessors(
codeWrapper: CodeWrapper,
archiveCache: ArchiveCache[Task]
): Seq[Preprocessor] =
Seq(
ScriptPreprocessor(codeWrapper),
JavaPreprocessor,
JavaPreprocessor(archiveCache),
ScalaPreprocessor,
DataPreprocessor
)
Expand Down
3 changes: 2 additions & 1 deletion modules/build/src/main/scala/scala/build/bsp/BspImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ final class BspImpl(
CrossSources.forInputs(
inputs,
Sources.defaultPreprocessors(
buildOptions.scriptOptions.codeWrapper.getOrElse(CustomCodeWrapper)
buildOptions.scriptOptions.codeWrapper.getOrElse(CustomCodeWrapper),
buildOptions.archiveCache
),
persistentLogger
).left.map((_, Scope.Main))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package scala.build.internal

import coursier.cache.ArchiveCache
import coursier.util.Task

import scala.build.Logger
import scala.build.errors.BuildException
import scala.cli.javaclassname.JavaParser

class JavaParserProxyJvm extends JavaParserProxy {
override def className(
archiveCache: ArchiveCache[Task],
content: Array[Byte],
logger: Logger
): Either[BuildException, Option[String]] =
Right(JavaParser.parseRootPublicClassName(content))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package scala.build.internal

import coursier.cache.ArchiveCache
import coursier.util.Task

import scala.build.EitherCps.{either, value}
import scala.build.Logger
import scala.build.errors.BuildException
import scala.util.Properties

class JavaParserProxy {
def className(
archiveCache: ArchiveCache[Task],
content: Array[Byte],
logger: Logger
): Either[BuildException, Option[String]] = either {

val versionOpt = Some("0.1.0")

val platformSuffix = FetchExternalBinary.platformSuffix()
val version = versionOpt
.getOrElse(Constants.javaClassNameVersion)
val (tag, changing) =
if (version == "latest") ("launchers", true)
else ("v" + version, false)
val ext = if (Properties.isWin) ".zip" else ".gz"
val url =
s"https://github.com/scala-cli/java-class-name/releases/download/$tag/java-class-name-$platformSuffix$ext"

val binary =
value(FetchExternalBinary.fetch(url, changing, archiveCache, logger, "java-class-name"))

val source =
os.temp(content, suffix = ".java", perms = if (Properties.isWin) null else "rw-------")
logger.log(s"Running $binary $source")
val res = os.proc(binary, source).call()
val output = res.out.text().trim

if (output.isEmpty) None
else Some(output)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package scala.build.internal

class JavaParserProxyMaker {
def get(): JavaParserProxy =
new JavaParserProxyJvm
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
package scala.build.preprocessing

import com.virtuslab.using_directives.custom.model.UsingDirectiveKind
import coursier.cache.ArchiveCache
import coursier.util.Task

import java.nio.charset.StandardCharsets

import scala.build.EitherCps.{either, value}
import scala.build.errors.BuildException
import scala.build.internal.JavaParser
import scala.build.internal.JavaParserProxyMaker
import scala.build.options.BuildRequirements
import scala.build.preprocessing.ExtractedDirectives.from
import scala.build.preprocessing.ScalaPreprocessor._
import scala.build.{Inputs, Logger}

case object JavaPreprocessor extends Preprocessor {
final case class JavaPreprocessor(archiveCache: ArchiveCache[Task]) extends Preprocessor {
def preprocess(
input: Inputs.SingleElement,
logger: Logger
Expand Down Expand Up @@ -45,27 +47,37 @@ case object JavaPreprocessor extends Preprocessor {
))
})
case v: Inputs.VirtualJavaFile =>
val relPath =
if (v.isStdin) {
val fileName = JavaParser.parseRootPublicClassName(v.content).map(
_ + ".java"
).getOrElse("stdin.java")
os.sub / fileName
}
else v.subPath
val content = new String(v.content, StandardCharsets.UTF_8)
val s = PreprocessedSource.InMemory(
originalPath = Left(v.source),
relPath = relPath,
code = content,
ignoreLen = 0,
options = None,
requirements = None,
scopedRequirements = Nil,
mainClassOpt = None,
scopePath = v.scopePath
)
Some(Right(Seq(s)))
val res = either {
val relPath =
if (v.isStdin) {
val classNameOpt = value {
(new JavaParserProxyMaker).get().className(
archiveCache,
v.content,
logger
)
}
val fileName = classNameOpt
.map(_ + ".java")
.getOrElse("stdin.java")
os.sub / fileName
}
else v.subPath
val content = new String(v.content, StandardCharsets.UTF_8)
val s = PreprocessedSource.InMemory(
originalPath = Left(v.source),
relPath = relPath,
code = content,
ignoreLen = 0,
options = None,
requirements = None,
scopedRequirements = Nil,
mainClassOpt = None,
scopePath = v.scopePath
)
Seq(s)
}
Some(res)

case _ => None
}
Expand Down
Loading

0 comments on commit 80a6ecf

Please sign in to comment.