Skip to content

Instantly share code, notes, and snippets.

@frgomes
Last active September 7, 2015 15:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save frgomes/ad5716e64da4108febf8 to your computer and use it in GitHub Desktop.
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
#!/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