Skip to content
Draft
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
2 changes: 1 addition & 1 deletion contrib/jmh/src/mill/contrib/jmh/JmhModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ trait JmhModule extends JavaModule {
def generateBenchmarkSources =
Task {
val dest = Task.ctx().dest
val forkedArgs = forkArgs().toSeq
val forkedArgs = forkArgs().toStringSeq
val sourcesDir = dest / "jmh_sources"
val resourcesDir = dest / "jmh_resources"

Expand Down
21 changes: 10 additions & 11 deletions contrib/scoverage/src/mill/contrib/scoverage/ScoverageModule.scala
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package mill.contrib.scoverage

import coursier.Repository
import mill._
import mill.api.{PathRef}
import mill.api.BuildCtx
import mill.api.{Result}
import mill.*
import mill.api.{BuildCtx, PathRef, Result}
import mill.api.opt.*
import mill.contrib.scoverage.api.ScoverageReportWorkerApi2.ReportType
import mill.util.BuildInfo
import mill.javalib.api.JvmWorkerUtil
Expand Down Expand Up @@ -168,18 +167,18 @@ trait ScoverageModule extends ScalaModule { outer: ScalaModule =>
Task { outer.scalacPluginMvnDeps() ++ outer.scoveragePluginDeps() }

/** Add the scoverage specific plugin settings (`dataDir`). */
override def scalacOptions: T[Seq[String]] =
override def scalacOptions: T[Opts] =
Task {
val extras =
if (isScala3()) {
Seq(
s"-coverage-out:${data().path.toIO.getPath()}",
s"-sourceroot:${BuildCtx.workspaceRoot}"
Opts(
opt"-coverage-out:${data().path}",
opt"-sourceroot:${BuildCtx.workspaceRoot}"
)
} else {
Seq(
s"-P:scoverage:dataDir:${data().path.toIO.getPath()}",
s"-P:scoverage:sourceRoot:${BuildCtx.workspaceRoot}"
Opts(
opt"-P:scoverage:dataDir:${data().path}",
opt"-P:scoverage:sourceRoot:${BuildCtx.workspaceRoot}"
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ trait JavaModuleApi extends ModuleApi with GenIdeaModuleApi {

def transitiveModuleCompileModuleDeps: Seq[JavaModuleApi]

def javacOptions: TaskApi[Seq[String]]
def mandatoryJavacOptions: TaskApi[Seq[String]]
def javacOptions: TaskApi[OptsApi]
def mandatoryJavacOptions: TaskApi[OptsApi]

// BSP Tasks that sometimes need to be customized

Expand Down
12 changes: 12 additions & 0 deletions core/api/daemon/src/mill/api/daemon/internal/OptsApi.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package mill.api.daemon.internal

trait OptsApi {
def toStringSeq: Seq[String]
def value: Seq[OptGroupApi]
}

trait OptGroupApi {}

trait OptApi {
def toString(): String
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package mill.api.daemon.internal.bsp

import mill.api.daemon.internal.{EvaluatorApi, ModuleApi, TaskApi}
import mill.api.daemon.internal.{EvaluatorApi, ModuleApi, OptApi, OptsApi, TaskApi}

import java.nio.file.Path

Expand Down Expand Up @@ -34,21 +34,25 @@ trait BspJavaModuleApi extends ModuleApi {
)
: TaskApi[EvaluatorApi => (
classesPath: Path,
javacOptions: Seq[String],
javacOptions: OptsApi,
classpath: Seq[String]
)]

private[mill] def bspBuildTargetScalacOptions(
needsToMergeResourcesIntoCompileDest: Boolean,
enableJvmCompileClasspathProvider: Boolean,
clientWantsSemanticDb: Boolean
): TaskApi[(Seq[String], EvaluatorApi => Seq[String], EvaluatorApi => java.nio.file.Path)]
): TaskApi[(
scalacOptionsTask: OptsApi,
compileClasspathTask: EvaluatorApi => Seq[String],
classPathTask: EvaluatorApi => java.nio.file.Path
)]

private[mill] def bspBuildTargetScalaMainClasses
: TaskApi[(
classes: Seq[String],
forkArgs: Seq[String],
forkEnv: Map[String, String]
forkArgs: OptsApi,
forkEnv: Map[String, OptApi]
)]

private[mill] def bspLoggingTest: TaskApi[Unit]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package mill.api.daemon.internal.bsp

import mill.api.daemon.internal.{ModuleApi, TaskApi}
import mill.api.daemon.internal.{ModuleApi, OptApi, OptsApi, TaskApi}

import java.nio.file.Path

Expand All @@ -10,18 +10,18 @@ trait BspRunModuleApi extends ModuleApi {

private[mill] def bspJvmRunEnvironment: TaskApi[(
runClasspath: Seq[Path],
forkArgs: Seq[String],
forkArgs: OptsApi,
forkWorkingDir: Path,
forkEnv: Map[String, String],
forkEnv: Map[String, OptApi],
mainClass: Option[String],
localMainClasses: Seq[String]
)]

private[mill] def bspJvmTestEnvironment: TaskApi[(
runClasspath: Seq[Path],
forkArgs: Seq[String],
forkArgs: OptsApi,
forkWorkingDir: Path,
forkEnv: Map[String, String],
forkEnv: Map[String, OptApi],
mainClass: Option[String],
testEnvVars: Option[(
mainClass: String,
Expand Down
117 changes: 117 additions & 0 deletions core/api/src/mill/api/opt/Opt.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package mill.api.opt

import mill.api.daemon.internal.OptApi
import mill.api.JsonFormatters.given

import scala.annotation.targetName
import scala.language.implicitConversions

case class Opt private (value: Seq[Opt.OptTypes]) extends OptApi {
override def toString(): String = value.mkString("")

def map(conv: Opt.OptTypes => Opt.OptTypes): Opt = Opt(value.map(conv)*)

private def startString: String =
value.takeWhile(_.isInstanceOf[String]).collect { case s: String => s }.mkString("")

def startsWith(prefix: String): Boolean = startString.startsWith(prefix)

def mapStartString(rep: String => String): Opt = {
val rest = value.dropWhile(_.isInstanceOf[String])
Opt.apply((rep(startString) +: rest)*)
}

def containsPaths: Boolean = value.exists {
case _: os.Path => true
case _ => false
}
}

object Opt {

type OptTypes = (String | os.Path)

@targetName("applyVarArg")
def apply(value: OptTypes*): Opt = {
// TODO: merge sequential strings
new Opt(value.filter {
case s: String if s.isEmpty => false
case _ => true
})
}

/**
* Constructs a path from multiple path elements and a separator string.
* Can be used to render classpaths.
* Each path component will still be handled properly, e.g. mapped according to the current [[MappedPaths]] mapping.
*/
def mkPath(paths: Seq[os.Path], prefix: String = "", sep: String, suffix: String = ""): Opt = {
var needSep = false
Opt(
(
Seq(prefix) ++
paths.flatMap { path =>
if (needSep)
Seq(sep, path)
else {
needSep = true
Seq(path)
}
} ++ Seq(suffix)
)*
)
}

def mkPlatformPath(paths: Seq[os.Path]): Opt = mkPath(paths, sep = java.io.File.pathSeparator)

// given jsonReadWriter: upickle.ReadWriter[Opt] =
// upickle.readwriter[Seq[(Option[String], Option[os.Path])]].bimap(
// _.value.map {
// case path: os.Path => (None, Some(path))
// case str: String => (Some(str), None)
// },
// seq =>
// Opt(seq.map {
// case (Some(str), _) => str
// case (_, Some(path)) => path
// }*)
// )

given jsonReadWriter: upickle.ReadWriter[Opt] =
upickle.readwriter[ujson.Value].bimap(
opt =>
if (!opt.containsPaths) ujson.Str(opt.toString())
else opt.value.map {
case str: String => ujson.Str(str)
case path: os.Path => ujson.Obj("path" -> upickle.transform(path).to[ujson.Value])
},
{
case ujson.Str(opt) => Opt(opt)
case arr: ujson.Arr =>
val elems = arr.value.map {
case ujson.Str(opt) => opt
case ujson.Obj(map) => upickle.read[os.Path](map("path"))
}
Opt(elems.toSeq*)
}
)

// given stringToOpt: Conversion[String, Opt] = (value: String) => Opt(value)
// given osPathToOpt: Conversion[os.Path, Opt] = (value: os.Path) => Opt(value)

// implicit def IterableToOpt[T](s: Iterable[T])(using f: T => Opt): Opt =
// Opt(s.toSeq.flatMap(f(_).value))

implicit def AllToOpt(o: String | os.Path | Opt): Opt = o match {
case s: String => Opt(s)
case p: os.Path => Opt(p)
case o: Opt => o
}

// implicit def StringToOpt(s: String): Opt = Opt(s)
//
// implicit def OsPathToOpt(p: os.Path): Opt = Opt(p)
//
// implicit def OptToOpt(o: Opt): Opt = o

}
75 changes: 75 additions & 0 deletions core/api/src/mill/api/opt/OptGroup.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package mill.api.opt

import mill.api.daemon.internal.OptGroupApi

import scala.annotation.targetName
import scala.language.implicitConversions

/**
* A set of options, which are used together
*/
case class OptGroup private (value: Seq[Opt]) extends OptGroupApi {

override def toString(): String = value.mkString("(", ", ", ")")

def isEmpty: Boolean = value.isEmpty

def size: Int = value.size

def containsPaths: Boolean = value.exists(_.containsPaths)

def head: Opt = value.head
def headOption: Option[Opt] = value.headOption

def toStringSeq: Seq[String] = value.map(_.toString())

def concat(suffix: OptGroup): OptGroup = new OptGroup(value ++ suffix.value)

@`inline` final def ++(suffix: OptGroup): OptGroup = concat(suffix)

}

object OptGroup {
@targetName("applyVarAar")
def apply(opts: (String | os.Path | Opt | Seq[(String | os.Path | Opt)])*): OptGroup = {
val opts0 = opts.flatMap {
case s: String => Seq(Opt(s))
case p: os.Path => Seq(Opt(p))
case o: Opt => Seq(o)
case o: Seq[(String | os.Path | Opt)] =>
o.map {
case s: String => Opt(s)
case p: os.Path => Opt(p)
case o: Opt => o
}
}
new OptGroup(opts0)
}
// @targetName("applyIterable")
// def apply[T](opts: T*)(using f: T => Opt): OptGroup = new OptGroup(opts.map(f(_)))

def when(cond: Boolean)(value: Opt*): OptGroup = if (cond) OptGroup(value*) else OptGroup()

// given optsToOptGroup: Conversion[(OptTypes, OptTypes), OptGroup] =
// (tuple: (OptTypes, OptTypes)) =>
// OptGroup(Opt(tuple._1), Opt(tuple._2))

// implicit def StringToOptGroup(s: String): OptGroup = OptGroup(Seq(Opt(s)))
//
// implicit def OsPathToOptGroup(p: os.Path): OptGroup = OptGroup(Seq(Opt(p)))
//
// implicit def OptToOptGroup(o: Opt): OptGroup = OptGroup(Seq(o))
//
// implicit def IterableToOptGroup[T](s: Iterable[T])(using f: T => OptGroup): OptGroup =
// OptGroup(s.toSeq.flatMap(f(_).value))

// implicit def ArrayToOptGroup[T](s: Array[T])(using f: T => OptGroup): OptGroup =
// OptGroup(s.flatMap(f(_).value))

given jsonReadWriter: upickle.ReadWriter[OptGroup] =
upickle.readwriter[Seq[Opt]].bimap(
_.value,
OptGroup(_*)
)

}
16 changes: 16 additions & 0 deletions core/api/src/mill/api/opt/OptSyntax.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package mill.api.opt

import scala.language.implicitConversions

implicit class OptSyntax(ctx: StringContext) extends AnyVal {
def opt(opts: Any*): Opt = {
val vals = ctx.parts.take(opts.length).zip(opts).flatMap { case (p, a) => Seq(p, a) } ++
ctx.parts.drop(opts.length)

val elems: Seq[(String | os.Path)] = vals.flatMap {
case path: os.Path => Seq(path)
case s => Seq(s.toString).filter(_.nonEmpty)
}
Opt(elems*)
}
}
Loading