Skip to content

Commit

Permalink
Warning for multiple files with using directives
Browse files Browse the repository at this point in the history
  • Loading branch information
wleczny committed Nov 28, 2022
1 parent 030edaf commit fb4830e
Show file tree
Hide file tree
Showing 15 changed files with 234 additions and 74 deletions.
4 changes: 3 additions & 1 deletion build.sc
Expand Up @@ -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
Expand Down Expand Up @@ -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}"
Expand Down
26 changes: 25 additions & 1 deletion modules/build/src/main/scala/scala/build/CrossSources.scala
Expand Up @@ -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,
Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -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)))
Expand Down
Expand Up @@ -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) {
Expand All @@ -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))
Expand All @@ -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
}

Expand Down
4 changes: 2 additions & 2 deletions modules/build/src/main/scala/scala/build/input/Inputs.scala
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)))
Expand Down
Expand Up @@ -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(
Expand All @@ -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
}
Expand Down
Expand Up @@ -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 '/*> ... */'"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
Expand Up @@ -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.
Expand Down Expand Up @@ -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 =>
Expand All @@ -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)
}
Expand Down
Expand Up @@ -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 -> _),
Expand All @@ -95,7 +95,8 @@ case object MarkdownPreprocessor extends Preprocessor {
requirements = Some(processingOutput.globalReqs),
processingOutput.scopedReqs,
mainClassOpt = None,
scopePath = scopePath
scopePath = scopePath,
directivesPositions = processingOutput.directivesPositions
)
}
}
Expand Down
Expand Up @@ -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 {
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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 =
Expand Down

0 comments on commit fb4830e

Please sign in to comment.