Last active
September 7, 2015 15:38
-
-
Save frgomes/ad5716e64da4108febf8 to your computer and use it in GitHub Desktop.
Scala - Converts a list of pom.xml files onto list of variables containing versions and dependencies
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
#-*- mode: scala; -*- | |
###################################################################### | |
# This Scala scripts extracts versions and dependencies from a list | |
# of pom.xml files passed as argument to the script. | |
###################################################################### | |
exec scala "$0" "$@" | |
!# | |
object Launcher { | |
def main(files: Array[String]) = { | |
println(Pom(files).toCode) | |
} | |
} | |
class ChildSelectable(ns: scala.xml.NodeSeq) { | |
def \* = ns flatMap { _ match { | |
case e: scala.xml.Elem => e.child | |
case _ => scala.xml.NodeSeq.Empty | |
} } | |
} | |
implicit def nodeSeqIsChildSelectable(xml: scala.xml.NodeSeq) = new ChildSelectable(xml) | |
case class GAV(g: String, opr: String, a: String, v: String, s: String) | |
case class Pom(files: Seq[String]) { | |
private val mprops = new scala.collection.mutable.HashMap[String, String] | |
private val mdeps = new scala.collection.mutable.HashMap[String, GAV] | |
private val malias = new scala.collection.mutable.HashMap[String, String] | |
private val pattern = """(.*)\$\{(.*)\}(.*)""".r | |
private val scalaVersionMajor = "_${scala.version.major}" | |
// Load properties | |
files.foreach { | |
file => | |
val pom = scala.xml.XML.loadFile(file) | |
mprops += "project.groupId" -> (pom \ "groupId").headOption.getOrElse((pom \ "parent" \ "groupId").head).text | |
mprops += "project.version" -> (pom \ "version").headOption.getOrElse((pom \ "parent" \ "version").head).text | |
(pom \ "properties" \*).filter(property => property.label != "#PCDATA").foreach { | |
property => | |
mprops += property.label -> property.text | |
} | |
} | |
// Resolve properties. | |
mprops.keys.map(k => mprops += (k -> resolve(mprops.get(k)))) | |
//TODO: Infer variables from dependencies. Refactor block below. | |
//TODO: Resolve variables inferred from dependencies. | |
// Load dependencies, resolving variables on the fly. Also defines aliases. | |
files.foreach { | |
file => | |
val pom = scala.xml.XML.loadFile(file) | |
(pom \ "dependencies" \ "dependency").foreach { | |
dependency => | |
val groupId = dependency \ "groupId" | |
val artifactId = dependency \ "artifactId" | |
val version = dependency \ "version" | |
val scope = dependency \ "scope" | |
if(groupId.length == 1 && artifactId.length == 1 && version.length == 1) { | |
val g = resolve(groupId.text) | |
val s = if(scope.headOption.isEmpty) "compile" else scope.head.text | |
val (opr, atmp, suffix) = artifact(artifactId.text, s) | |
val a = resolve(atmp) | |
val v = resolve(version.text) | |
val name = a + suffix | |
val normalized = normalize(a) + suffix | |
mdeps += name -> GAV(g, opr, a, v, s) | |
malias += normalized -> name | |
}}} | |
def lexical(ns: String): String = { | |
if(ns.startsWith("$")) | |
ns.replace("{","{`").replace("}","`}") | |
else | |
ns | |
} | |
def upperCaseFirst(s: String): String = | |
s.substring(0, 1).toUpperCase + s.substring(1).toLowerCase | |
def artifact(artifact: String, scope: String): (String, String, String) = { | |
val opr = if(artifact.indexOf(scalaVersionMajor) == -1) "%" else "%%" | |
val suffix = scope.toLowerCase match { | |
case "compile" => "" | |
case "test" => "T" | |
case "provided" => "P" | |
case "runtime" => "R" | |
case "system" => "S" | |
case _ => "_" | |
} | |
val a = artifact.replace(scalaVersionMajor, "") | |
(opr, a, suffix) | |
} | |
def normalize(s: String): String = | |
s.split("-") | |
.map(upperCaseFirst(_)) | |
.mkString.map( | |
ch => | |
if(java.lang.Character.isLetterOrDigit(ch)) ch else '_') | |
def embedded(s: String): Option[String] = { | |
val m = pattern.pattern.matcher(s) | |
if(m.find()) { | |
val prop = m.group(2) | |
val name = prop.substring(2, prop.length-1) | |
Some(name) | |
} else | |
None | |
} | |
def resolve(input: String): String = resolve(Option(input)) | |
def resolve(input: Option[String]): String = | |
input.getOrElse("") match { | |
case pattern(before, name, after) => | |
val resolved = mprops.get(name).map(value => resolve(value)).getOrElse("?"+name+"?") | |
before + resolved + after | |
// before + name + after | |
case _ => input.getOrElse("") | |
} | |
def listProps() = | |
mprops.keySet.toSeq.sorted.map { | |
case k => | |
val value = mprops(k) | |
s"""val `${k}` = s"${value}"""" } | |
def listLibraries() = | |
mdeps.keySet.toSeq.sorted.map { | |
case k => | |
val gav = mdeps(k) | |
s"""val `${k}` = s"${gav.g}" ${gav.opr} s"${gav.a}" % s"${gav.v}" % s"${gav.s}"""" } | |
def listAliases() = | |
malias.keySet.toSeq.sorted.map { | |
case k => | |
val value = malias(k) | |
s"""val ${k} = `${value}`""" } | |
def toSeq(): Seq[String] = | |
Seq("// versions") ++ listProps() ++ | |
Seq("// libraries") ++ listLibraries() ++ | |
Seq("// aliases") ++ listAliases() | |
def toCode(): String = toSeq().mkString("\n") | |
} | |
Launcher.main(args) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment