From fb4830ead22c4365f3eed64ac64334a1487a85b2 Mon Sep 17 00:00:00 2001 From: Marek Moryl Date: Tue, 15 Nov 2022 19:20:00 +0100 Subject: [PATCH] Warning for multiple files with using directives --- build.sc | 4 +- .../main/scala/scala/build/CrossSources.scala | 26 ++++++++- .../scala/build/input/ElementsUtils.scala | 7 +-- .../main/scala/scala/build/input/Inputs.scala | 4 +- .../preprocessing/DataPreprocessor.scala | 46 ++++++++++------ .../preprocessing/ExtractedDirectives.scala | 26 +++++++-- .../preprocessing/JavaPreprocessor.scala | 44 ++++++++------- .../preprocessing/MarkdownPreprocessor.scala | 5 +- .../preprocessing/PreprocessedSource.scala | 8 ++- .../preprocessing/PreprocessingUtil.scala | 39 ++++++++++++++ .../preprocessing/ScalaPreprocessor.scala | 53 +++++++++++++------ .../preprocessing/ScriptPreprocessor.scala | 5 +- .../scala/scala/build/tests/InputsTests.scala | 16 +++--- .../cli/integration/RunTestDefinitions.scala | 24 +++++++++ project/settings.sc | 1 + 15 files changed, 234 insertions(+), 74 deletions(-) diff --git a/build.sc b/build.sc index 89639b7ad4..6c47cea2c4 100644 --- a/build.sc +++ b/build.sc @@ -16,7 +16,8 @@ import $file.project.settings, settings.{ ScalaCliTests, localRepoResourcePath, platformExecutableJarExtension, - workspaceDirName + workspaceDirName, + projectFileName } import $file.project.deps, deps.customRepositories import $file.project.website @@ -385,6 +386,7 @@ trait Core extends ScalaCliSbtModule with ScalaCliPublishModule with HasTests | def defaultScala213Version = "${Scala.scala213}" | | def workspaceDirName = "$workspaceDirName" + | def projectFileName = "$projectFileName" | | def defaultGraalVMJavaVersion = ${deps.graalVmJavaVersion} | def defaultGraalVMVersion = "${deps.graalVmVersion}" diff --git a/modules/build/src/main/scala/scala/build/CrossSources.scala b/modules/build/src/main/scala/scala/build/CrossSources.scala index 362ad844ba..8a8e12d05f 100644 --- a/modules/build/src/main/scala/scala/build/CrossSources.scala +++ b/modules/build/src/main/scala/scala/build/CrossSources.scala @@ -6,6 +6,7 @@ import scala.build.Positioned import scala.build.errors.{BuildException, CompositeBuildException, MalformedDirectiveError} import scala.build.input.ElementsUtils.* import scala.build.input.* +import scala.build.internal.Constants import scala.build.options.{ BuildOptions, BuildRequirements, @@ -166,6 +167,29 @@ object CrossSources { val preprocessedSources = (preprocessedInputFromArgs ++ preprocessedSourcesFromDirectives).distinct + val preprocessedWithUsingDirs = preprocessedSources.filter(_.directivesPositions.isDefined) + if (preprocessedWithUsingDirs.length > 1) { + val projectFilePath = inputs.elements.projectSettingsFiles.headOption match + case Some(s) => s.path + case _ => inputs.workspace / Constants.projectFileName + preprocessedWithUsingDirs + .filter(_.scopePath != ScopePath.fromPath(projectFilePath)) + .foreach { source => + source.directivesPositions match + case Some(positions) => + val pos = Seq( + positions.codeDirectives, + positions.specialCommentDirectives, + positions.plainCommentDirectives + ).maxBy(p => p.endPos._1) + logger.diagnostic( + s"Using directives detected in multiple files. It is recommended to keep them centralized in the $projectFilePath file.", + positions = Seq(pos) + ) + case _ => () + } + } + val scopedRequirements = preprocessedSources.flatMap(_.scopedRequirements) val scopedRequirementsByRoot = scopedRequirements.groupBy(_.path.root) def baseReqs(path: ScopePath): BuildRequirements = { @@ -238,7 +262,7 @@ object CrossSources { lazy val subPath = sourcePath.subRelativeTo(dir) if (os.isDir(sourcePath)) Right(Directory(sourcePath).singleFilesFromDirectory(enableMarkdown)) - else if (sourcePath == os.sub / "project.scala") + else if (sourcePath == os.sub / Constants.projectFileName) Right(Seq(ProjectScalaFile(dir, subPath))) else if (sourcePath.ext == "scala") Right(Seq(SourceScalaFile(dir, subPath))) else if (sourcePath.ext == "sc") Right(Seq(Script(dir, subPath))) diff --git a/modules/build/src/main/scala/scala/build/input/ElementsUtils.scala b/modules/build/src/main/scala/scala/build/input/ElementsUtils.scala index 08ebfd8214..00cad854d7 100644 --- a/modules/build/src/main/scala/scala/build/input/ElementsUtils.scala +++ b/modules/build/src/main/scala/scala/build/input/ElementsUtils.scala @@ -5,6 +5,7 @@ import java.nio.charset.StandardCharsets import java.security.MessageDigest import scala.build.Directories +import scala.build.internal.Constants object ElementsUtils { extension (d: Directory) { @@ -15,7 +16,7 @@ object ElementsUtils { .collect { case p if p.last.endsWith(".java") => JavaFile(d.path, p.subRelativeTo(d.path)) - case p if p.last == "project.scala" => + case p if p.last == Constants.projectFileName => ProjectScalaFile(d.path, p.subRelativeTo(d.path)) case p if p.last.endsWith(".scala") => SourceScalaFile(d.path, p.subRelativeTo(d.path)) @@ -31,8 +32,8 @@ object ElementsUtils { } def configFile: Seq[ProjectScalaFile] = - if (os.exists(d.path / "project.scala")) - Seq(ProjectScalaFile(d.path, os.sub / "project.scala")) + if (os.exists(d.path / Constants.projectFileName)) + Seq(ProjectScalaFile(d.path, os.sub / Constants.projectFileName)) else Nil } diff --git a/modules/build/src/main/scala/scala/build/input/Inputs.scala b/modules/build/src/main/scala/scala/build/input/Inputs.scala index e6d73aee51..ea160995d1 100644 --- a/modules/build/src/main/scala/scala/build/input/Inputs.scala +++ b/modules/build/src/main/scala/scala/build/input/Inputs.scala @@ -139,7 +139,7 @@ object Inputs { settingsFiles.headOption.map { s => if (settingsFiles.length > 1) System.err.println( - s"Warning: more than one project.scala file has been found. Setting ${s.base} as the project root directory for this run." + s"Warning: more than one ${Constants.projectFileName} file has been found. Setting ${s.base} as the project root directory for this run." ) (s.base, true, WorkspaceOrigin.SourcePaths) }.orElse { @@ -282,7 +282,7 @@ object Inputs { else List(Virtual(url, urlContent)) } } - else if path.last == "project.scala" then Right(Seq(ProjectScalaFile(dir, subPath))) + else if path.last == Constants.projectFileName then Right(Seq(ProjectScalaFile(dir, subPath))) else if arg.endsWith(".sc") then Right(Seq(Script(dir, subPath))) else if arg.endsWith(".scala") then Right(Seq(SourceScalaFile(dir, subPath))) else if arg.endsWith(".java") then Right(Seq(JavaFile(dir, subPath))) diff --git a/modules/build/src/main/scala/scala/build/preprocessing/DataPreprocessor.scala b/modules/build/src/main/scala/scala/build/preprocessing/DataPreprocessor.scala index 9cafcaeb18..3a2bff0f31 100644 --- a/modules/build/src/main/scala/scala/build/preprocessing/DataPreprocessor.scala +++ b/modules/build/src/main/scala/scala/build/preprocessing/DataPreprocessor.scala @@ -2,9 +2,12 @@ package scala.build.preprocessing import java.nio.charset.StandardCharsets +import scala.build.EitherCps.{either, value} import scala.build.Logger import scala.build.errors.BuildException import scala.build.input.{Inputs, SingleElement, VirtualData} +import scala.build.options.BuildRequirements +import scala.build.preprocessing.PreprocessingUtil.optionsAndPositionsFromDirectives case object DataPreprocessor extends Preprocessor { def preprocess( @@ -15,23 +18,36 @@ case object DataPreprocessor extends Preprocessor { ): Option[Either[BuildException, Seq[PreprocessedSource]]] = input match { case file: VirtualData => - val content = new String(file.content, StandardCharsets.UTF_8) + val res = either { + val content = new String(file.content, StandardCharsets.UTF_8) + val (updatedOptions, directivesPositions) = value { + optionsAndPositionsFromDirectives( + content, + file.scopePath, + Left(file.subPath.toString), + logger, + maybeRecoverOnError, + allowRestrictedFeatures + ) + } - val inMemory = Seq( - PreprocessedSource.InMemory( - Left(file.source), - file.subPath, - content, - 0, - None, - None, - Nil, - None, - file.scopePath + val inMemory = Seq( + PreprocessedSource.InMemory( + Left(file.source), + file.subPath, + content, + 0, + Some(updatedOptions.global), + Some(BuildRequirements()), + Nil, + None, + file.scopePath, + directivesPositions + ) ) - ) - - Some(Right(inMemory)) + inMemory + } + Some(res) case _ => None } diff --git a/modules/build/src/main/scala/scala/build/preprocessing/ExtractedDirectives.scala b/modules/build/src/main/scala/scala/build/preprocessing/ExtractedDirectives.scala index 31c25b38d2..d0e8716b8f 100644 --- a/modules/build/src/main/scala/scala/build/preprocessing/ExtractedDirectives.scala +++ b/modules/build/src/main/scala/scala/build/preprocessing/ExtractedDirectives.scala @@ -18,16 +18,23 @@ import scala.jdk.CollectionConverters.* case class ExtractedDirectives( offset: Int, - directives: Seq[StrictDirective] + directives: Seq[StrictDirective], + location: Option[DirectivesPositions] ) { @targetName("append") def ++(other: ExtractedDirectives): ExtractedDirectives = - ExtractedDirectives(offset, directives ++ other.directives) + ExtractedDirectives(offset, directives ++ other.directives, location) } +case class DirectivesPositions( + codeDirectives: Position.File, + specialCommentDirectives: Position.File, + plainCommentDirectives: Position.File +) + object ExtractedDirectives { - def empty: ExtractedDirectives = ExtractedDirectives(0, Seq.empty) + def empty: ExtractedDirectives = ExtractedDirectives(0, Seq.empty, None) val changeToSpecialCommentMsg = "Using directive using plain comments are deprecated. Please use a special comment syntax: '//> ...' or '/*> ... */'" @@ -73,10 +80,21 @@ object ExtractedDirectives { Nil } + def getPosition(directives: UsingDirectives) = + val line = directives.getAst().getPosition().getLine() + val column = directives.getAst().getPosition().getColumn() + Position.File(path, (0, 0), (line, column)) + val codeDirectives = byKind(UsingDirectiveKind.Code) val specialCommentDirectives = byKind(UsingDirectiveKind.SpecialComment) val plainCommentDirectives = byKind(UsingDirectiveKind.PlainComment) + val directivesPositions = DirectivesPositions( + getPosition(codeDirectives), + getPosition(specialCommentDirectives), + getPosition(plainCommentDirectives) + ) + def reportWarning(msg: String, values: Seq[UsingDef], before: Boolean = true): Unit = values.foreach { v => val astPos = v.getPosition @@ -126,7 +144,7 @@ object ExtractedDirectives { if (usedDirectives.getKind != UsingDirectiveKind.Code) 0 else usedDirectives.getCodeOffset if (supportedDirectives.contains(usedDirectives.getKind)) - Right(ExtractedDirectives(offset, strictDirectives)) + Right(ExtractedDirectives(offset, strictDirectives, Some(directivesPositions))) else { val directiveVales = usedDirectives.getFlattenedMap.values().asScala.toList.flatMap(_.asScala) diff --git a/modules/build/src/main/scala/scala/build/preprocessing/JavaPreprocessor.scala b/modules/build/src/main/scala/scala/build/preprocessing/JavaPreprocessor.scala index 721421ea27..84acac1b96 100644 --- a/modules/build/src/main/scala/scala/build/preprocessing/JavaPreprocessor.scala +++ b/modules/build/src/main/scala/scala/build/preprocessing/JavaPreprocessor.scala @@ -13,6 +13,7 @@ import scala.build.input.{Inputs, JavaFile, SingleElement, VirtualJavaFile} import scala.build.internal.JavaParserProxyMaker import scala.build.options.BuildRequirements import scala.build.preprocessing.ExtractedDirectives.from +import scala.build.preprocessing.PreprocessingUtil.optionsAndPositionsFromDirectives import scala.build.preprocessing.ScalaPreprocessor._ /** Java source preprocessor. @@ -42,29 +43,23 @@ final case class JavaPreprocessor( case j: JavaFile => Some(either { val content = value(PreprocessingUtil.maybeRead(j.path)) val scopePath = ScopePath.fromPath(j.path) - val ExtractedDirectives(_, directives0) = - value(from( - content.toCharArray, + val (updatedOptions, directivesPositions) = value { + optionsAndPositionsFromDirectives( + content, + scopePath, Right(j.path), logger, - Array(UsingDirectiveKind.PlainComment, UsingDirectiveKind.SpecialComment), - scopePath, - maybeRecoverOnError - )) - val updatedOptions = value(DirectivesProcessor.process( - directives0, - usingDirectiveHandlers, - Right(j.path), - scopePath, - logger, - allowRestrictedFeatures - )) + maybeRecoverOnError, + allowRestrictedFeatures + ) + } Seq(PreprocessedSource.OnDisk( j.path, Some(updatedOptions.global), Some(BuildRequirements()), Nil, - None + None, + directivesPositions )) }) case v: VirtualJavaFile => @@ -88,16 +83,27 @@ final case class JavaPreprocessor( } else v.subPath val content = new String(v.content, StandardCharsets.UTF_8) + val (updatedOptions, directivesPositions) = value { + optionsAndPositionsFromDirectives( + content, + v.scopePath, + Left(relPath.toString), + logger, + maybeRecoverOnError, + allowRestrictedFeatures + ) + } val s = PreprocessedSource.InMemory( originalPath = Left(v.source), relPath = relPath, code = content, ignoreLen = 0, - options = None, - requirements = None, + options = Some(updatedOptions.global), + requirements = Some(BuildRequirements()), scopedRequirements = Nil, mainClassOpt = None, - scopePath = v.scopePath + scopePath = v.scopePath, + directivesPositions = directivesPositions ) Seq(s) } diff --git a/modules/build/src/main/scala/scala/build/preprocessing/MarkdownPreprocessor.scala b/modules/build/src/main/scala/scala/build/preprocessing/MarkdownPreprocessor.scala index 56e4c3b915..fe6d1e45c5 100644 --- a/modules/build/src/main/scala/scala/build/preprocessing/MarkdownPreprocessor.scala +++ b/modules/build/src/main/scala/scala/build/preprocessing/MarkdownPreprocessor.scala @@ -84,7 +84,7 @@ case object MarkdownPreprocessor extends Preprocessor { maybeRecoverOnError = maybeRecoverOnError, allowRestrictedFeatures = allowRestrictedFeatures ) - }.getOrElse(ProcessingOutput(BuildRequirements(), Nil, BuildOptions(), None)) + }.getOrElse(ProcessingOutput(BuildRequirements(), Nil, BuildOptions(), None, None)) val processedCode = processingOutput.updatedContent.getOrElse(wrappedMarkdown.code) PreprocessedSource.InMemory( originalPath = reportingPath.map(subPath -> _), @@ -95,7 +95,8 @@ case object MarkdownPreprocessor extends Preprocessor { requirements = Some(processingOutput.globalReqs), processingOutput.scopedReqs, mainClassOpt = None, - scopePath = scopePath + scopePath = scopePath, + directivesPositions = processingOutput.directivesPositions ) } } diff --git a/modules/build/src/main/scala/scala/build/preprocessing/PreprocessedSource.scala b/modules/build/src/main/scala/scala/build/preprocessing/PreprocessedSource.scala index 33d739af06..97daf650f0 100644 --- a/modules/build/src/main/scala/scala/build/preprocessing/PreprocessedSource.scala +++ b/modules/build/src/main/scala/scala/build/preprocessing/PreprocessedSource.scala @@ -9,6 +9,7 @@ sealed abstract class PreprocessedSource extends Product with Serializable { def scopedRequirements: Seq[Scoped[BuildRequirements]] def scopePath: ScopePath + def directivesPositions: Option[DirectivesPositions] } object PreprocessedSource { @@ -18,7 +19,8 @@ object PreprocessedSource { options: Option[BuildOptions], requirements: Option[BuildRequirements], scopedRequirements: Seq[Scoped[BuildRequirements]], - mainClassOpt: Option[String] + mainClassOpt: Option[String], + directivesPositions: Option[DirectivesPositions] ) extends PreprocessedSource { def scopePath: ScopePath = ScopePath.fromPath(path) @@ -32,7 +34,8 @@ object PreprocessedSource { requirements: Option[BuildRequirements], scopedRequirements: Seq[Scoped[BuildRequirements]], mainClassOpt: Option[String], - scopePath: ScopePath + scopePath: ScopePath, + directivesPositions: Option[DirectivesPositions] ) extends PreprocessedSource { def reportingPath: Either[String, os.Path] = originalPath.map(_._2) @@ -46,6 +49,7 @@ object PreprocessedSource { def mainClassOpt: None.type = None def scopePath: ScopePath = ScopePath.fromPath(path) + def directivesPositions: None.type = None } private def index(s: PreprocessedSource): Int = diff --git a/modules/build/src/main/scala/scala/build/preprocessing/PreprocessingUtil.scala b/modules/build/src/main/scala/scala/build/preprocessing/PreprocessingUtil.scala index b6dce69c02..5d61d785c5 100644 --- a/modules/build/src/main/scala/scala/build/preprocessing/PreprocessingUtil.scala +++ b/modules/build/src/main/scala/scala/build/preprocessing/PreprocessingUtil.scala @@ -1,8 +1,16 @@ package scala.build.preprocessing +import com.virtuslab.using_directives.custom.model.UsingDirectiveKind + import java.nio.charset.StandardCharsets +import scala.build.EitherCps.{either, value} +import scala.build.Logger import scala.build.errors.{BuildException, FileNotFoundException} +import scala.build.options.BuildOptions +import scala.build.preprocessing.DirectivesProcessor.DirectivesProcessorOutput +import scala.build.preprocessing.ExtractedDirectives.from +import scala.build.preprocessing.ScalaPreprocessor._ object PreprocessingUtil { @@ -11,4 +19,35 @@ object PreprocessingUtil { def maybeRead(f: os.Path): Either[BuildException, String] = if (os.isFile(f)) Right(os.read(f, defaultCharSet)) else Left(new FileNotFoundException(f)) + + def optionsAndPositionsFromDirectives( + content: String, + scopePath: ScopePath, + path: Either[String, os.Path], + logger: Logger, + maybeRecoverOnError: BuildException => Option[BuildException] = e => Some(e), + allowRestrictedFeatures: Boolean + ): Either[ + BuildException, + (DirectivesProcessorOutput[BuildOptions], Option[DirectivesPositions]) + ] = either { + val ExtractedDirectives(_, directives0, directivesPositions) = + value(from( + content.toCharArray, + path, + logger, + Array(UsingDirectiveKind.PlainComment, UsingDirectiveKind.SpecialComment), + scopePath, + maybeRecoverOnError + )) + val updatedOptions = value(DirectivesProcessor.process( + directives0, + usingDirectiveHandlers, + path, + scopePath, + logger, + allowRestrictedFeatures + )) + (updatedOptions, directivesPositions) + } } diff --git a/modules/build/src/main/scala/scala/build/preprocessing/ScalaPreprocessor.scala b/modules/build/src/main/scala/scala/build/preprocessing/ScalaPreprocessor.scala index d7b2626d53..6f633bb828 100644 --- a/modules/build/src/main/scala/scala/build/preprocessing/ScalaPreprocessor.scala +++ b/modules/build/src/main/scala/scala/build/preprocessing/ScalaPreprocessor.scala @@ -21,7 +21,8 @@ case object ScalaPreprocessor extends Preprocessor { globalReqs: BuildRequirements, globalUsings: BuildOptions, scopedReqs: Seq[Scoped[BuildRequirements]], - strippedContent: Option[String] + strippedContent: Option[String], + directivesPositions: Option[DirectivesPositions] ) { def isEmpty: Boolean = globalReqs == BuildRequirements.monoid.zero && globalUsings == BuildOptions.monoid.zero && @@ -39,7 +40,8 @@ case object ScalaPreprocessor extends Preprocessor { globalReqs: BuildRequirements, scopedReqs: Seq[Scoped[BuildRequirements]], opts: BuildOptions, - updatedContent: Option[String] + updatedContent: Option[String], + directivesPositions: Option[DirectivesPositions] ) val usingDirectiveHandlers: Seq[UsingDirectiveHandler] = Seq( @@ -96,12 +98,13 @@ case object ScalaPreprocessor extends Preprocessor { ) ) match { case None => - PreprocessedSource.OnDisk(f.path, None, None, Nil, None) + PreprocessedSource.OnDisk(f.path, None, None, Nil, None, None) case Some(ProcessingOutput( requirements, scopedRequirements, options, - Some(updatedCode) + Some(updatedCode), + directivesPositions )) => PreprocessedSource.InMemory( Right((f.subPath, f.path)), @@ -112,15 +115,23 @@ case object ScalaPreprocessor extends Preprocessor { Some(requirements), scopedRequirements, None, - scopePath + scopePath, + directivesPositions ) - case Some(ProcessingOutput(requirements, scopedRequirements, options, None)) => + case Some(ProcessingOutput( + requirements, + scopedRequirements, + options, + None, + directivesPositions + )) => PreprocessedSource.OnDisk( f.path, Some(options), Some(requirements), scopedRequirements, - None + None, + directivesPositions ) } Seq(source) @@ -134,7 +145,7 @@ case object ScalaPreprocessor extends Preprocessor { case v => os.sub / v.generatedSourceFileName } val content = new String(v.content, StandardCharsets.UTF_8) - val (requirements, scopedRequirements, options, updatedContentOpt) = + val (requirements, scopedRequirements, options, updatedContentOpt, directivesPositions) = value( process( content, @@ -145,9 +156,9 @@ case object ScalaPreprocessor extends Preprocessor { allowRestrictedFeatures ) ).map { - case ProcessingOutput(reqs, scopedReqs, opts, updatedContent) => - (reqs, scopedReqs, opts, updatedContent) - }.getOrElse((BuildRequirements(), Nil, BuildOptions(), None)) + case ProcessingOutput(reqs, scopedReqs, opts, updatedContent, dirsPositions) => + (reqs, scopedReqs, opts, updatedContent, dirsPositions) + }.getOrElse((BuildRequirements(), Nil, BuildOptions(), None, None)) val s = PreprocessedSource.InMemory( originalPath = Left(v.source), relPath = relPath, @@ -157,7 +168,8 @@ case object ScalaPreprocessor extends Preprocessor { requirements = Some(requirements), scopedRequirements, mainClassOpt = None, - scopePath = v.scopePath + scopePath = v.scopePath, + directivesPositions = directivesPositions ) Seq(s) } @@ -232,9 +244,16 @@ case object ScalaPreprocessor extends Preprocessor { .map(_.content) .orElse(afterStrictUsing.strippedContent) .orElse(if (isSheBang) Some(content0) else None) + val directivesPositions = afterStrictUsing.directivesPositions val scopedRequirements = afterStrictUsing.scopedReqs - Some(ProcessingOutput(summedRequirements, scopedRequirements, summedOptions, lastContentOpt)) + Some(ProcessingOutput( + summedRequirements, + scopedRequirements, + summedOptions, + lastContentOpt, + directivesPositions + )) } } @@ -332,8 +351,9 @@ case object ScalaPreprocessor extends Preprocessor { logger: Logger, allowRestrictedFeatures: Boolean ): Either[BuildException, StrictDirectivesProcessingOutput] = either { - val contentChars = content.toCharArray - val ExtractedDirectives(codeOffset, directives0) = extractedDirectives + val contentChars = content.toCharArray + + val ExtractedDirectives(codeOffset, directives0, directivesPositions) = extractedDirectives val updatedOptions = value { DirectivesProcessor.process( @@ -382,7 +402,8 @@ case object ScalaPreprocessor extends Preprocessor { updatedRequirements.global, updatedOptions.global, updatedRequirements.scoped, - updatedContentOpt + updatedContentOpt, + directivesPositions )) case Seq(h, t*) => val errors = ::( diff --git a/modules/build/src/main/scala/scala/build/preprocessing/ScriptPreprocessor.scala b/modules/build/src/main/scala/scala/build/preprocessing/ScriptPreprocessor.scala index 5396926fd3..9a5912f8a7 100644 --- a/modules/build/src/main/scala/scala/build/preprocessing/ScriptPreprocessor.scala +++ b/modules/build/src/main/scala/scala/build/preprocessing/ScriptPreprocessor.scala @@ -88,7 +88,7 @@ object ScriptPreprocessor { maybeRecoverOnError, allowRestrictedFeatures )) - .getOrElse(ProcessingOutput(BuildRequirements(), Nil, BuildOptions(), None)) + .getOrElse(ProcessingOutput(BuildRequirements(), Nil, BuildOptions(), None, None)) val (code, topWrapperLen, _) = codeWrapper.wrapCode( pkg, @@ -108,7 +108,8 @@ object ScriptPreprocessor { Some(processingOutput.globalReqs), processingOutput.scopedReqs, Some(CustomCodeWrapper.mainClassObject(Name(className)).backticked), - scopePath + scopePath, + processingOutput.directivesPositions ) List(file) } diff --git a/modules/build/src/test/scala/scala/build/tests/InputsTests.scala b/modules/build/src/test/scala/scala/build/tests/InputsTests.scala index c5db622f90..698c4e0e6f 100644 --- a/modules/build/src/test/scala/scala/build/tests/InputsTests.scala +++ b/modules/build/src/test/scala/scala/build/tests/InputsTests.scala @@ -40,11 +40,12 @@ class InputsTests extends munit.FunSuite { } } - test("project.scala file") { + test("project file") { + val projectFileName = Constants.projectFileName val testInputs = TestInputs( files = Seq( - os.rel / "custom-dir" / "project.scala" -> "", - os.rel / "project.scala" -> s"//> using javaProp \"foo=bar\"".stripMargin, + os.rel / "custom-dir" / projectFileName -> "", + os.rel / projectFileName -> s"//> using javaProp \"foo=bar\"".stripMargin, os.rel / "foo.scala" -> s"""object Foo { | def main(args: Array[String]): Unit = @@ -52,7 +53,7 @@ class InputsTests extends munit.FunSuite { |} |""".stripMargin ), - inputArgs = Seq("foo.scala", "custom-dir", "project.scala") + inputArgs = Seq("foo.scala", "custom-dir", projectFileName) ) testInputs.withBuild(buildOptions, buildThreads, bloopConfigOpt) { (root, _, buildMaybe) => @@ -87,7 +88,8 @@ class InputsTests extends munit.FunSuite { } } - test("passing project.scala and its parent directory") { + test("passing project file and its parent directory") { + val projectFileName = Constants.projectFileName val testInputs = TestInputs( files = Seq( os.rel / "foo.scala" -> @@ -96,9 +98,9 @@ class InputsTests extends munit.FunSuite { | println("Foo") |} |""".stripMargin, - os.rel / "project.scala" -> "" + os.rel / projectFileName -> "" ), - inputArgs = Seq(".", "project.scala") + inputArgs = Seq(".", projectFileName) ) testInputs.withBuild(buildOptions, buildThreads, bloopConfigOpt) { (root, inputs, _) => diff --git a/modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala b/modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala index abdc9b5d0c..5de791b6f1 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/RunTestDefinitions.scala @@ -671,6 +671,30 @@ abstract class RunTestDefinitions(val scalaVersionOpt: Option[String]) } } + test("multiple using directives warning message") { + val inputs = TestInputs( + os.rel / "Foo.scala" -> + s"""//> using scala "3.2.0" + | + |object Foo extends App { + | println("Foo") + |} + |""".stripMargin, + os.rel / "Bar.scala" -> "", + os.rel / "Hello.java" -> "//> using packaging.output \"out\"" + ) + inputs.fromRoot { root => + val warningMessage = "Using directives detected in" + val output1 = os.proc(TestUtil.cli, ".").call(cwd = root, stderr = os.Pipe).err.trim() + val output2 = os.proc(TestUtil.cli, "Foo.scala", "Bar.scala").call( + cwd = root, + stderr = os.Pipe + ).err.trim() + expect(output1.contains(warningMessage)) + expect(!output2.contains(warningMessage)) + } + } + def sudoTest(): Unit = { val fileName = "simple.sc" val message = "Hello" diff --git a/project/settings.sc b/project/settings.sc index 1c4c1207b1..b0966205f6 100644 --- a/project/settings.sc +++ b/project/settings.sc @@ -858,6 +858,7 @@ trait ScalaCliModule extends ScalaModule { } def workspaceDirName = ".scala-build" +def projectFileName = "project.scala" final case class License(licenseId: String, name: String, reference: String) object License {