summaryrefslogtreecommitdiff
path: root/scalalib/src/mill/scalalib/GenIdeaImpl.scala
diff options
context:
space:
mode:
authorLi Haoyi <haoyi.sg@gmail.com>2018-04-07 19:58:57 -0700
committerLi Haoyi <haoyi.sg@gmail.com>2018-04-07 19:58:57 -0700
commitb5350ae72c33d937aaa1953e2dfba3c0d6a88168 (patch)
treec40b2f0d7f23960045337713043a6040b5ff85ce /scalalib/src/mill/scalalib/GenIdeaImpl.scala
parent2d72abe64de8dbd98cabc688ce4bb57f733a1d12 (diff)
downloadmill-b5350ae72c33d937aaa1953e2dfba3c0d6a88168.tar.gz
mill-b5350ae72c33d937aaa1953e2dfba3c0d6a88168.tar.bz2
mill-b5350ae72c33d937aaa1953e2dfba3c0d6a88168.zip
update travis mill version
Diffstat (limited to 'scalalib/src/mill/scalalib/GenIdeaImpl.scala')
-rw-r--r--scalalib/src/mill/scalalib/GenIdeaImpl.scala389
1 files changed, 389 insertions, 0 deletions
diff --git a/scalalib/src/mill/scalalib/GenIdeaImpl.scala b/scalalib/src/mill/scalalib/GenIdeaImpl.scala
new file mode 100644
index 00000000..3e90f269
--- /dev/null
+++ b/scalalib/src/mill/scalalib/GenIdeaImpl.scala
@@ -0,0 +1,389 @@
+package mill.scalalib
+
+import ammonite.ops._
+import coursier.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 scala.util.Try
+
+
+object GenIdea extends ExternalModule {
+
+ def idea(ev: Evaluator[Any]) = T.command{
+ mill.scalalib.GenIdeaImpl(
+ implicitly,
+ ev.rootModule,
+ ev.rootModule.millDiscover
+ )
+ }
+
+ implicit def millScoptEvaluatorReads[T] = new mill.main.EvaluatorScopt[T]()
+ lazy val millDiscover = Discover[this.type]
+}
+object GenIdeaImpl {
+
+ def apply(ctx: Log with Home,
+ rootModule: BaseModule,
+ discover: Discover[_]): Unit = {
+ val pp = new scala.xml.PrettyPrinter(999, 4)
+
+ val jdkInfo = extractCurrentJdk(pwd / ".idea" / "misc.xml").getOrElse(("JDK_1_8", "1.8 (1)"))
+
+ rm! pwd/".idea"
+ rm! pwd/".idea_modules"
+
+
+ val evaluator = new Evaluator(ctx.home, pwd / 'out, pwd / 'out, rootModule, ctx.log)
+
+ for((relPath, xml) <- xmlFileLayout(evaluator, rootModule, jdkInfo)){
+ write.over(pwd/relPath, pp.format(xml))
+ }
+ }
+
+ def extractCurrentJdk(ideaPath: Path): Option[(String,String)] = {
+ import scala.xml.XML
+ Try {
+ val xml = XML.loadFile(ideaPath.toString)
+ (xml \\ "component")
+ .filter(x => x.attribute("project-jdk-type").map(_.text).contains("JavaSDK"))
+ .map { n => (n.attribute("languageLevel"), n.attribute("project-jdk-name")) }
+ .collectFirst{ case (Some(lang), Some(jdk)) => (lang.text, jdk.text) }
+ }.getOrElse(None)
+ }
+
+ def xmlFileLayout[T](evaluator: Evaluator[T],
+ rootModule: mill.Module,
+ jdkInfo: (String,String),
+ fetchMillModules: Boolean = true): Seq[(RelPath, scala.xml.Node)] = {
+
+ val modules = rootModule.millInternal.segmentsToModules.values
+ .collect{ case x: scalalib.JavaModule => (x.millModuleSegments, x)}
+ .toSeq
+
+ val buildLibraryPaths =
+ if (!fetchMillModules) Nil
+ else sys.props.get("MILL_BUILD_LIBRARIES") match {
+ case Some(found) => found.split(',').map(Path(_)).distinct.toList
+ case None =>
+ val repos = modules.foldLeft(Set.empty[Repository]) { _ ++ _._2.repositories }
+ val artifactNames = Seq("moduledefs", "core", "scalalib", "scalajslib")
+ val Result.Success(res) = scalalib.Lib.resolveDependencies(
+ repos.toList,
+ Lib.depToDependency(_, "2.12.4", ""),
+ for(name <- artifactNames)
+ yield ivy"com.lihaoyi::mill-$name:${sys.props("MILL_VERSION")}"
+ )
+ res.items.toList.map(_.path)
+ }
+
+ val resolved = for((path, mod) <- modules) yield {
+ val scalaLibraryIvyDeps = mod match{
+ case x: ScalaModule => x.scalaLibraryIvyDeps
+ case _ => T.task{Nil}
+ }
+ val allIvyDeps = T.task{mod.transitiveIvyDeps() ++ scalaLibraryIvyDeps() ++ mod.compileIvyDeps()}
+ val externalDependencies = T.task{
+ mod.resolveDeps(allIvyDeps)() ++
+ Task.traverse(mod.transitiveModuleDeps)(_.unmanagedClasspath)().flatten
+ }
+
+ val externalSources = T.task{
+ mod.resolveDeps(allIvyDeps, sources = true)()
+ }
+
+ val (scalacPluginsIvyDeps, scalacOptions) = mod match{
+ case mod: ScalaModule => T.task{mod.scalacPluginIvyDeps()} -> T.task{mod.scalacOptions()}
+ case _ => T.task(Loose.Agg[Dep]()) -> T.task(Seq())
+ }
+ val scalacPluginDependencies = T.task{
+ mod.resolveDeps(scalacPluginsIvyDeps)()
+ }
+
+ val resolvedCp: Loose.Agg[PathRef] = evalOrElse(evaluator, externalDependencies, Loose.Agg.empty)
+ val resolvedSrcs: Loose.Agg[PathRef] = evalOrElse(evaluator, externalSources, Loose.Agg.empty)
+ val resolvedSp: Loose.Agg[PathRef] = evalOrElse(evaluator, scalacPluginDependencies, Loose.Agg.empty)
+ val scalacOpts: Seq[String] = evalOrElse(evaluator, scalacOptions, Seq())
+
+ (
+ path,
+ resolvedCp.map(_.path).filter(_.ext == "jar") ++ resolvedSrcs.map(_.path),
+ mod,
+ resolvedSp.map(_.path).filter(_.ext == "jar"),
+ scalacOpts
+ )
+ }
+ val moduleLabels = modules.map(_.swap).toMap
+
+
+ val allResolved = resolved.flatMap(_._2) ++ buildLibraryPaths
+ val commonPrefix =
+ if (allResolved.isEmpty) 0
+ else {
+ val minResolvedLength = allResolved.map(_.segments.length).min
+ allResolved.map(_.segments.take(minResolvedLength))
+ .transpose
+ .takeWhile(_.distinct.length == 1)
+ .length
+ }
+
+ // only resort to full long path names if the jar name is a duplicate
+ val pathShortLibNameDuplicate = allResolved
+ .distinct
+ .map{p => p.segments.last -> p}
+ .groupBy(_._1)
+ .filter(_._2.size > 1)
+ .keySet
+
+ val pathToLibName = allResolved
+ .map{p =>
+ if (pathShortLibNameDuplicate(p.segments.last))
+ (p, p.segments.drop(commonPrefix).mkString("_"))
+ else
+ (p, p.segments.last)
+ }
+ .toMap
+
+ val compilerSettings = resolved
+ .foldLeft(Map[(Loose.Agg[Path], Seq[String]), Vector[JavaModule]]()) {
+ (r, q) =>
+ val key = (q._4, q._5)
+ r + (key -> (r.getOrElse(key, Vector()) :+ q._3))
+ }
+
+ val fixedFiles = Seq(
+ Tuple2(".idea"/"misc.xml", miscXmlTemplate(jdkInfo)),
+ Tuple2(".idea"/"scala_settings.xml", scalaSettingsTemplate()),
+ Tuple2(
+ ".idea"/"modules.xml",
+ allModulesXmlTemplate(
+ for((path, mod) <- modules)
+ yield moduleName(path)
+ )
+ ),
+ Tuple2(
+ ".idea_modules"/"root.iml",
+ rootXmlTemplate(
+ for(path <- buildLibraryPaths)
+ yield pathToLibName(path)
+ )
+ ),
+ Tuple2(
+ ".idea"/"scala_compiler.xml",
+ scalaCompilerTemplate(compilerSettings)
+ )
+ )
+
+ val libraries = allResolved.map{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 moduleFiles = resolved.map{ case (path, resolvedDeps, mod, _, _) =>
+ val Seq(
+ resourcesPathRefs: Seq[PathRef],
+ sourcesPathRef: Seq[PathRef],
+ generatedSourcePathRefs: Seq[PathRef],
+ allSourcesPathRefs: Seq[PathRef]
+ ) = evaluator.evaluate(Agg(mod.resources, mod.sources, mod.generatedSources, mod.allSources)).values
+
+ val generatedSourcePaths = generatedSourcePathRefs.map(_.path)
+ val normalSourcePaths = (allSourcesPathRefs.map(_.path).toSet -- generatedSourcePaths.toSet).toSeq
+
+ val paths = Evaluator.resolveDestPaths(
+ evaluator.outPath,
+ mod.compile.ctx.segments
+ )
+ val scalaVersionOpt = mod match {
+ case x: ScalaModule => Some(evaluator.evaluate(Agg(x.scalaVersion)).values.head.asInstanceOf[String])
+ case _ => None
+ }
+ val generatedSourceOutPath = Evaluator.resolveDestPaths(
+ evaluator.outPath,
+ mod.generatedSources.ctx.segments
+ )
+
+ val elem = moduleXmlTemplate(
+ mod.millModuleBasePath.value,
+ scalaVersionOpt,
+ Strict.Agg.from(resourcesPathRefs.map(_.path)),
+ Strict.Agg.from(normalSourcePaths),
+ Strict.Agg.from(generatedSourcePaths),
+ paths.out,
+ generatedSourceOutPath.dest,
+ Strict.Agg.from(resolvedDeps.map(pathToLibName)),
+ Strict.Agg.from(mod.moduleDeps.map{ m => moduleName(moduleLabels(m))}.distinct)
+ )
+ Tuple2(".idea_modules"/s"${moduleName(path)}.iml", elem)
+ }
+
+ fixedFiles ++ libraries ++ moduleFiles ++ buildLibraries
+ }
+
+ def evalOrElse[T](evaluator: Evaluator[_], e: Task[T], default: => T): T = {
+ evaluator.evaluate(Agg(e)).values match {
+ case Seq() => default
+ case Seq(e: T) => e
+ }
+ }
+
+ def relify(p: Path) = {
+ val r = p.relativeTo(pwd/".idea_modules")
+ (Seq.fill(r.ups)("..") ++ r.segments).mkString("/")
+ }
+
+ def moduleName(p: Segments) = p.value.foldLeft(StringBuilder.newBuilder) {
+ case (sb, Segment.Label(s)) if sb.isEmpty => sb.append(s)
+ case (sb, Segment.Cross(s)) if sb.isEmpty => sb.append(s.mkString("-"))
+ case (sb, Segment.Label(s)) => sb.append(".").append(s)
+ case (sb, Segment.Cross(s)) => sb.append("-").append(s.mkString("-"))
+ }.mkString.toLowerCase()
+
+ def scalaSettingsTemplate() = {
+
+ <project version="4">
+ <component name="ScalaProjectSettings">
+ <option name="scFileMode" value="Ammonite" />
+ </component>
+ </project>
+ }
+ def miscXmlTemplate(jdkInfo: (String,String)) = {
+ <project version="4">
+ <component name="ProjectRootManager" version="2" languageLevel={jdkInfo._1} project-jdk-name={jdkInfo._2} project-jdk-type="JavaSDK">
+ <output url="file://$PROJECT_DIR$/target/idea_output"/>
+ </component>
+ </project>
+ }
+
+ def allModulesXmlTemplate(selectors: Seq[String]) = {
+ <project version="4">
+ <component name="ProjectModuleManager">
+ <modules>
+ <module fileurl="file://$PROJECT_DIR$/.idea_modules/root.iml" filepath="$PROJECT_DIR$/.idea_modules/root.iml" />
+ {
+ for(selector <- selectors)
+ yield {
+ val filepath = "$PROJECT_DIR$/.idea_modules/" + selector + ".iml"
+ val fileurl = "file://" + filepath
+ <module fileurl={fileurl} filepath={filepath} />
+ }
+ }
+ </modules>
+ </component>
+ </project>
+ }
+ def rootXmlTemplate(libNames: Strict.Agg[String]) = {
+ <module type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager">
+ <output url="file://$MODULE_DIR$/../out"/>
+ <content url="file://$MODULE_DIR$/..">
+ <excludeFolder url="file://$MODULE_DIR$/../project" />
+ <excludeFolder url="file://$MODULE_DIR$/../target" />
+ </content>
+ <exclude-output/>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ {
+ for(name <- libNames.toSeq.sorted)
+ yield <orderEntry type="library" name={name} level="project" />
+ }
+ </component>
+ </module>
+ }
+ def libraryXmlTemplate(name: String, url: String) = {
+ <component name="libraryTable">
+ <library name={name} type={if(name.contains("scala-library-")) "Scala" else null}>
+ <CLASSES>
+ <root url={url}/>
+ </CLASSES>
+ </library>
+ </component>
+ }
+ def moduleXmlTemplate(basePath: Path,
+ scalaVersionOpt: Option[String],
+ resourcePaths: Strict.Agg[Path],
+ normalSourcePaths: Strict.Agg[Path],
+ generatedSourcePaths: Strict.Agg[Path],
+ compileOutputPath: Path,
+ generatedSourceOutputPath: Path,
+ libNames: Strict.Agg[String],
+ depNames: Strict.Agg[String]) = {
+ <module type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager">
+ <output url={"file://$MODULE_DIR$/" + relify(compileOutputPath) + "/dest/classes"} />
+ <exclude-output />
+ <content url={"file://$MODULE_DIR$/" + relify(generatedSourceOutputPath)} />
+ <content url={"file://$MODULE_DIR$/" + relify(basePath)}>
+ {
+ for (normalSourcePath <- normalSourcePaths.toSeq.sorted)
+ yield
+ <sourceFolder url={"file://$MODULE_DIR$/" + relify(normalSourcePath)} isTestSource="false" />
+ }
+ {
+ for (generatedSourcePath <- generatedSourcePaths.toSeq.sorted)
+ yield
+ <sourceFolder url={"file://$MODULE_DIR$/" + relify(generatedSourcePath)} isTestSource="false" generated="true" />
+ }
+ {
+ for (resourcePath <- resourcePaths.toSeq.sorted)
+ yield
+ <sourceFolder url={"file://$MODULE_DIR$/" + relify(resourcePath)} isTestSource="false" type="java-resource" />
+ }
+ <excludeFolder url={"file://$MODULE_DIR$/" + relify(basePath) + "/target"} />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ {
+ for(scalaVersion <- scalaVersionOpt.toSeq)
+ yield <orderEntry type="library" name={s"scala-sdk-$scalaVersion"} level="application" />
+ }
+
+ {
+ for(name <- libNames.toSeq.sorted)
+ yield <orderEntry type="library" name={name} level="project" />
+
+ }
+ {
+ for(depName <- depNames.toSeq.sorted)
+ yield <orderEntry type="module" module-name={depName} exported="" />
+ }
+ </component>
+ </module>
+ }
+ def scalaCompilerTemplate(settings: Map[(Loose.Agg[Path], Seq[String]), Seq[JavaModule]]) = {
+
+ <project version="4">
+ <component name="ScalaCompilerConfiguration">
+ {
+ for((((plugins, params), mods), i) <- settings.toSeq.zip(1 to settings.size))
+ yield
+ <profile name={s"mill $i"} modules={mods.map(m => moduleName(m.millModuleSegments)).mkString(",")}>
+ <parameters>
+ {
+ for(param <- params)
+ yield <parameter value={param} />
+ }
+ </parameters>
+ <plugins>
+ {
+ for(plugin <- plugins.toSeq)
+ yield <plugin path={plugin.toString} />
+ }
+ </plugins>
+ </profile>
+ }
+ </component>
+ </project>
+ }
+}