Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for 'in' clause in require directives #164

Merged
merged 10 commits into from
Sep 30, 2021
55 changes: 26 additions & 29 deletions modules/build/src/main/scala/scala/build/CrossSources.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,22 @@ final case class CrossSources(
inMemory: Seq[HasBuildRequirements[(Either[String, os.Path], os.RelPath, String, Int)]],
mainClass: Option[String],
resourceDirs: Seq[os.Path],
buildOptions: Seq[HasBuildRequirements[(Either[String, os.Path], BuildOptions)]]
buildOptions: Seq[HasBuildRequirements[BuildOptions]]
) {

def sources(baseOptions: BuildOptions): Sources = {

val sharedOptions = buildOptions
.filter(_.requirements.isEmpty)
.map(_.value._2)
.map(_.value)
.foldLeft(baseOptions)(_ orElse _)

val retainedScalaVersion = sharedOptions.scalaParams.scalaVersion

val buildOptionsWithScalaVersion = buildOptions
.flatMap(_.withScalaVersion(retainedScalaVersion).toSeq)
.filter(_.requirements.isEmpty)
.map(_.value._2)
.map(_.value)
.foldLeft(sharedOptions)(_ orElse _)

val platform =
Expand Down Expand Up @@ -56,7 +56,7 @@ final case class CrossSources(
buildOptions
.flatMap(_.withScalaVersion(retainedScalaVersion).toSeq)
.flatMap(_.withPlatform(platform).toSeq)
.map(_.value._2)
.map(_.value)
.foldLeft(BuildOptions() /* not baseOptions */ )(_ orElse _)
)
}
Expand All @@ -81,29 +81,24 @@ object CrossSources {
.map(_.flatten)
}

val buildOptions = preprocessedSources.flatMap {
case d: PreprocessedSource.OnDisk =>
d.options.toSeq.map { opt =>
HasBuildRequirements(
d.requirements.getOrElse(BuildRequirements()),
(Right(d.path), opt)
)
}
case m: PreprocessedSource.InMemory =>
m.options.toSeq.map { opt =>
HasBuildRequirements(
m.requirements.getOrElse(BuildRequirements()),
(m.reportingPath, opt)
)
}
case n: PreprocessedSource.NoSourceCode =>
val elem = HasBuildRequirements(
n.requirements.getOrElse(BuildRequirements()),
(Right(n.path), n.options.getOrElse(BuildOptions()))
)
Seq(elem)
case _ =>
Nil
val scopedRequirements = preprocessedSources.flatMap(_.scopedRequirements)
val scopedRequirementsByRoot = scopedRequirements.groupBy(_.path.root)
def baseReqs(path: PreprocessedSource.ScopePath): BuildRequirements =
scopedRequirementsByRoot
.getOrElse(path.root, Nil)
.flatMap(_.valueFor(path).toSeq)
.foldLeft(BuildRequirements())(_ orElse _)

val buildOptions = for {
s <- preprocessedSources
opt <- s.options.toSeq
if opt != BuildOptions()
} yield {
val baseReqs0 = baseReqs(s.scopePath)
HasBuildRequirements(
s.requirements.fold(baseReqs0)(_ orElse baseReqs0),
opt
)
}

val mainClassOpt = value {
Expand All @@ -122,15 +117,17 @@ object CrossSources {

val paths = preprocessedSources.collect {
case d: PreprocessedSource.OnDisk =>
val baseReqs0 = baseReqs(d.scopePath)
HasBuildRequirements(
d.requirements.getOrElse(BuildRequirements()),
d.requirements.fold(baseReqs0)(_ orElse baseReqs0),
(d.path, d.path.relativeTo(inputs.workspace))
)
}
val inMemory = preprocessedSources.collect {
case m: PreprocessedSource.InMemory =>
val baseReqs0 = baseReqs(m.scopePath)
HasBuildRequirements(
m.requirements.getOrElse(BuildRequirements()),
m.requirements.fold(baseReqs0)(_ orElse baseReqs0),
(m.reportingPath, m.relPath, m.code, m.ignoreLen)
)
}
Expand Down
6 changes: 5 additions & 1 deletion modules/build/src/main/scala/scala/build/Inputs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import java.security.MessageDigest
import java.util.zip.{ZipEntry, ZipInputStream}

import scala.annotation.tailrec
import scala.build.preprocessing.PreprocessedSource
import scala.util.matching.Regex

final case class Inputs(
Expand Down Expand Up @@ -137,6 +138,9 @@ object Inputs {
val idx = source.lastIndexOf('/')
os.sub / source.drop(idx + 1)
}

def scopePath: PreprocessedSource.ScopePath =
PreprocessedSource.ScopePath(source, subPath)
}

sealed trait SingleFile extends OnDisk with SingleElement
Expand Down Expand Up @@ -345,7 +349,7 @@ object Inputs {
else
forNonEmptyArgs(args, cwd, directories, baseProjectName, download, stdinOpt, acceptFds)

def default(cwd: os.Path = Os.pwd): Option[Inputs] =
def default(): Option[Inputs] =
None

def empty(workspace: os.Path): Inputs =
Expand Down
24 changes: 4 additions & 20 deletions modules/build/src/main/scala/scala/build/internal/AmmUtil.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,10 @@ package scala.build.internal

object AmmUtil {
val upPathSegment = "^"
def pathToPackageWrapper(
flexiblePkgName0: Seq[Name],
relPath0: os.RelPath
): (Seq[Name], Name) = {
var flexiblePkgName = flexiblePkgName0
var relPath = relPath0 / os.up
val fileName = relPath0.last
while (
flexiblePkgName.length > 1 &&
flexiblePkgName.last.encoded != upPathSegment &&
relPath.ups > 0
) {
flexiblePkgName = flexiblePkgName.dropRight(1)
relPath = os.RelPath(relPath.segments, relPath.ups - 1)
}
val pkg = {
val ups = Seq.fill(relPath.ups)(upPathSegment)
val rest = relPath.segments
flexiblePkgName ++ (ups ++ rest).map(Name(_))
}
def pathToPackageWrapper(relPath0: os.SubPath): (Seq[Name], Name) = {
val relPath = relPath0 / os.up
val fileName = relPath0.last
val pkg = relPath.segments.map(Name(_))
val wrapper = fileName.lastIndexOf('.') match {
case -1 => fileName
case i => fileName.take(i)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package scala.build.options

final case class BuildRequirements(
scalaVersion: Seq[BuildRequirements.VersionRequirement] = Nil,
platform: Option[BuildRequirements.PlatformRequirement] = None
platform: Seq[BuildRequirements.PlatformRequirement] = Nil
) {
def withScalaVersion(sv: String): Either[String, BuildRequirements] = {
val dontPass = scalaVersion.filter(!_.valid(sv))
Expand All @@ -12,10 +12,10 @@ final case class BuildRequirements(
Left(dontPass.map(_.failedMessage).mkString(", "))
}
def withPlatform(pf: Platform): Either[String, BuildRequirements] =
platform match {
BuildRequirements.PlatformRequirement.merge(platform) match {
case None => Right(this)
case Some(platform0) =>
if (platform0.valid(pf)) Right(copy(platform = None))
if (platform0.valid(pf)) Right(copy(platform = Nil))
else Left(platform0.failedMessage)
}
def isEmpty: Boolean =
Expand Down Expand Up @@ -73,6 +73,18 @@ object BuildRequirements {
"Expected platform: " + platforms.toVector.map(_.repr).sorted.mkString(" or ")
}

object PlatformRequirement {
def merge(requirements: Seq[PlatformRequirement]): Option[PlatformRequirement] =
if (requirements.isEmpty) None
else if (requirements.lengthCompare(1) == 0) Some(requirements.head)
else {
val platforms = requirements.tail.foldLeft(requirements.head.platforms) { (acc, req) =>
acc.intersect(req.platforms)
}
Some(PlatformRequirement(platforms))
}
}

implicit val monoid: ConfigMonoid[BuildRequirements] = ConfigMonoid.derive

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ case object JavaPreprocessor extends Preprocessor {
: Option[Either[BuildException, Seq[PreprocessedSource]]] =
input match {
case j: Inputs.JavaFile =>
Some(Right(Seq(PreprocessedSource.OnDisk(j.path, None, None, None))))
Some(Right(Seq(PreprocessedSource.OnDisk(j.path, None, None, Nil, None))))

case v: Inputs.VirtualJavaFile =>
val content = new String(v.content, StandardCharsets.UTF_8)
Expand All @@ -21,7 +21,9 @@ case object JavaPreprocessor extends Preprocessor {
0,
None,
None,
None
Nil,
None,
v.scopePath
)
Some(Right(Seq(s)))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ sealed abstract class PreprocessedSource extends Product with Serializable {
def options: Option[BuildOptions]
def requirements: Option[BuildRequirements]
def mainClassOpt: Option[String]

def scopedRequirements: Seq[PreprocessedSource.Scoped[BuildRequirements]]
def scopePath: PreprocessedSource.ScopePath
}

object PreprocessedSource {
Expand All @@ -14,23 +17,32 @@ object PreprocessedSource {
path: os.Path,
options: Option[BuildOptions],
requirements: Option[BuildRequirements],
scopedRequirements: Seq[Scoped[BuildRequirements]],
mainClassOpt: Option[String]
) extends PreprocessedSource
) extends PreprocessedSource {
def scopePath: ScopePath =
ScopePath.fromPath(path)
}
final case class InMemory(
reportingPath: Either[String, os.Path],
relPath: os.RelPath,
code: String,
ignoreLen: Int,
options: Option[BuildOptions],
requirements: Option[BuildRequirements],
mainClassOpt: Option[String]
scopedRequirements: Seq[Scoped[BuildRequirements]],
mainClassOpt: Option[String],
scopePath: ScopePath
) extends PreprocessedSource
final case class NoSourceCode(
options: Option[BuildOptions],
requirements: Option[BuildRequirements],
scopedRequirements: Seq[Scoped[BuildRequirements]],
path: os.Path
) extends PreprocessedSource {
def mainClassOpt: None.type = None
def scopePath: ScopePath =
ScopePath.fromPath(path)
}

private def index(s: PreprocessedSource): Int =
Expand Down Expand Up @@ -64,4 +76,29 @@ object PreprocessedSource {
}
}

final case class ScopePath(
root: String,
path: os.SubPath
) {
def /(subPath: os.PathChunk): ScopePath =
copy(path = path / subPath)
}

object ScopePath {
def fromPath(path: os.Path): ScopePath = {
def root(p: os.Path): os.Path =
if (p.segmentCount > 0) root(p / os.up) else p
val root0 = root(path)
ScopePath(root0.toString, path.subRelativeTo(root0))
}
}

final case class Scoped[T](path: ScopePath, value: T) {
def appliesTo(candidate: ScopePath): Boolean =
path.root == candidate.root &&
candidate.path.startsWith(path.path)
def valueFor(candidate: ScopePath): Option[T] =
if (appliesTo(candidate)) Some(value) else None
}

}
Loading