Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions modules/build/src/main/scala/scala/build/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1199,6 +1199,24 @@ object Build {
)
}

if sources.hasJava && sources.hasScala && options.useBuildServer.contains(false) then {
val javaPaths = sources.paths
.filter(_._1.last.endsWith(".java"))
.map(_._1.toString) ++
sources.inMemory
.filter(_.generatedRelPath.last.endsWith(".java"))
.map(_.originalPath.fold(identity, _._2.toString))
val javaPathsList =
javaPaths.map(p => s" $p").mkString(System.lineSeparator())
logger.message(
s"""$warnPrefix With ${Console.BOLD}--server=false${Console.RESET}, .java files are not compiled to .class files.
|scalac parses .java sources for type information (cross-compilation), but without the build server (Bloop/Zinc) nothing compiles them to bytecode.
|Affected .java files:
|$javaPathsList
|Remove --server=false or compile Java files separately to avoid runtime NoClassDefFoundError.""".stripMargin
)
}

buildClient.clear()
buildClient.setGeneratedSources(scope, generatedSources)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,32 @@
package scala.build.tests

class BuildTestsScalac extends BuildTests(server = false)
class BuildTestsScalac extends BuildTests(server = false) {

test("warn about Java files in mixed compilation with --server=false") {
val recordingLogger = new RecordingLogger()
val inputs = TestInputs(
os.rel / "Side.java" ->
"""public class Side {
| public static String message = "Hello";
|}
|""".stripMargin,
os.rel / "Main.scala" ->
"""@main def main() = println(Side.message)
|""".stripMargin
)
val options = defaultScala3Options.copy(useBuildServer = Some(false))
inputs.withBuild(options, buildThreads, bloopConfigOpt, logger = Some(recordingLogger)) {
(_, _, maybeBuild) =>
assert(maybeBuild.isRight)
val hasWarning = recordingLogger.messages.exists { msg =>
msg.contains(".java files are not compiled to .class files") &&
msg.contains("--server=false") &&
msg.contains("Affected .java files")
}
assert(
hasWarning,
s"Expected warning about Java files with --server=false in: ${recordingLogger.messages.mkString("\n")}"
)
}
}
}
14 changes: 9 additions & 5 deletions modules/build/src/test/scala/scala/build/tests/TestInputs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import scala.build.compiler.{BloopCompilerMaker, SimpleScalaCompilerMaker}
import scala.build.errors.BuildException
import scala.build.input.{Inputs, ScalaCliInvokeData}
import scala.build.options.{BuildOptions, Scope}
import scala.build.{Build, BuildThreads, Builds}
import scala.build.{Build, BuildThreads, Builds, Logger}
import scala.util.Try
import scala.util.control.NonFatal

Expand Down Expand Up @@ -94,7 +94,8 @@ final case class TestInputs(
fromDirectory: Boolean = false,
buildTests: Boolean = true,
actionableDiagnostics: Boolean = false,
skipCreatingSources: Boolean = false
skipCreatingSources: Boolean = false,
logger: Option[Logger] = None
)(f: (os.Path, Inputs, Either[BuildException, Builds]) => T): T =
withCustomInputs(fromDirectory, None, skipCreatingSources) { (root, inputs) =>
val compilerMaker = bloopConfigOpt match {
Expand All @@ -108,13 +109,14 @@ final case class TestInputs(
case None =>
SimpleScalaCompilerMaker("java", Nil)
}
val log = logger.getOrElse(TestLogger())
val builds =
Build.build(
inputs,
options,
compilerMaker,
None,
TestLogger(),
log,
crossBuilds = false,
buildTests = buildTests,
partial = None,
Expand All @@ -131,7 +133,8 @@ final case class TestInputs(
buildTests: Boolean = true,
actionableDiagnostics: Boolean = false,
scope: Scope = Scope.Main,
skipCreatingSources: Boolean = false
skipCreatingSources: Boolean = false,
logger: Option[Logger] = None
)(f: (os.Path, Inputs, Either[BuildException, Build]) => T): T =
withBuilds(
options,
Expand All @@ -140,7 +143,8 @@ final case class TestInputs(
fromDirectory,
buildTests = buildTests,
actionableDiagnostics = actionableDiagnostics,
skipCreatingSources = skipCreatingSources
skipCreatingSources = skipCreatingSources,
logger = logger
) {
(p, i, builds) =>
f(
Expand Down
35 changes: 35 additions & 0 deletions modules/build/src/test/scala/scala/build/tests/TestLogger.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,43 @@ import java.io.PrintStream
import scala.build.Logger
import scala.build.errors.{BuildException, Diagnostic}
import scala.build.internals.FeatureType
import scala.collection.mutable.ListBuffer
import scala.scalanative.build as sn

/** Logger that records all message() and log() calls for test assertions. */
final class RecordingLogger(delegate: Logger = TestLogger()) extends Logger {
val messages: ListBuffer[String] = ListBuffer.empty

override def error(message: String): Unit = delegate.error(message)
override def message(message: => String): Unit = {
val msg = message
messages += msg
delegate.message(msg)
}
override def log(s: => String): Unit = {
val msg = s
messages += msg
delegate.log(msg)
}
override def log(s: => String, debug: => String): Unit = delegate.log(s, debug)
override def debug(s: => String): Unit = delegate.debug(s)
override def log(diagnostics: Seq[Diagnostic]): Unit = delegate.log(diagnostics)
override def log(ex: BuildException): Unit = delegate.log(ex)
override def debug(ex: BuildException): Unit = delegate.debug(ex)
override def exit(ex: BuildException): Nothing = delegate.exit(ex)
override def coursierLogger(message: String): CacheLogger = delegate.coursierLogger(message)
override def bloopRifleLogger: BloopRifleLogger = delegate.bloopRifleLogger
override def scalaJsLogger: ScalaJsLogger = delegate.scalaJsLogger
override def scalaNativeTestLogger: sn.Logger = delegate.scalaNativeTestLogger
override def scalaNativeCliInternalLoggerOptions: List[String] =
delegate.scalaNativeCliInternalLoggerOptions
override def compilerOutputStream: PrintStream = delegate.compilerOutputStream
override def verbosity: Int = delegate.verbosity
override def experimentalWarning(featureName: String, featureType: FeatureType): Unit =
delegate.experimentalWarning(featureName, featureType)
override def flushExperimentalWarnings: Unit = delegate.flushExperimentalWarnings
}

case class TestLogger(info: Boolean = true, debug: Boolean = false) extends Logger {
override def log(diagnostics: Seq[Diagnostic]): Unit = {
diagnostics.foreach { d =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,7 @@ abstract class CompileTestDefinitions
|""".stripMargin
)

test(
"java files with no using directives should not produce warnings about using directives in multiple files"
) {
{
val inputs = TestInputs(
os.rel / "Bar.java" ->
"""public class Bar {}
Expand All @@ -73,12 +71,23 @@ abstract class CompileTestDefinitions
"""public class Foo {}
|""".stripMargin
)

inputs.fromRoot { root =>
val warningMessage = "Using directives detected in multiple files"
val output = os.proc(TestUtil.cli, "compile", extraOptions, ".")
.call(cwd = root, stderr = os.Pipe).err.trim()
expect(!output.contains(warningMessage))
test(
"java files with no using directives should not produce warnings about using directives in multiple files"
) {
inputs.fromRoot { root =>
val warningMessage = "Using directives detected in multiple files"
val output = os.proc(TestUtil.cli, "compile", extraOptions, ".")
.call(cwd = root, stderr = os.Pipe).err.trim()
expect(!output.contains(warningMessage))
}
}
test("Pure Java with --server=false: no warning about .java files not being compiled") {
inputs.fromRoot { root =>
val warningMessage = ".java files are not compiled to .class files"
val output = os.proc(TestUtil.cli, "compile", "--server=false", extraOptions, ".")
.call(cwd = root, stderr = os.Pipe).err.text()
expect(!output.contains(warningMessage))
}
}
}

Expand Down Expand Up @@ -140,7 +149,7 @@ abstract class CompileTestDefinitions
}

test(
"having target + using directives in files should not produce warnings about using directives in multiple files"
"having target + using directives in files: no using-directives or .java-not-compiled warnings"
) {
val inputs = TestInputs(
os.rel / "Bar.java" ->
Expand All @@ -160,14 +169,14 @@ abstract class CompileTestDefinitions
val output = os.proc(TestUtil.cli, "--power", "compile", extraOptions, ".")
.call(cwd = root).err.trim()
expect(!output.contains(warningMessage))
expect(!output.contains(".java files are not compiled to .class files"))
}
}

test(
"warn about directives in multiple files"
) {
val inputs = TestInputs(
os.rel / "Bar.java" ->
{
val javaSourceFile = "Bar.java"
val inputs = TestInputs(
os.rel / javaSourceFile ->
"""//> using jvm 17
|public class Bar {}
|""".stripMargin,
Expand All @@ -176,12 +185,24 @@ abstract class CompileTestDefinitions
|class Foo {}
|""".stripMargin
)
test("warn about directives in multiple files") {
inputs.fromRoot { root =>
val warningMessage = "Using directives detected in multiple files"
val output = os.proc(TestUtil.cli, "--power", "compile", extraOptions, ".")
.call(cwd = root, stderr = os.Pipe).err.trim()
expect(output.contains(warningMessage))
}
}

inputs.fromRoot { root =>
val warningMessage = "Using directives detected in multiple files"
val output = os.proc(TestUtil.cli, "--power", "compile", extraOptions, ".")
.call(cwd = root, stderr = os.Pipe).err.trim()
expect(output.contains(warningMessage))
test("mixed .java/.scala: with --server=false warn about .java not compiled") {
inputs.fromRoot { root =>
val warningMessage = ".java files are not compiled to .class files"
val output =
os.proc(TestUtil.cli, "--power", "compile", extraOptions, ".", "--server=false")
.call(cwd = root, stderr = os.Pipe).err.trim()
expect(output.contains(warningMessage))
expect(output.contains(javaSourceFile))
}
}
}

Expand Down Expand Up @@ -699,7 +720,9 @@ abstract class CompileTestDefinitions
}
}

test("pass java options to scalac when server=false") {
test(
"pass java options to scalac when server=false (Scala-only, no .java-not-compiled warning)"
) {
val inputs = TestInputs(
os.rel / "Main.scala" ->
"""object Main extends App {
Expand All @@ -721,6 +744,7 @@ abstract class CompileTestDefinitions
val out = res.out.text()
expect(out.contains("Error occurred during initialization of VM"))
expect(out.contains("Too small maximum heap"))
expect(!out.contains(".java files are not compiled to .class files"))
}
}

Expand Down
Loading