Skip to content

Instantly share code, notes, and snippets.

@jhinrichsen
Created April 12, 2018 12:24
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 jhinrichsen/0fc9f7b041f76d3b2b1c6635fc2d202b to your computer and use it in GitHub Desktop.
Save jhinrichsen/0fc9f7b041f76d3b2b1c6635fc2d202b to your computer and use it in GitHub Desktop.
Bazel: transform Maven wsimport tasks from POM into buildozer commands
// Needs to be executed in the root of the Maven project because files are
// looked up relative to module directory
package main
import (
"encoding/xml"
"flag"
"fmt"
"log"
"os"
"path/filepath"
"strings"
)
const (
Cmd = "buildozer"
)
// we cannot use a fixed struct because all kind of plugins are listed in a POM,
// each and every with its own set of child elements.
// Look mom - who says Go has no generics?
type Node struct {
XMLName xml.Name
Attrs []xml.Attr `xml:"-"`
Content []byte `xml:",innerxml"`
Nodes []Node `xml:",any"`
}
func (a *Node) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
a.Attrs = start.Attr
type node Node
return d.DecodeElement((*node)(a), &start)
}
// Jaxws mirrors a jaxws plugin execution
type Jaxws struct {
ID string
BindingDirectory string
BindingFile string
Goal string // wsimport or wsgen
PackageName string
Target string
WsdlLocation, WsdlFile string
Dirname string // pom.xml basename
}
// wsimport invocation via genrule
type Wsimport struct {
BindingFile string
Name string
Srcs []string
Outs string
PackageName string
Target string
WsdlFile string
}
// current POM setup is weird, wsdlLocation starts with classpath: and contains
// the relative path, wsdlFile is the filename only without any path. As a
// resolution, go for POM relative path and add wsdlLocation w/o classpath:
func (a Jaxws) Filepath() string {
return filepath.Join(a.Dirname,
strings.TrimPrefix(a.WsdlLocation, "classpath:"))
}
// convert a Java package name into a rule name
func asName(pkg string) string {
return strings.Replace(pkg, ".", "_", -1)
}
func die(err error) {
if err != nil {
log.Fatal(err)
}
}
// flatten string array into buildozer compatible commandline
func flatten(ss []string) string {
return strings.Join(ss, "\\ ")
}
func gen(a Wsimport) {
fmt.Println(fmt.Sprintf("%s 'new genrule %s' //:__pkg__", Cmd, a.Name))
// Do not flatten() so it becomes a list
fmt.Println(fmt.Sprintf("%s 'set srcs %s' %s", Cmd,
strings.Join(a.Srcs, " "), a.Name))
fmt.Println(fmt.Sprintf("%s 'set outs %s' %s", Cmd, a.Outs, a.Name))
fmt.Println(fmt.Sprintf("%s 'set cmd %s' %s", Cmd, genCmd(a), a.Name))
fmt.Println(fmt.Sprintf("%s 'set tools %s %s' %s", Cmd,
"@local_jdk//:jdk-default",
"@local_jdk//:wsimport",
a.Name))
}
func genCmd(a Wsimport) string {
// Escape buildozer commandline (one argument)
var ss []string
ss = append(ss, "$(location", "@local_jdk//:wsimport)")
if len(a.Target) > 0 {
ss = append(ss, "-target", a.Target)
}
if len(a.BindingFile) > 0 {
ss = append(ss, "-b", a.BindingFile)
}
if len(a.PackageName) > 0 {
ss = append(ss, "-p", a.PackageName)
}
ss = append(ss, "-extension", "-clientjar", "$@")
// If there is more then one input file, need to specify which one
if len(a.BindingFile) > 0 && len(a.WsdlFile) > 0 {
ss = append(ss, a.WsdlFile)
} else {
ss = append(ss, "$<")
}
return flatten(ss)
}
func isJaxwsPlugin(group, artifact string) bool {
return group == "org.jvnet.jax-ws-commons" &&
artifact == "jaxws-maven-plugin"
}
// Try to pinpoint exact input file location
func wsdlFile(j Jaxws) string {
var filename string
loc := strings.TrimPrefix(j.WsdlLocation, "classpath:")
if len(loc) > 0 && strings.Contains(loc, "/") {
filename = filepath.Join(j.Dirname,
"src/main/resources",
loc)
} else {
// plugin configuration is broke, let glob figure it out
spec := filepath.Join(j.Dirname,
"src/main/resources",
"**",
j.WsdlFile)
filename = fmt.Sprintf(`glob(["%s"])`, spec)
}
if len(loc) > 0 && strings.Contains(loc, "/") {
filename = filepath.Join(j.Dirname,
"src/main/resources",
loc)
} else {
// plugin configuration is broke, let glob figure it out
spec := filepath.Join(j.Dirname,
"src/main/resources",
"**",
j.WsdlFile)
filename = fmt.Sprintf(`glob(["%s"])`, spec)
}
return filename
}
// apply custom migration logic
func transform(j Jaxws) Wsimport {
var w Wsimport
if len(j.BindingFile) > 0 {
// Optional default for binding directory
// http://www.mojohaus.org/jaxws-maven-plugin/wsimport-mojo.html#bindingDirectory
d := j.BindingDirectory
if len(d) == 0 {
d = "src/jaxws"
}
w.BindingFile = filepath.Join(j.Dirname, d, j.BindingFile)
// Mark binding file as rule input
w.Srcs = append(w.Srcs, w.BindingFile)
}
// id is not unique across all plugin invocations, use Java package name
w.Name = asName(j.PackageName)
// Determine source WSDL file
w.WsdlFile = wsdlFile(j)
w.Srcs = append(w.Srcs, w.WsdlFile)
w.Outs = w.Name + ".jar"
w.PackageName = j.PackageName
w.Target = j.Target
return w
}
// SAX style
func walk(nodes []Node, f func(Node) bool) {
for _, n := range nodes {
if f(n) {
walk(n.Nodes, f)
}
}
}
func main() {
flag.Parse()
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage: %s <pom>...\n", os.Args[0])
flag.PrintDefaults()
}
if flag.NArg() == 0 {
flag.Usage()
os.Exit(2)
}
for _, pom := range flag.Args() {
log.Printf("processing %s\n", pom)
buf, js := os.Open(pom)
die(js)
dec := xml.NewDecoder(buf)
var n Node
js = dec.Decode(&n)
die(js)
var pluginGroup, pluginArtifact string
var jaxws []Jaxws
idx := -1
// parent POMs may contain <configuration/> elements
executions := false
walk([]Node{n}, func(n Node) bool {
e := n.XMLName.Local
switch e {
case "groupId":
pluginGroup = string(n.Content)
executions = false
case "artifactId":
pluginArtifact = string(n.Content)
executions = false
case "executions":
executions = true
}
if isJaxwsPlugin(pluginGroup, pluginArtifact) &&
executions {
switch e {
case "execution":
jaxws = append(jaxws, Jaxws{})
idx++
d, _ := filepath.Split(pom)
jaxws[idx].Dirname = d
log.Printf("created new execution "+
"#%d\n", idx)
case "bindingDirectory":
jaxws[idx].BindingDirectory = string(n.Content)
case "bindingFile":
jaxws[idx].BindingFile = string(n.Content)
case "goal":
jaxws[idx].Goal = string(n.Content)
case "id":
jaxws[idx].ID = string(n.Content)
case "wsdlLocation":
jaxws[idx].WsdlLocation = string(n.Content)
case "packageName":
jaxws[idx].PackageName = string(n.Content)
case "target":
jaxws[idx].Target = string(n.Content)
case "wsdlFile":
jaxws[idx].WsdlFile = string(n.Content)
default:
log.Printf("ignoring %s\n", e)
}
} else {
log.Printf("skipping %s:%s <%s/>, "+
"executions=%t\n",
pluginGroup, pluginArtifact, e,
executions)
}
return true
})
log.Printf("found %d executions\n", len(jaxws))
for _, j := range jaxws {
log.Printf("%+v\n", j)
if j.Goal == "wsimport" {
gen(transform(j))
} else {
log.Printf("skipping jaxws %+v\n", j)
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment