summaryrefslogtreecommitdiff
path: root/scalalib/src/mill/scalalib/GenIdeaImpl.scala
diff options
context:
space:
mode:
authorOlivier Mélois <baccata64@gmail.com>2018-05-31 02:22:40 +0100
committerLi Haoyi <haoyi.sg@gmail.com>2018-05-30 18:22:40 -0700
commit1b03026dd2009d4a6f3d25226b2f13bd5c42e8a4 (patch)
treeafab4b39e02f718e22b12f5b510e3c5b52b1ad99 /scalalib/src/mill/scalalib/GenIdeaImpl.scala
parent5766840e1978b5db7612020c64d2dd33967175d2 (diff)
downloadmill-1b03026dd2009d4a6f3d25226b2f13bd5c42e8a4.tar.gz
mill-1b03026dd2009d4a6f3d25226b2f13bd5c42e8a4.tar.bz2
mill-1b03026dd2009d4a6f3d25226b2f13bd5c42e8a4.zip
Improve Intellij Idea support (#351)
* Improve Intellij Idea support Improves the Intellij Idea support in various ways : * Cherrypicks the idea conf that needs deleting rather than deleting the whole .idea directory. That directory contains elements of configuration like VCS reference that were annoying to set again every time mill regenerated idea config. * Attempts to retrieve libraries that the build depends on by inspecting the classloader of the top module * Attempts at grouping jars and sources together in order to have both in the same idea files, which appears to give better jump to definition * Hacks the library names for the libraries the build depends on, in order to match Intellij's ammonite support and not show red to the user about the library that has successfuly been resolved. Also allows to jump to the library sources from the magic import. * Remove un-necessary filters * Avoid Agg throwing because of duplicated build libraries * Removing hardcoded version from SBT idea module names
Diffstat (limited to 'scalalib/src/mill/scalalib/GenIdeaImpl.scala')
-rw-r--r--scalalib/src/mill/scalalib/GenIdeaImpl.scala105
1 files changed, 87 insertions, 18 deletions
diff --git a/scalalib/src/mill/scalalib/GenIdeaImpl.scala b/scalalib/src/mill/scalalib/GenIdeaImpl.scala
index 763482c8..33437b12 100644
--- a/scalalib/src/mill/scalalib/GenIdeaImpl.scala
+++ b/scalalib/src/mill/scalalib/GenIdeaImpl.scala
@@ -1,13 +1,14 @@
package mill.scalalib
import ammonite.ops._
-import coursier.Repository
+import ammonite.runtime.SpecialClassLoader
+import coursier.{Cache, CoursierPaths, Repository}
import mill.define._
import mill.eval.{Evaluator, PathRef, Result}
-import mill.{T, scalalib}
import mill.util.Ctx.{Home, Log}
-import mill.util.{Loose, PrintLogger, Strict}
import mill.util.Strict.Agg
+import mill.util.{Loose, Strict}
+import mill.{T, scalalib}
import scala.util.Try
@@ -34,7 +35,8 @@ object GenIdeaImpl {
val jdkInfo = extractCurrentJdk(pwd / ".idea" / "misc.xml").getOrElse(("JDK_1_8", "1.8 (1)"))
- rm! pwd/".idea"
+ rm! pwd/".idea"/"libraries"
+ rm! pwd/".idea"/"scala_compiler.xml"
rm! pwd/".idea_modules"
@@ -81,6 +83,17 @@ object GenIdeaImpl {
res.items.toList.map(_.path)
}
+ val buildDepsPaths = Try(evaluator
+ .rootModule
+ .getClass
+ .getClassLoader
+ .asInstanceOf[SpecialClassLoader]
+ ).map {
+ _.allJars
+ .map(url => Path(url.getFile))
+ .filter(_.toIO.exists)
+ }.getOrElse(Seq())
+
val resolved = for((path, mod) <- modules) yield {
val scalaLibraryIvyDeps = mod match{
case x: ScalaModule => x.scalaLibraryIvyDeps
@@ -119,8 +132,8 @@ object GenIdeaImpl {
}
val moduleLabels = modules.map(_.swap).toMap
+ val allResolved = resolved.flatMap(_._2) ++ buildLibraryPaths ++ buildDepsPaths
- val allResolved = resolved.flatMap(_._2) ++ buildLibraryPaths
val commonPrefix =
if (allResolved.isEmpty) 0
else {
@@ -148,6 +161,54 @@ object GenIdeaImpl {
}
.toMap
+ sealed trait ResolvedLibrary { def path : Path }
+ case class CoursierResolved(path : Path, pom : Path, sources : Option[Path])
+ extends ResolvedLibrary
+ case class OtherResolved(path : Path) extends ResolvedLibrary
+
+ // Tries to group jars with their poms and sources.
+ def toResolvedJar(path : Path) : Option[ResolvedLibrary] = {
+ val inCoursierCache = path.startsWith(Path(CoursierPaths.cacheDirectory()))
+ val isSource = path.segments.last.endsWith("sources.jar")
+ val isPom = path.ext == "pom"
+ if (inCoursierCache && (isSource || isPom)) {
+ // Remove sources and pom as they'll be recovered from the jar path
+ None
+ } else if (inCoursierCache && path.ext == "jar") {
+ val withoutExt = path.segments.last.dropRight(path.ext.length + 1)
+ val pom = path / up / s"$withoutExt.pom"
+ val sources = Some(path / up / s"$withoutExt-sources.jar")
+ .filter(_.toIO.exists())
+ Some(CoursierResolved(path, pom, sources))
+ } else Some(OtherResolved(path))
+ }
+
+ // Hack so that Intellij does not complain about unresolved magic
+ // imports in build.sc when in fact they are resolved
+ def sbtLibraryNameFromPom(pom : Path) : String = {
+ val xml = scala.xml.XML.loadFile(pom.toIO)
+
+ val groupId = (xml \ "groupId").text
+ val artifactId = (xml \ "artifactId").text
+ val version = (xml \ "version").text
+
+ // The scala version here is non incidental
+ s"SBT: $groupId:$artifactId:$version:jar"
+ }
+
+ def libraryName(resolvedJar: ResolvedLibrary) : String = resolvedJar match {
+ case CoursierResolved(path, pom, _) if buildDepsPaths.contains(path) =>
+ sbtLibraryNameFromPom(pom)
+ case CoursierResolved(path, _, _) =>
+ pathToLibName(path)
+ case OtherResolved(path) =>
+ pathToLibName(path)
+ }
+
+ def resolvedLibraries(resolved : Seq[Path]) : Seq[ResolvedLibrary] = resolved
+ .map(toResolvedJar)
+ .collect { case Some(r) => r}
+
val compilerSettings = resolved
.foldLeft(Map[(Loose.Agg[Path], Seq[String]), Vector[JavaModule]]()) {
(r, q) =>
@@ -155,6 +216,9 @@ object GenIdeaImpl {
r + (key -> (r.getOrElse(key, Vector()) :+ q._3))
}
+ val allBuildLibraries : Set[ResolvedLibrary] =
+ resolvedLibraries(buildLibraryPaths ++ buildDepsPaths).toSet
+
val fixedFiles = Seq(
Tuple2(".idea"/"misc.xml", miscXmlTemplate(jdkInfo)),
Tuple2(".idea"/"scala_settings.xml", scalaSettingsTemplate()),
@@ -168,8 +232,8 @@ object GenIdeaImpl {
Tuple2(
".idea_modules"/"mill-build.iml",
rootXmlTemplate(
- for(path <- buildLibraryPaths)
- yield pathToLibName(path)
+ for(lib <- allBuildLibraries)
+ yield libraryName(lib)
)
),
Tuple2(
@@ -178,16 +242,15 @@ object GenIdeaImpl {
)
)
- val libraries = allResolved.map{path =>
+ val libraries = resolvedLibraries(allResolved).map{ resolved =>
+ import resolved.path
val url = "jar://" + path + "!/"
- val name = pathToLibName(path)
- Tuple2(".idea"/'libraries/s"$name.xml", libraryXmlTemplate(name, url))
- }
-
- val buildLibraries = buildLibraryPaths.map{path =>
- val url = "jar://" + path + "!/"
- val name = pathToLibName(path)
- Tuple2(".idea"/'libraries/s"$name.xml", libraryXmlTemplate(name, url))
+ val name = libraryName(resolved)
+ val sources = resolved match {
+ case CoursierResolved(_, _, s) => s.map(p => "jar://" + p + "!/")
+ case OtherResolved(_) => None
+ }
+ Tuple2(".idea"/'libraries/s"$name.xml", libraryXmlTemplate(name, url, sources))
}
val moduleFiles = resolved.map{ case (path, resolvedDeps, mod, _, _) =>
@@ -231,7 +294,7 @@ object GenIdeaImpl {
Tuple2(".idea_modules"/s"${moduleName(path)}.iml", elem)
}
- fixedFiles ++ libraries ++ moduleFiles ++ buildLibraries
+ fixedFiles ++ libraries ++ moduleFiles
}
def evalOrElse[T](evaluator: Evaluator[_], e: Task[T], default: => T): T = {
@@ -307,12 +370,18 @@ object GenIdeaImpl {
</component>
</module>
}
- def libraryXmlTemplate(name: String, url: String) = {
+ def libraryXmlTemplate(name: String, url: String, sources: Option[String]) = {
<component name="libraryTable">
<library name={name} type={if(name.contains("scala-library-")) "Scala" else null}>
<CLASSES>
<root url={url}/>
</CLASSES>
+ { if (sources.isDefined) {
+ <SOURCES>
+ <root url={sources.get}/>
+ </SOURCES>
+ }
+ }
</library>
</component>
}