
mill.scalalib.JavaModule.scala Maven / Gradle / Ivy
The newest version!
package mill
package scalalib
import coursier.{core => cs}
import coursier.core.{BomDependency, Configuration, DependencyManagement, Resolution}
import coursier.params.ResolutionParams
import coursier.parse.JavaOrScalaModule
import coursier.parse.ModuleParser
import coursier.util.{EitherT, ModuleMatcher, Monad}
import coursier.{Repository, Type}
import mainargs.{Flag, arg}
import mill.Agg
import mill.api.{Ctx, JarManifest, MillException, PathRef, Result, internal}
import mill.define.{Command, ModuleRef, Segment, Task, TaskModule}
import mill.scalalib.internal.ModuleUtils
import mill.scalalib.api.CompilationResult
import mill.scalalib.bsp.{BspBuildTarget, BspModule, BspUri, JvmBuildTarget}
import mill.scalalib.publish.Artifact
import mill.util.Jvm
import os.{Path, ProcessOutput}
import scala.annotation.nowarn
/**
* Core configuration required to compile a single Java compilation target
*/
trait JavaModule
extends mill.Module
with WithZincWorker
with TestModule.JavaModuleBase
with TaskModule
with RunModule
with GenIdeaModule
with CoursierModule
with OfflineSupportModule
with BspModule
with SemanticDbJavaModule
with AssemblyModule { outer =>
override def zincWorker: ModuleRef[ZincWorkerModule] = super.zincWorker
@nowarn
type JavaTests = JavaModuleTests
@deprecated("Use JavaTests instead", since = "Mill 0.11.10")
trait JavaModuleTests extends JavaModule with TestModule {
// Run some consistence checks
hierarchyChecks()
override def resources = super[JavaModule].resources
override def moduleDeps: Seq[JavaModule] = Seq(outer)
override def repositoriesTask: Task[Seq[Repository]] = Task.Anon {
outer.repositoriesTask()
}
override def resolutionCustomizer: Task[Option[coursier.Resolution => coursier.Resolution]] =
outer.resolutionCustomizer
override def javacOptions: T[Seq[String]] = Task { outer.javacOptions() }
override def zincWorker: ModuleRef[ZincWorkerModule] = outer.zincWorker
override def skipIdea: Boolean = outer.skipIdea
override def runUseArgsFile: T[Boolean] = Task { outer.runUseArgsFile() }
override def sources = Task.Sources {
for (src <- outer.sources()) yield {
PathRef(this.millSourcePath / src.path.relativeTo(outer.millSourcePath))
}
}
override def bomIvyDeps = Task.Anon[Agg[Dep]] {
// FIXME Add that back when we can break bin-compat
// super.bomIvyDeps() ++
outer.bomIvyDeps()
}
override def depManagement = Task.Anon[Agg[Dep]] {
// FIXME Add that back when we can break bin-compat
// super.depManagement() ++
outer.depManagement()
}
/**
* JavaModule and its derivatives define inner test modules.
* To avoid unexpected misbehavior due to the use of the wrong inner test trait
* we apply some hierarchy consistency checks.
* If, for some reason, those are too restrictive to you, you can override this method.
* @throws MillException
*/
protected def hierarchyChecks(): Unit = {
val outerInnerSets = Seq(
("mill.scalajslib.ScalaJSModule", "ScalaJSTests"),
("mill.scalanativelib.ScalaNativeModule", "ScalaNativeTests"),
("mill.scalalib.SbtModule", "SbtTests"),
("mill.scalalib.MavenModule", "MavenTests")
)
for {
(mod, testModShort) <- outerInnerSets
testMod = s"${mod}$$${testModShort}"
}
try {
if (Class.forName(mod).isInstance(outer) && !Class.forName(testMod).isInstance(this))
throw new MillException(
s"$outer is a `${mod}`. $this needs to extend `${testModShort}`."
)
} catch {
case _: ClassNotFoundException => // if we can't find the classes, we certainly are not in a ScalaJSModule
}
}
}
def defaultCommandName(): String = "run"
def resolvePublishDependency: Task[Dep => publish.Dependency] = Task.Anon {
Artifact.fromDepJava(_: Dep)
}
/**
* Allows you to specify an explicit main class to use for the `run` command.
* If none is specified, the classpath is searched for an appropriate main
* class to use if one exists
*/
def mainClass: T[Option[String]] = None
def finalMainClassOpt: T[Either[String, String]] = Task {
mainClass() match {
case Some(m) => Right(m)
case None =>
if (zincWorker().javaHome().isDefined) {
super[RunModule].finalMainClassOpt()
} else {
zincWorker().worker().discoverMainClasses(compile()) match {
case Seq() => Left("No main class specified or found")
case Seq(main) => Right(main)
case mains =>
Left(
s"Multiple main classes found (${mains.mkString(",")}) " +
"please explicitly specify which one to use by overriding `mainClass` " +
"or using `runMain <...args>` instead of `run`"
)
}
}
}
}
def finalMainClass: T[String] = Task {
finalMainClassOpt() match {
case Right(main) => Result.Success(main)
case Left(msg) => Result.Failure(msg)
}
}
/**
* Mandatory ivy dependencies that are typically always required and shouldn't be removed by
* overriding [[ivyDeps]], e.g. the scala-library in the [[ScalaModule]].
*/
def mandatoryIvyDeps: T[Agg[Dep]] = Task { Agg.empty[Dep] }
/**
* Any ivy dependencies you want to add to this Module, in the format
* ivy"org::name:version" for Scala dependencies or ivy"org:name:version"
* for Java dependencies
*/
def ivyDeps: T[Agg[Dep]] = Task { Agg.empty[Dep] }
/**
* Aggregation of mandatoryIvyDeps and ivyDeps.
* In most cases, instead of overriding this Target you want to override `ivyDeps` instead.
*/
def allIvyDeps: T[Agg[Dep]] = Task { ivyDeps() ++ mandatoryIvyDeps() }
/**
* Same as `ivyDeps`, but only present at compile time. Useful for e.g.
* macro-related dependencies like `scala-reflect` that doesn't need to be
* present at runtime
*/
def compileIvyDeps: T[Agg[Dep]] = Task { Agg.empty[Dep] }
/**
* Additional dependencies, only present at runtime. Useful for e.g.
* selecting different versions of a dependency to use at runtime after your
* code has already been compiled.
*/
def runIvyDeps: T[Agg[Dep]] = Task { Agg.empty[Dep] }
/**
* Any Bill of Material (BOM) dependencies you want to add to this Module, in the format
* ivy"org:name:version"
*/
def bomIvyDeps: T[Agg[Dep]] = Task { Agg.empty[Dep] }
def allBomDeps: Task[Agg[BomDependency]] = Task.Anon {
val modVerOrMalformed =
bomIvyDeps().map(bindDependency()).map { bomDep =>
val fromModVer = coursier.core.Dependency(bomDep.dep.module, bomDep.dep.version)
if (fromModVer == bomDep.dep)
Right(bomDep.dep.asBomDependency)
else
Left(bomDep)
}
val malformed = modVerOrMalformed.collect {
case Left(malformedBomDep) =>
malformedBomDep
}
if (malformed.isEmpty)
modVerOrMalformed.collect {
case Right(bomDep) => bomDep
}
else
throw new Exception(
"Found Bill of Material (BOM) dependencies with invalid parameters:" + System.lineSeparator() +
malformed.map("- " + _.dep + System.lineSeparator()).mkString +
"Only organization, name, and version are accepted."
)
}
/**
* Dependency management data
*
* Versions and exclusions in dependency management override those of transitive dependencies,
* while they have no effect if the corresponding dependency isn't pulled during dependency
* resolution.
*
* For example, the following forces com.lihaoyi::os-lib to version 0.11.3, and
* excludes org.slf4j:slf4j-api from com.lihaoyi::cask that it forces to version 0.9.4
* {{{
* def depManagement = super.depManagement() ++ Agg(
* ivy"com.lihaoyi::os-lib:0.11.3",
* ivy"com.lihaoyi::cask:0.9.5".exclude("org.slf4j", "slf4j-api")
* )
* }}}
*/
def depManagement: T[Agg[Dep]] = Task { Agg.empty[Dep] }
/**
* Data from depManagement, converted to a type ready to be passed to coursier
* for dependency resolution
*/
protected def processedDependencyManagement(deps: Seq[coursier.core.Dependency])
: Seq[(DependencyManagement.Key, DependencyManagement.Values)] = {
val keyValuesOrErrors =
deps.map { depMgmt =>
val fromUsedValues = coursier.core.Dependency(depMgmt.module, depMgmt.version)
.withPublication(coursier.core.Publication(
"",
depMgmt.publication.`type`,
coursier.core.Extension.empty,
depMgmt.publication.classifier
))
.withMinimizedExclusions(depMgmt.minimizedExclusions)
.withOptional(depMgmt.optional)
if (fromUsedValues == depMgmt) {
val key = DependencyManagement.Key(
depMgmt.module.organization,
depMgmt.module.name,
if (depMgmt.publication.`type`.isEmpty) coursier.core.Type.jar
else depMgmt.publication.`type`,
depMgmt.publication.classifier
)
val values = DependencyManagement.Values(
Configuration.empty,
depMgmt.version,
depMgmt.minimizedExclusions,
depMgmt.optional
)
Right(key -> values)
} else
Left(depMgmt)
}
val errors = keyValuesOrErrors.collect {
case Left(errored) => errored
}
if (errors.isEmpty)
keyValuesOrErrors.collect { case Right(kv) => kv }
else
throw new Exception(
"Found dependency management entries with invalid values. Only organization, name, version, type, classifier, exclusions, and optionality can be specified" + System.lineSeparator() +
errors.map("- " + _ + System.lineSeparator()).mkString
)
}
/**
* Default artifact types to fetch and put in the classpath. Add extra types
* here if you'd like fancy artifact extensions to be fetched.
*/
def artifactTypes: T[Set[Type]] = Task { coursier.core.Resolution.defaultTypes }
/**
* Options to pass to the java compiler
*/
def javacOptions: T[Seq[String]] = Task { Seq.empty[String] }
/**
* Additional options for the java compiler derived from other module settings.
*/
def mandatoryJavacOptions: T[Seq[String]] = Task { Seq.empty[String] }
/**
* The direct dependencies of this module.
* This is meant to be overridden to add dependencies.
* To read the value, you should use [[moduleDepsChecked]] instead,
* which uses a cached result which is also checked to be free of cycle.
* @see [[moduleDepschecked]]
*/
def moduleDeps: Seq[JavaModule] = Seq.empty
/**
* The compile-only direct dependencies of this module. These are *not*
* transitive, and only take effect in the module that they are declared in.
*/
def compileModuleDeps: Seq[JavaModule] = Seq.empty
/**
* The runtime-only direct dependencies of this module. These *are* transitive,
* and so get propagated to downstream modules automatically
*/
def runModuleDeps: Seq[JavaModule] = Seq.empty
/**
* Bill of Material (BOM) dependencies of this module.
* This is meant to be overridden to add BOM dependencies.
* To read the value, you should use [[bomModuleDepsChecked]] instead,
* which uses a cached result which is also checked to be free of cycles.
* @see [[bomModuleDepsChecked]]
*/
def bomModuleDeps: Seq[BomModule] = Seq.empty
/**
* Same as [[moduleDeps]] but checked to not contain cycles.
* Prefer this over using [[moduleDeps]] directly.
*/
final def moduleDepsChecked: Seq[JavaModule] = {
// trigger initialization to check for cycles
recModuleDeps
moduleDeps
}
/** Same as [[compileModuleDeps]] but checked to not contain cycles. */
final def compileModuleDepsChecked: Seq[JavaModule] = {
// trigger initialization to check for cycles
recCompileModuleDeps
compileModuleDeps
}
/**
* Same as [[moduleDeps]] but checked to not contain cycles.
* Prefer this over using [[moduleDeps]] directly.
*/
final def runModuleDepsChecked: Seq[JavaModule] = {
// trigger initialization to check for cycles
recRunModuleDeps
runModuleDeps
}
/**
* Same as [[bomModuleDeps]] but checked to not contain cycles.
* Prefer this over using [[bomModuleDeps]] directly.
*/
final def bomModuleDepsChecked: Seq[BomModule] = {
// trigger initialization to check for cycles
recBomModuleDeps
bomModuleDeps
}
/** Should only be called from [[moduleDepsChecked]] */
private lazy val recModuleDeps: Seq[JavaModule] =
ModuleUtils.recursive[JavaModule](
(millModuleSegments ++ Seq(Segment.Label("moduleDeps"))).render,
this,
_.moduleDeps
)
/** Should only be called from [[compileModuleDeps]] */
private lazy val recCompileModuleDeps: Seq[JavaModule] =
ModuleUtils.recursive[JavaModule](
(millModuleSegments ++ Seq(Segment.Label("compileModuleDeps"))).render,
this,
_.compileModuleDeps
)
/** Should only be called from [[runModuleDepsChecked]] */
private lazy val recRunModuleDeps: Seq[JavaModule] =
ModuleUtils.recursive[JavaModule](
(millModuleSegments ++ Seq(Segment.Label("runModuleDeps"))).render,
this,
m => m.runModuleDeps ++ m.moduleDeps
)
/** Should only be called from [[bomModuleDepsChecked]] */
private lazy val recBomModuleDeps: Seq[BomModule] =
ModuleUtils.recursive[BomModule](
(millModuleSegments ++ Seq(Segment.Label("bomModuleDeps"))).render,
null,
mod => if (mod == null) bomModuleDeps else mod.bomModuleDeps
)
/** The direct and indirect dependencies of this module */
def recursiveModuleDeps: Seq[JavaModule] = { recModuleDeps }
/** The direct and indirect runtime module dependencies of this module */
def recursiveRunModuleDeps: Seq[JavaModule] = { recRunModuleDeps }
/**
* Like `recursiveModuleDeps` but also include the module itself,
* basically the modules whose classpath are needed at runtime
*/
def transitiveModuleDeps: Seq[JavaModule] = recursiveModuleDeps ++ Seq(this)
/**
* Like `recursiveModuleDeps` but also include the module itself,
* basically the modules whose classpath are needed at runtime
*/
def transitiveRunModuleDeps: Seq[JavaModule] = recursiveRunModuleDeps ++ Seq(this)
/**
* All direct and indirect module dependencies of this module, including
* compile-only dependencies: basically the modules whose classpath are needed
* at compile-time.
*
* Note that `compileModuleDeps` are defined to be non-transitive, so we only
* look at the direct `compileModuleDeps` when assembling this list
*/
def transitiveModuleCompileModuleDeps: Seq[JavaModule] = {
(moduleDepsChecked ++ compileModuleDepsChecked).flatMap(_.transitiveModuleDeps).distinct
}
/**
* All direct and indirect module dependencies of this module, including
* compile-only dependencies: basically the modules whose classpath are needed
* at runtime.
*
* Note that `runModuleDeps` are defined to be transitive
*/
def transitiveModuleRunModuleDeps: Seq[JavaModule] = {
(runModuleDepsChecked ++ moduleDepsChecked).flatMap(_.transitiveRunModuleDeps).distinct
}
private def formatModuleDeps(recursive: Boolean, includeHeader: Boolean): Task[String] =
Task.Anon {
val normalDeps = if (recursive) recursiveModuleDeps else moduleDepsChecked
val compileDeps =
if (recursive) compileModuleDepsChecked.flatMap(_.transitiveModuleDeps).distinct
else compileModuleDepsChecked
val runtimeDeps =
if (recursive) runModuleDepsChecked.flatMap(_.transitiveRunModuleDeps).distinct
else runModuleDepsChecked
val deps = (normalDeps ++ compileDeps ++ runModuleDeps).distinct
val header = Option.when(includeHeader)(
s"${if (recursive) "Recursive module" else "Module"} dependencies of ${millModuleSegments.render}:"
).toSeq
val lines = deps.map { dep =>
val isNormal = normalDeps.contains(dep)
val markers = Seq(
Option.when(!isNormal && compileDeps.contains(dep))("compile"),
Option.when(!isNormal && runtimeDeps.contains(dep))("runtime")
).flatten
val suffix = if (markers.isEmpty) "" else markers.mkString(" (", ",", ")")
" " + dep.millModuleSegments.render + suffix
}
(header ++ lines).mkString("\n")
}
/**
* Show the module dependencies.
* @param recursive If `true` include all recursive module dependencies, else only show direct dependencies.
*/
def showModuleDeps(recursive: Boolean = false): Command[Unit] = {
// This is exclusive to avoid scrambled output
Task.Command(exclusive = true) {
val asString = formatModuleDeps(recursive, true)()
Task.log.outputStream.println(asString)
}
}
/**
* Additional jars, classfiles or resources to add to the classpath directly
* from disk rather than being downloaded from Maven Central or other package
* repositories
*/
def unmanagedClasspath: T[Agg[PathRef]] = Task { Agg.empty[PathRef] }
/**
* The `coursier.Dependency` to use to refer to this module
*/
def coursierDependency: cs.Dependency =
// this is a simple def, and not a Task, as this is simple enough and needed in places
// where eval'ing a Task would be impractical or not allowed
cs.Dependency(
cs.Module(
JavaModule.internalOrg,
coursier.core.ModuleName(millModuleSegments.parts.mkString("-")),
Map.empty
),
JavaModule.internalVersion
).withConfiguration(cs.Configuration.compile)
/**
* The `coursier.Project` corresponding to this `JavaModule`.
*
* This provides details about this module to the coursier resolver (details such as
* dependencies, BOM dependencies, dependency management, etc.). Beyond more general
* resolution parameters (such as artifact types, etc.), this should be the only way
* we provide details about this module to coursier.
*/
def coursierProject: Task[cs.Project] = Task.Anon {
coursierProject0()
}
private[mill] def coursierProject0: Task[cs.Project] = Task.Anon {
// Tells coursier that if something depends on a given scope of ours, we should also
// pull other scopes of our own dependencies.
//
// E.g. scopes(runtime) contains compile, so depending on us as a runtime dependency
// will not only pull our runtime dependencies, but also our compile ones.
//
// This is the default scope mapping used in coursier for Maven dependencies, but for
// one scope: provided. By default in Maven, depending on a dependency as provided
// doesn't pull anything. Here, by explicitly adding provided to the values,
// we make coursier pull our own provided dependencies.
//
// Note that this is kind of a hack: by default, pulling a dependency in scope A
// pulls its scope A dependencies. But this is withheld for provided, unless it's
// added back explicitly like we do here.
val scopes = Map(
cs.Configuration.compile -> Seq.empty,
cs.Configuration.runtime -> Seq(cs.Configuration.compile),
cs.Configuration.default -> Seq(cs.Configuration.runtime),
cs.Configuration.test -> Seq(cs.Configuration.runtime),
// hack, so that depending on `coursierDependency.withConfiguration(Configuration.provided)`
// pulls our provided dependencies (rather than nothing)
cs.Configuration.provided -> Seq(cs.Configuration.provided)
)
val internalDependencies =
bomModuleDepsChecked.map { modDep =>
val dep = coursier.core.Dependency(
coursier.core.Module(
coursier.core.Organization("mill-internal"),
coursier.core.ModuleName(modDep.millModuleSegments.parts.mkString("-")),
Map.empty
),
"0+mill-internal"
)
(coursier.core.Configuration.`import`, dep)
} ++
moduleDepsChecked.flatMap { modDep =>
// Standard dependencies
// We pull their compile scope when our compile scope is asked,
// and pull their runtime scope when our runtime scope is asked.
Seq(
(
cs.Configuration.compile,
modDep.coursierDependency.withConfiguration(cs.Configuration.compile)
),
(
cs.Configuration.runtime,
modDep.coursierDependency.withConfiguration(cs.Configuration.runtime)
)
)
} ++
compileModuleDepsChecked.map { modDep =>
// Compile-only (aka provided) dependencies
// We pull their compile scope when our provided scope is asked (see scopes above)
(
cs.Configuration.provided,
modDep.coursierDependency.withConfiguration(cs.Configuration.compile)
)
} ++
runModuleDepsChecked.map { modDep =>
// Runtime dependencies
// We pull their runtime scope when our runtime scope is pulled
(
cs.Configuration.runtime,
modDep.coursierDependency.withConfiguration(cs.Configuration.runtime)
)
}
val dependencies =
(mandatoryIvyDeps() ++ ivyDeps()).map(bindDependency()).map(_.dep).iterator.toSeq.flatMap {
dep =>
// Standard dependencies, like above
// We pull their compile scope when our compile scope is asked,
// and pull their runtime scope when our runtime scope is asked.
Seq(
(cs.Configuration.compile, dep.withConfiguration(cs.Configuration.compile)),
(cs.Configuration.runtime, dep.withConfiguration(cs.Configuration.runtime))
)
} ++
compileIvyDeps().map(bindDependency()).map(_.dep).map { dep =>
// Compile-only (aka provided) dependencies, like above
// We pull their compile scope when our provided scope is asked (see scopes above)
(cs.Configuration.provided, dep.withConfiguration(cs.Configuration.compile))
} ++
runIvyDeps().map(bindDependency()).map(_.dep).map { dep =>
// Runtime dependencies, like above
// We pull their runtime scope when our runtime scope is pulled
(
cs.Configuration.runtime,
dep.withConfiguration(cs.Configuration.runtime)
)
} ++
allBomDeps().map { bomDep =>
// BOM dependencies
// Maven has a special scope for those: "import"
val dep =
cs.Dependency(bomDep.module, bomDep.version).withConfiguration(bomDep.config)
(cs.Configuration.`import`, dep)
}
val depMgmt =
processedDependencyManagement(
depManagement().iterator.toSeq.map(bindDependency()).map(_.dep)
).map {
case (key, values) =>
val config0 =
if (values.config.isEmpty) cs.Configuration.compile
else values.config
(config0, values.fakeDependency(key))
}
cs.Project(
module = coursierDependency.module,
version = coursierDependency.version,
dependencies = internalDependencies ++ dependencies,
configurations = scopes,
parent = None,
dependencyManagement = depMgmt,
properties = Nil,
profiles = Nil,
versions = None,
snapshotVersioning = None,
packagingOpt = None,
relocated = false,
actualVersionOpt = None,
publications = Nil,
info = coursier.core.Info.empty
)
}
/**
* Coursier project of this module and those of all its transitive module dependencies
*/
def transitiveCoursierProjects: Task[Seq[cs.Project]] = Task {
(Seq(coursierProject()) ++
Task.traverse(
(compileModuleDepsChecked ++ moduleDepsChecked ++ runModuleDepsChecked ++ bomModuleDepsChecked).distinct
)(_.transitiveCoursierProjects)().flatten).distinctBy(_.module)
}
/**
* The Ivy dependencies of this module, with Bill of Material (BOM) and dependency management details
* added to them. This should be used when propagating the dependencies transitively
* to other modules.
*/
@deprecated("Unused by Mill, use allIvyDeps instead", "Mill after 0.12.5")
def processedIvyDeps: Task[Agg[BoundDep]] = Task {
allIvyDeps().map(bindDependency())
}
/**
* Returns a function adding BOM and dependency management details of
* this module to a `coursier.core.Dependency`
*/
@deprecated("Unused by Mill", "Mill after 0.12.5")
def processDependency(
overrideVersions: Boolean = false
): Task[coursier.core.Dependency => coursier.core.Dependency] =
Task.Anon((x: coursier.core.Dependency) => x)
/**
* The transitive ivy dependencies of this module and all it's upstream modules.
* This is calculated from [[ivyDeps]], [[mandatoryIvyDeps]] and recursively from [[moduleDeps]].
*
* This isn't used by Mill anymore. Instead of this, consider using either:
* * `coursierDependency`, which will pull all this module's dependencies transitively
* * `allIvyDeps`, which contains the full list of direct (external) dependencies of this module
*/
@deprecated("Unused by Mill, use coursierDependency or allIvyDeps instead", "Mill after 0.12.5")
def transitiveIvyDeps: T[Agg[BoundDep]] = Task {
allIvyDeps().map(bindDependency()) ++
Task.traverse(moduleDepsChecked)(_.transitiveIvyDeps)().flatten
}
/**
* The compile-only transitive ivy dependencies of this module and all its upstream compile-only modules.
*
* This isn't used by Mill anymore. Instead of this, consider using either:
* * `coursierDependency().withConfiguration(Configuration.provided`), which will pull all
* this module's compile-only dependencies transitively
* * `compileIvyDeps`, which contains the full list of direct (external) compile-only
* dependencies of this module
*/
@deprecated(
"Unused by Mill, use coursierDependency().withConfiguration(Configuration.provided) or compileIvyDeps instead",
"Mill after 0.12.5"
)
def transitiveCompileIvyDeps: T[Agg[BoundDep]] = Task {
compileIvyDeps().map(bindDependency()) ++
Task.traverse(moduleDepsChecked)(_.transitiveCompileIvyDeps)().flatten
}
/**
* The transitive run ivy dependencies of this module and all it's upstream modules.
* This is calculated from [[runIvyDeps]], [[mandatoryIvyDeps]] and recursively from [[moduleDeps]].
*
* This isn't used by Mill anymore. Instead of this, consider using either:
* * `coursierDependency().withConfiguration(Configuration.runtime`), which will pull all
* this module's runtime dependencies transitively
* * `runIvyDeps`, which contains the full list of direct (external) runtime
* dependencies of this module
*/
@deprecated(
"Unused by Mill, use coursierDependency().withConfiguration(Configuration.runtime) or runIvyDeps instead",
"Mill after 0.12.5"
)
def transitiveRunIvyDeps: T[Agg[BoundDep]] = Task {
runIvyDeps().map(bindDependency()) ++
Task.traverse(moduleDepsChecked)(_.transitiveRunIvyDeps)().flatten
}
/**
* The repository that knows about this project itself and its module dependencies
*/
def internalDependenciesRepository: Task[cs.Repository] = Task.Anon {
// This is the main point of contact between the coursier resolver and Mill.
// Basically, all relevant Mill modules are aggregated and converted to a
// coursier.Project (provided by JavaModule#coursierProject).
//
// Dependencies, both external ones (like ivyDeps, bomIvyDeps, etc.) and internal ones
// (like moduleDeps) are put in coursier.Project#dependencies. The coursier.Dependency
// used to represent each module is built by JavaModule#coursierDependency. So we put
// JavaModule#coursierDependency in the dependencies field of other modules'
// JavaModule#coursierProject to represent links between them.
//
// coursier.Project#dependencies accepts (coursier.Configuration, coursier.Dependency) tuples.
// The configuration is different for compile-time only / runtime / BOM dependencies
// (it's respectively provided, runtime, import). The configuration is compile for
// standard ivyDeps / moduleDeps.
//
JavaModule.InternalRepo(transitiveCoursierProjects().distinctBy(_.module.name.value))
}
/**
* Mill internal repositories to be used during dependency resolution
*
* These are not meant to be modified by Mill users, unless you really know what you're
* doing.
*/
private[mill] def internalRepositories: Task[Seq[cs.Repository]] = Task.Anon {
Seq(internalDependenciesRepository())
}
/**
* The upstream compilation output of all this module's upstream modules
*/
def upstreamCompileOutput: T[Seq[CompilationResult]] = Task {
Task.traverse(transitiveModuleCompileModuleDeps)(_.compile)
}
/**
* The transitive version of `localClasspath`
*/
def transitiveLocalClasspath: T[Agg[PathRef]] = Task {
Task.traverse(transitiveModuleRunModuleDeps)(_.localClasspath)().flatten
}
/**
* Almost the same as [[transitiveLocalClasspath]], but using the [[jar]]s instead of [[localClasspath]].
*/
def transitiveJars: T[Seq[PathRef]] = Task {
Task.traverse(transitiveModuleCompileModuleDeps)(_.jar)()
}
/**
* Same as [[transitiveLocalClasspath]], but with all dependencies on [[compile]]
* replaced by their non-compiling [[bspCompileClassesPath]] variants.
*
* Keep in sync with [[transitiveLocalClasspath]]
*/
@internal
def bspTransitiveLocalClasspath: T[Agg[UnresolvedPath]] = Task {
Task.traverse(transitiveModuleCompileModuleDeps)(_.bspLocalClasspath)().flatten
}
/**
* The transitive version of `compileClasspath`
*/
def transitiveCompileClasspath: T[Agg[PathRef]] = Task {
Task.traverse(transitiveModuleCompileModuleDeps)(m =>
Task.Anon { m.localCompileClasspath() ++ Agg(m.compile().classes) }
)().flatten
}
/**
* Same as [[transitiveCompileClasspath]], but with all dependencies on [[compile]]
* replaced by their non-compiling [[bspCompileClassesPath]] variants.
*
* Keep in sync with [[transitiveCompileClasspath]]
*/
@internal
def bspTransitiveCompileClasspath: T[Agg[UnresolvedPath]] = Task {
Task.traverse(transitiveModuleCompileModuleDeps)(m =>
Task.Anon {
m.localCompileClasspath().map(p => UnresolvedPath.ResolvedPath(p.path)) ++
Agg(m.bspCompileClassesPath())
}
)()
.flatten
}
/**
* What platform suffix to use for publishing, e.g. `_sjs` for Scala.js
* projects
*/
def platformSuffix: T[String] = Task { "" }
// bincompat stub
def prependShellScript: T[String] = Task {
prependShellScript0()
}
/**
* Configuration for the [[assembly]] task: how files and file-conflicts are
* managed when combining multiple jar files into one big assembly jar.
*/
def assemblyRules: Seq[Assembly.Rule] = Assembly.defaultRules
/**
* The folders where the source files for this module live
*/
def sources: T[Seq[PathRef]] = Task.Sources { "src" }
/**
* The folders where the resource files for this module live.
* If you need resources to be seen by the compiler, use [[compileResources]].
*/
def resources: T[Seq[PathRef]] = Task.Sources { "resources" }
/**
* The folders where the compile time resource files for this module live.
* If your resources files do not necessarily need to be seen by the compiler,
* you should use [[resources]] instead.
*/
def compileResources: T[Seq[PathRef]] = Task.Sources { "compile-resources" }
/**
* Folders containing source files that are generated rather than
* handwritten; these files can be generated in this target itself,
* or can refer to files generated from other targets
*/
def generatedSources: T[Seq[PathRef]] = Task { Seq.empty[PathRef] }
/**
* The folders containing all source files fed into the compiler
*/
def allSources: T[Seq[PathRef]] = Task { sources() ++ generatedSources() }
/**
* All individual source files fed into the Java compiler
*/
def allSourceFiles: T[Seq[PathRef]] = Task {
Lib.findSourceFiles(allSources(), Seq("java")).map(PathRef(_))
}
/**
* If `true`, we always show problems (errors, warnings, infos) found in all source files, even when they have not changed since the previous incremental compilation.
* When `false`, we report only problems for files which we re-compiled.
*/
def zincReportCachedProblems: T[Boolean] = Task.Input {
sys.props.getOrElse(
"mill.scalalib.JavaModule.zincReportCachedProblems",
"false"
).equalsIgnoreCase("true")
}
def zincIncrementalCompilation: T[Boolean] = Task {
true
}
/**
* Compiles the current module to generate compiled classfiles/bytecode.
*
* When you override this, you probably also want/need to override [[bspCompileClassesPath]],
* as that needs to point to the same compilation output path.
*
* Keep in sync with [[bspCompileClassesPath]]
*/
def compile: T[mill.scalalib.api.CompilationResult] = Task(persistent = true) {
zincWorker()
.worker()
.compileJava(
upstreamCompileOutput = upstreamCompileOutput(),
sources = allSourceFiles().map(_.path),
compileClasspath = compileClasspath().map(_.path),
javacOptions = javacOptions() ++ mandatoryJavacOptions(),
reporter = Task.reporter.apply(hashCode),
reportCachedProblems = zincReportCachedProblems(),
incrementalCompilation = zincIncrementalCompilation()
)
}
/**
* The path to the compiled classes by [[compile]] without forcing to actually run the compilation.
* This is safe in an BSP context, as the compilation done later will use the
* exact same compilation settings, so we can safely use the same path.
*
* Keep in sync with [[compile]]
*/
@internal
def bspCompileClassesPath: T[UnresolvedPath] =
if (compile.ctx.enclosing == s"${classOf[JavaModule].getName}#compile") {
Task {
Task.log.debug(
s"compile target was not overridden, assuming hard-coded classes directory for target ${compile}"
)
UnresolvedPath.DestPath(os.sub / "classes", compile.ctx.segments, compile.ctx.foreign)
}
} else {
Task {
Task.log.debug(
s"compile target was overridden, need to actually execute compilation to get the compiled classes directory for target ${compile}"
)
UnresolvedPath.ResolvedPath(compile().classes.path)
}
}
/**
* The part of the [[localClasspath]] which is available "after compilation".
*
* Keep in sync with [[bspLocalRunClasspath]]
*/
override def localRunClasspath: T[Seq[PathRef]] = Task {
super.localRunClasspath() ++ resources() ++
Agg(compile().classes)
}
/**
* Same as [[localRunClasspath]] but for use in BSP server.
*
* Keep in sync with [[localRunClasspath]]
*/
def bspLocalRunClasspath: T[Agg[UnresolvedPath]] = Task {
Agg.from(super.localRunClasspath() ++ resources())
.map(p => UnresolvedPath.ResolvedPath(p.path)) ++
Agg(bspCompileClassesPath())
}
/**
* The *output* classfiles/resources from this module, used for execution,
* excluding upstream modules and third-party dependencies, but including unmanaged dependencies.
*
* This is build from [[localCompileClasspath]] and [[localRunClasspath]]
* as the parts available "before compilation" and "after compilation".
*
* Keep in sync with [[bspLocalClasspath]]
*/
def localClasspath: T[Seq[PathRef]] = Task {
localCompileClasspath().toSeq ++ localRunClasspath()
}
/**
* Same as [[localClasspath]], but with all dependencies on [[compile]]
* replaced by their non-compiling [[bspCompileClassesPath]] variants.
*
* Keep in sync with [[localClasspath]]
*/
@internal
def bspLocalClasspath: T[Agg[UnresolvedPath]] = Task {
(localCompileClasspath()).map(p => UnresolvedPath.ResolvedPath(p.path)) ++
bspLocalRunClasspath()
}
/**
* All classfiles and resources from upstream modules and dependencies
* necessary to compile this module.
*
* Keep in sync with [[bspCompileClasspath]]
*/
def compileClasspath: T[Agg[PathRef]] = Task {
resolvedIvyDeps() ++ transitiveCompileClasspath() ++ localCompileClasspath()
}
/**
* Same as [[compileClasspath]], but does not trigger compilation targets, if possible.
*
* Keep in sync with [[compileClasspath]]
*/
@internal
def bspCompileClasspath: T[Agg[UnresolvedPath]] = Task {
resolvedIvyDeps().map(p => UnresolvedPath.ResolvedPath(p.path)) ++
bspTransitiveCompileClasspath() ++
localCompileClasspath().map(p => UnresolvedPath.ResolvedPath(p.path))
}
/**
* The *input* classfiles/resources from this module, used during compilation,
* excluding upstream modules and third-party dependencies
*/
def localCompileClasspath: T[Agg[PathRef]] = Task {
compileResources() ++ unmanagedClasspath()
}
/**
* Resolved dependencies
*/
def resolvedIvyDeps: T[Agg[PathRef]] = Task {
millResolver().classpath(
Seq(
BoundDep(
coursierDependency.withConfiguration(cs.Configuration.provided),
force = false
),
BoundDep(coursierDependency, force = false)
),
artifactTypes = Some(artifactTypes()),
resolutionParamsMapOpt =
Some((_: ResolutionParams).withDefaultConfiguration(coursier.core.Configuration.compile))
)
}
def upstreamIvyAssemblyClasspath: T[Agg[PathRef]] = Task {
resolvedRunIvyDeps()
}
def upstreamLocalAssemblyClasspath: T[Agg[PathRef]] = Task {
transitiveLocalClasspath()
}
/**
* All upstream classfiles and resources necessary to build and executable
* assembly, but without this module's contribution
*/
def upstreamAssemblyClasspath: T[Agg[PathRef]] = Task {
resolvedRunIvyDeps() ++ transitiveLocalClasspath()
}
def resolvedRunIvyDeps: T[Agg[PathRef]] = Task {
millResolver().classpath(
Seq(
BoundDep(
coursierDependency.withConfiguration(cs.Configuration.runtime),
force = false
)
),
artifactTypes = Some(artifactTypes()),
resolutionParamsMapOpt =
Some((_: ResolutionParams).withDefaultConfiguration(cs.Configuration.runtime))
)
}
/**
* All classfiles and resources from upstream modules and dependencies
* necessary to run this module's code after compilation
*/
override def runClasspath: T[Seq[PathRef]] = Task {
super.runClasspath() ++
resolvedRunIvyDeps().toSeq ++
transitiveLocalClasspath() ++
localClasspath()
}
// bincompat stub
def manifest: T[JarManifest] = Task { manifest0() }
/**
* Build the assembly for upstream dependencies separate from the current
* classpath
*
* This should allow much faster assembly creation in the common case where
* upstream dependencies do not change
*
* This implementation is deprecated because of it's return value.
* Please use [[upstreamAssembly2]] instead.
*/
@deprecated("Use upstreamAssembly2 instead, which has a richer return value", "Mill 0.11.8")
def upstreamAssembly: T[PathRef] = Task {
Task.log.error(
s"upstreamAssembly target is deprecated and should no longer used." +
s" Please make sure to use upstreamAssembly2 instead."
)
upstreamAssembly2().pathRef
}
// Bincompat stub
def upstreamAssembly2: T[Assembly] = Task { upstreamAssembly2_0() }
// Bincompat stub
override def assembly: T[PathRef] = Task[PathRef] { assembly0() }
/**
* A jar containing only this module's resources and compiled classfiles,
* without those from upstream modules and dependencies
*/
def jar: T[PathRef] = Task {
Jvm.createJar(localClasspath().map(_.path).filter(os.exists), manifest())
}
/**
* Additional options to be used by the javadoc tool.
* You should not set the `-d` setting for specifying the target directory,
* as that is done in the [[docJar]] target.
*/
def javadocOptions: T[Seq[String]] = Task { Seq[String]() }
/**
* Directories to be processed by the API documentation tool.
*
* Typically, includes the source files to generate documentation from.
* @see [[docResources]]
*/
def docSources: T[Seq[PathRef]] = Task { allSources() }
/**
* Extra directories to be copied into the documentation.
*
* Typically, includes static files such as html and markdown, but depends
* on the doc tool that is actually used.
* @see [[docSources]]
*/
def docResources: T[Seq[PathRef]] = Task.Sources("docs")
/**
* Control whether `docJar`-target should use a file to pass command line arguments to the javadoc tool.
* Defaults to `true` on Windows.
* Beware: Using an args-file is probably not supported for very old javadoc versions.
*/
def docJarUseArgsFile: T[Boolean] = Task { scala.util.Properties.isWin }
/**
* The documentation jar, containing all the Javadoc/Scaladoc HTML files, for
* publishing to Maven Central
*/
def docJar: T[PathRef] = T[PathRef] {
val outDir = Task.dest
val javadocDir = outDir / "javadoc"
os.makeDir.all(javadocDir)
val files = Lib.findSourceFiles(docSources(), Seq("java"))
if (files.nonEmpty) {
val classPath = compileClasspath().iterator.map(_.path).filter(_.ext != "pom").toSeq
val cpOptions =
if (classPath.isEmpty) Seq()
else Seq(
"-classpath",
classPath.mkString(java.io.File.pathSeparator)
)
val options = javadocOptions() ++
Seq("-d", javadocDir.toString) ++
cpOptions ++
files.map(_.toString)
val cmdArgs =
if (docJarUseArgsFile()) {
val content = options.map(s =>
// make sure we properly mask backslashes (path separators on Windows)
s""""${s.replace("\\", "\\\\")}""""
).mkString(" ")
val argsFile = os.temp(
contents = content,
prefix = "javadoc-",
deleteOnExit = false,
dir = outDir
)
Task.log.debug(
s"Creating javadoc options file @${argsFile} ..."
)
Seq(s"@${argsFile}")
} else {
options
}
Task.log.info("options: " + cmdArgs)
os.call(
cmd = Seq(Jvm.jdkTool("javadoc")) ++ cmdArgs,
env = Map(),
cwd = Task.dest,
stdin = os.Inherit,
stdout = os.Inherit
)
}
Jvm.createJar(Agg(javadocDir))(outDir)
}
/**
* The source jar, containing only source code for publishing to Maven Central
*/
def sourceJar: T[PathRef] = Task {
Jvm.createJar(
(allSources() ++ resources() ++ compileResources()).map(_.path).filter(os.exists),
manifest()
)
}
/**
* Any command-line parameters you want to pass to the forked JVM under `run`,
* `test` or `repl`
*/
override def forkArgs: T[Seq[String]] = Task {
// overridden here for binary compatibility (0.11.x)
super.forkArgs()
}
/**
* Any environment variables you want to pass to the forked JVM under `run`,
* `test` or `repl`
*/
override def forkEnv: T[Map[String, String]] = Task {
// overridden here for binary compatibility (0.11.x)
super.forkEnv()
}
def launcher: T[PathRef] = Task { launcher0() }
/**
* Task that print the transitive dependency tree to STDOUT.
* NOTE: that when `whatDependsOn` is used with `inverse` it will just
* be ignored since when using `whatDependsOn` the tree _must_ be
* inversed to work, so this will always be set as true.
* @param inverse Invert the tree representation, so that the root is on the bottom.
* @param additionalDeps Additional dependency to be included into the tree.
* @param whatDependsOn possible list of modules to target in the tree in order to see
* where a dependency stems from.
*/
protected def printDepsTree(
inverse: Boolean,
additionalDeps: Task[Agg[BoundDep]],
whatDependsOn: List[JavaOrScalaModule]
): Task[Unit] =
Task.Anon {
val dependencies =
(additionalDeps() ++ Seq(BoundDep(coursierDependency, force = false))).iterator.to(Seq)
val resolution: Resolution = Lib.resolveDependenciesMetadataSafe(
allRepositories(),
dependencies,
Some(mapDependencies()),
customizer = resolutionCustomizer(),
coursierCacheCustomizer = coursierCacheCustomizer(),
resolutionParams = resolutionParams()
).getOrThrow
val roots = whatDependsOn match {
case List() =>
val mandatoryModules =
mandatoryIvyDeps().map(bindDependency()).iterator.map(_.dep.module).toSet
val (mandatory, main) = resolution.dependenciesOf(coursierDependency)
.partition(dep => mandatoryModules.contains(dep.module))
additionalDeps().iterator.toSeq.map(_.dep) ++ main ++ mandatory
case _ =>
// We don't really care what scalaVersions is set as here since the user
// will be passing in `_2.13` or `._3` anyways. Or it may even be a java
// dependency. Looking at the usage upstream, it seems that this is set if
// it can be or else defaults to "". Using it, I haven't been able to see
// any difference whether it's set, and by using "" it greatly simplifies
// it.
val matchers = whatDependsOn
.map(module => module.module(scalaVersion = ""))
.map(module => ModuleMatcher(module))
resolution.minDependencies
.filter(dep => matchers.exists(matcher => matcher.matches(dep.module))).toSeq
}
val tree = coursier.util.Print.dependencyTree(
resolution = resolution,
roots = roots,
printExclusions = false,
reverse = if (whatDependsOn.isEmpty) inverse else true
)
// Filter the output, so that the special organization and version used for Mill's own modules
// don't appear in the output. This only leaves the modules' name built from millModuleSegments.
val processedTree = tree
.replace(" mill-internal:", " ")
.replace(":0+mill-internal ", " ")
.replace(":0+mill-internal" + System.lineSeparator(), System.lineSeparator())
println(processedTree)
Result.Success(())
}
/**
* Command to print the transitive dependency tree to STDOUT.
*/
def ivyDepsTree(args: IvyDepsTreeArgs = IvyDepsTreeArgs()): Command[Unit] = {
val (invalidModules, validModules) =
args.whatDependsOn.map(ModuleParser.javaOrScalaModule(_)).partitionMap(identity)
if (invalidModules.isEmpty) {
(args.withCompile, args.withRuntime) match {
case (Flag(true), Flag(true)) =>
Task.Command {
printDepsTree(
args.inverse.value,
Task.Anon {
Agg(
coursierDependency.withConfiguration(cs.Configuration.provided),
coursierDependency.withConfiguration(cs.Configuration.runtime)
).map(BoundDep(_, force = false))
},
validModules
)()
}
case (Flag(true), Flag(false)) =>
Task.Command {
printDepsTree(
args.inverse.value,
Task.Anon {
Agg(BoundDep(
coursierDependency.withConfiguration(cs.Configuration.provided),
force = false
))
},
validModules
)()
}
case (Flag(false), Flag(true)) =>
Task.Command {
printDepsTree(
args.inverse.value,
Task.Anon {
Agg(BoundDep(
coursierDependency.withConfiguration(cs.Configuration.runtime),
force = false
))
},
validModules
)()
}
case _ =>
Task.Command {
printDepsTree(args.inverse.value, Task.Anon { Agg.empty[BoundDep] }, validModules)()
}
}
} else {
Task.Command {
val msg = invalidModules.mkString("\n")
Result.Failure[Unit](msg)
}
}
}
override def runUseArgsFile: T[Boolean] = Task {
// overridden here for binary compatibility (0.11.x)
super.runUseArgsFile()
}
override def runLocal(args: Task[Args] = Task.Anon(Args())): Command[Unit] = {
// overridden here for binary compatibility (0.11.x)
super.runLocal(args)
}
override def run(args: Task[Args] = Task.Anon(Args())): Command[Unit] = {
// overridden here for binary compatibility (0.11.x)
super.run(args)
}
@deprecated("Binary compat shim, use `.runner().run(..., background=true)`", "Mill 0.12.0")
override protected def doRunBackground(
taskDest: Path,
runClasspath: Seq[PathRef],
zwBackgroundWrapperClasspath: Agg[PathRef],
forkArgs: Seq[String],
forkEnv: Map[String, String],
finalMainClass: String,
forkWorkingDir: Path,
runUseArgsFile: Boolean,
backgroundOutputs: Option[Tuple2[ProcessOutput, ProcessOutput]]
)(args: String*): Ctx => Result[Unit] = {
// overridden here for binary compatibility (0.11.x)
super.doRunBackground(
taskDest,
runClasspath,
zwBackgroundWrapperClasspath,
forkArgs,
forkEnv,
finalMainClass,
forkWorkingDir,
runUseArgsFile,
backgroundOutputs
)(args: _*)
}
override def runBackgroundLogToConsole: Boolean = {
// overridden here for binary compatibility (0.11.x)
super.runBackgroundLogToConsole
}
/**
* Runs this module's code in a background process, until it dies or
* `runBackground` is used again. This lets you continue using Mill while
* the process is running in the background: editing files, compiling, and
* only re-starting the background process when you're ready.
*
* You can also use `-w foo.runBackground` to make Mill watch for changes
* and automatically recompile your code & restart the background process
* when ready. This is useful when working on long-running server processes
* that would otherwise run forever
*/
def runBackground(args: String*): Command[Unit] = {
val task = runBackgroundTask(finalMainClass, Task.Anon { Args(args) })
Task.Command { task() }
}
/**
* Same as `runBackground`, but lets you specify a main class to run
*/
override def runMainBackground(
@arg(positional = true) mainClass: String,
args: String*
): Command[Unit] = {
// overridden here for binary compatibility (0.11.x)
super.runMainBackground(mainClass, args: _*)
}
/**
* Same as `runLocal`, but lets you specify a main class to run
*/
override def runMainLocal(
@arg(positional = true) mainClass: String,
args: String*
): Command[Unit] = {
// overridden here for binary compatibility (0.11.x)
super.runMainLocal(mainClass, args: _*)
}
/**
* Same as `run`, but lets you specify a main class to run
*/
override def runMain(@arg(positional = true) mainClass: String, args: String*): Command[Unit] = {
// overridden here for binary compatibility (0.11.x)
super.runMain(mainClass, args: _*)
}
/**
* Override this to change the published artifact id.
* For example, by default a scala module foo.baz might be published as foo-baz_2.12 and a java module would be foo-baz.
* Setting this to baz would result in a scala artifact baz_2.12 or a java artifact baz.
*/
def artifactName: T[String] = artifactNameParts().mkString("-")
def artifactNameParts: T[Seq[String]] = millModuleSegments.parts
/**
* The exact id of the artifact to be published. You probably don't want to override this.
* If you want to customize the name of the artifact, override artifactName instead.
* If you want to customize the scala version in the artifact id, see ScalaModule.artifactScalaVersion
*/
def artifactId: T[String] = artifactName() + artifactSuffix()
/**
* The suffix appended to the artifact IDs during publishing
*/
def artifactSuffix: T[String] = platformSuffix()
override def forkWorkingDir: T[Path] = Task {
// overridden here for binary compatibility (0.11.x)
super.forkWorkingDir()
}
/**
* Files extensions that need to be managed by Zinc together with class files.
* This means, if zinc needs to remove a class file, it will also remove files
* which match the class file basename and a listed file extension.
*/
def zincAuxiliaryClassFileExtensions: T[Seq[String]] = Task { Seq.empty[String] }
/**
* @param all If `true` fetches also source dependencies
*/
override def prepareOffline(all: Flag): Command[Unit] = {
val tasks =
if (all.value) Seq(
Task.Anon {
millResolver().classpath(
Seq(
coursierDependency.withConfiguration(cs.Configuration.provided),
coursierDependency
),
sources = true,
resolutionParamsMapOpt =
Some(
(_: ResolutionParams).withDefaultConfiguration(coursier.core.Configuration.compile)
)
)
},
Task.Anon {
millResolver().classpath(
Seq(coursierDependency.withConfiguration(cs.Configuration.runtime)),
sources = true
)
}
)
else Seq()
Task.Command {
super.prepareOffline(all)()
resolvedIvyDeps()
zincWorker().prepareOffline(all)()
resolvedRunIvyDeps()
Task.sequence(tasks)()
()
}
}
@internal
override def bspBuildTarget: BspBuildTarget = super.bspBuildTarget.copy(
languageIds = Seq(BspModule.LanguageId.Java),
canCompile = true,
canRun = true
)
@internal
@deprecated("Use bspJvmBuildTargetTask instead", "0.12.3")
def bspJvmBuildTarget: JvmBuildTarget =
JvmBuildTarget(
javaHome = Option(System.getProperty("java.home")).map(p => BspUri(os.Path(p))),
javaVersion = Option(System.getProperty("java.version"))
)
@internal
def bspJvmBuildTargetTask: Task[JvmBuildTarget] = Task.Anon {
JvmBuildTarget(
javaHome = zincWorker()
.javaHome()
.map(p => BspUri(p.path))
.orElse(Option(System.getProperty("java.home")).map(p => BspUri(os.Path(p)))),
javaVersion = Option(System.getProperty("java.version"))
)
}
@internal
override def bspBuildTargetData: Task[Option[(String, AnyRef)]] = Task.Anon {
Some((JvmBuildTarget.dataKind, bspJvmBuildTargetTask()))
}
}
object JavaModule {
/**
* An in-memory [[coursier.Repository]] that exposes the passed projects
*
* Doesn't generate artifacts for these. These are assumed to be managed
* externally for now.
*
* @param projects
*/
final case class InternalRepo(projects: Seq[cs.Project])
extends cs.Repository {
private lazy val map = projects.map(proj => proj.moduleVersion -> proj).toMap
override def toString(): String =
pprint.apply(this).toString
def find[F[_]: Monad](
module: cs.Module,
version: String,
fetch: cs.Repository.Fetch[F]
): EitherT[F, String, (cs.ArtifactSource, cs.Project)] =
EitherT(
Monad[F].point {
map.get((module, version))
.map((this, _))
.toRight(s"Not an internal Mill module: ${module.repr}:$version")
}
)
def artifacts(
dependency: cs.Dependency,
project: cs.Project,
overrideClassifiers: Option[Seq[coursier.core.Classifier]]
): Seq[(coursier.core.Publication, coursier.util.Artifact)] =
// Mill modules' artifacts are handled by Mill itself
Nil
}
private[mill] def internalOrg = coursier.core.Organization("mill-internal")
private[mill] def internalVersion = "0+mill-internal"
}
/**
* A module that consists solely of dependency management
*
* To be used by other modules via `JavaModule#bomModuleDeps`
*/
trait BomModule extends JavaModule {
def compile: T[CompilationResult] = Task {
val sources = allSourceFiles()
if (sources.nonEmpty)
throw new Exception("A BomModule cannot have sources")
CompilationResult(Task.dest / "zinc", PathRef(Task.dest / "classes"))
}
def resources: T[Seq[PathRef]] = Task {
val value = super.resources()
if (value.nonEmpty)
throw new Exception("A BomModule cannot have resources")
Seq.empty[PathRef]
}
private def emptyJar: T[PathRef] = Task {
Jvm.createJar(Agg.empty[os.Path])
}
def jar: T[PathRef] = Task {
emptyJar()
}
def docJar: T[PathRef] = Task {
emptyJar()
}
def sourceJar: T[PathRef] = Task {
emptyJar()
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy