| /* |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, |
| software distributed under the License is distributed on an |
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| KIND, either express or implied. See the License for the |
| specific language governing permissions and limitations |
| under the License. |
| */ |
| |
| // Code generator to generate a thin Go wrapper API around the C proton API. |
| // |
| |
| package main |
| |
| import ( |
| "flag" |
| "fmt" |
| "io" |
| "io/ioutil" |
| "os" |
| "os/exec" |
| "path" |
| "regexp" |
| "strings" |
| "text/template" |
| ) |
| |
| var includeProton = "../../include/proton" |
| var outpath = "src/qpid.apache.org/proton/wrappers_gen.go" |
| |
| func main() { |
| flag.Parse() |
| out, err := os.Create(outpath) |
| panicIf(err) |
| defer out.Close() |
| |
| apis := []string{"session", "link", "delivery", "disposition", "condition", "terminus", "connection", "transport"} |
| fmt.Fprintln(out, copyright) |
| fmt.Fprint(out, ` |
| package proton |
| |
| import ( |
| "time" |
| "unsafe" |
| ) |
| |
| // #include <proton/types.h> |
| // #include <proton/error.h> |
| // #include <proton/condition.h> |
| // #include <proton/event.h> |
| // #include <stdlib.h> |
| `) |
| for _, api := range apis { |
| fmt.Fprintf(out, "// #include <proton/%s.h>\n", api) |
| } |
| fmt.Fprintln(out, `import "C"`) |
| |
| event(out) |
| |
| for _, api := range apis { |
| fmt.Fprintf(out, "// Wrappers for declarations in %s.h\n\n", api) |
| header := readHeader(api) |
| enums := findEnums(header) |
| for _, e := range enums { |
| genEnum(out, e.Name, e.Values) |
| } |
| apiWrapFns(api, header, out) |
| } |
| out.Close() |
| |
| // Run gofmt. |
| cmd := exec.Command("gofmt", "-w", outpath) |
| cmd.Stdout = os.Stdout |
| cmd.Stderr = os.Stderr |
| err = cmd.Run() |
| if err != nil { |
| fmt.Fprintf(os.Stderr, "gofmt: %s", err) |
| os.Exit(1) |
| } |
| } |
| |
| func mixedCase(s string) string { |
| result := "" |
| for _, w := range strings.Split(s, "_") { |
| if w != "" { |
| result = result + strings.ToUpper(w[0:1]) + strings.ToLower(w[1:]) |
| } |
| } |
| return result |
| } |
| |
| func mixedCaseTrim(s, prefix string) string { |
| return mixedCase(strings.TrimPrefix(s, prefix)) |
| } |
| |
| var templateFuncs = template.FuncMap{"mixedCase": mixedCase, "mixedCaseTrim": mixedCaseTrim} |
| |
| func doTemplate(out io.Writer, data interface{}, tmpl string) { |
| panicIf(template.Must(template.New("").Funcs(templateFuncs).Parse(tmpl)).Execute(out, data)) |
| } |
| |
| type enumType struct { |
| Name string |
| Values []string |
| } |
| |
| // Find enums in a header file return map of enum name to values. |
| func findEnums(header string) (enums []enumType) { |
| for _, enum := range enumDefRe.FindAllStringSubmatch(header, -1) { |
| enums = append(enums, enumType{enum[2], enumValRe.FindAllString(enum[1], -1)}) |
| } |
| return enums |
| } |
| |
| func genEnum(out io.Writer, name string, values []string) { |
| doTemplate(out, []interface{}{name, values}, ` |
| {{$enumName := index . 0}}{{$values := index . 1}} |
| type {{mixedCase $enumName}} C.pn_{{$enumName}}_t |
| const ({{range $values}} |
| {{mixedCaseTrim . "PN_"}} {{mixedCase $enumName}} = C.{{.}} {{end}} |
| ) |
| |
| func (e {{mixedCase $enumName}}) String() string { |
| switch e { |
| {{range $values}} |
| case C.{{.}}: return "{{mixedCaseTrim . "PN_"}}" {{end}} |
| } |
| return "unknown" |
| } |
| `) |
| } |
| |
| var ( |
| reSpace = regexp.MustCompile("\\s+") |
| ) |
| |
| func panicIf(err error) { |
| if err != nil { |
| panic(err) |
| } |
| } |
| |
| func readHeader(name string) string { |
| file, err := os.Open(path.Join(includeProton, name+".h")) |
| panicIf(err) |
| defer file.Close() |
| s, err := ioutil.ReadAll(file) |
| panicIf(err) |
| return string(s) |
| } |
| |
| var copyright string = `/* |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, |
| software distributed under the License is distributed on an |
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| KIND, either express or implied. See the License for the |
| specific language governing permissions and limitations |
| under the License. |
| */ |
| |
| // |
| // NOTE: DO NOT EDIT. This file was generated by genwrap.go from the proton header files. |
| // Update the generator and re-run if you need to modify this code. |
| // |
| ` |
| |
| type eventType struct { |
| // C, function and interface names for the event |
| Name, Cname, Fname, Iname string |
| } |
| |
| func newEventType(cName string) eventType { |
| var etype eventType |
| etype.Cname = cName |
| etype.Name = mixedCaseTrim(cName, "PN_") |
| etype.Fname = "On" + etype.Name |
| etype.Iname = etype.Fname + "Interface" |
| return etype |
| } |
| |
| var ( |
| enumDefRe = regexp.MustCompile("typedef enum {([^}]*)} pn_([a-z_]+)_t;") |
| enumValRe = regexp.MustCompile("PN_[A-Z_]+") |
| skipEventRe = regexp.MustCompile("EVENT_NONE|REACTOR|SELECTABLE|TIMER") |
| skipFnRe = regexp.MustCompile("attach|context|class|collect|link_recv|link_send|transport_.*logf$|transport_.*trace|transport_head|transport_push") |
| ) |
| |
| // Generate event wrappers. |
| func event(out io.Writer) { |
| event_h := readHeader("event") |
| |
| // Event is implented by hand in wrappers.go |
| |
| // Get all the pn_event_type_t enum values |
| var etypes []eventType |
| enums := findEnums(event_h) |
| for _, e := range enums[0].Values { |
| if skipEventRe.FindStringSubmatch(e) == nil { |
| etypes = append(etypes, newEventType(e)) |
| } |
| } |
| |
| doTemplate(out, etypes, ` |
| type EventType int |
| const ({{range .}} |
| E{{.Name}} EventType = C.{{.Cname}}{{end}} |
| ) |
| `) |
| |
| doTemplate(out, etypes, ` |
| func (e EventType) String() string { |
| switch e { |
| {{range .}} |
| case C.{{.Cname}}: return "{{.Name}}"{{end}} |
| } |
| return "Unknown" |
| } |
| `) |
| } |
| |
| type genType struct { |
| Ctype, Gotype string |
| ToGo func(value string) string |
| ToC func(value string) string |
| Assign func(value string) string |
| } |
| |
| func (g genType) printBody(out io.Writer, value string) { |
| if g.Gotype != "" { |
| fmt.Fprintf(out, "return %s", g.ToGo(value)) |
| } else { |
| fmt.Fprintf(out, "%s", value) |
| } |
| } |
| |
| func (g genType) goLiteral(value string) string { |
| return fmt.Sprintf("%s{%s}", g.Gotype, value) |
| } |
| |
| func (g genType) goConvert(value string) string { |
| switch g.Gotype { |
| case "string": |
| return fmt.Sprintf("C.GoString(%s)", value) |
| case "Event": |
| return fmt.Sprintf("makeEvent(%s)", value) |
| default: |
| return fmt.Sprintf("%s(%s)", g.Gotype, value) |
| } |
| } |
| |
| var notStruct = map[string]bool{ |
| "EventType": true, |
| "SndSettleMode": true, |
| "RcvSettleMode": true, |
| "TerminusType": true, |
| "State": true, |
| "Durability": true, |
| "ExpiryPolicy": true, |
| "DistributionMode": true, |
| } |
| |
| func mapType(ctype string) (g genType) { |
| g.Ctype = "C." + strings.Trim(ctype, " \n") |
| |
| switch g.Ctype { |
| case "C.void": |
| g.Gotype = "" |
| case "C.size_t": |
| g.Gotype = "uint" |
| case "C.int": |
| g.Gotype = "int" |
| case "C.void *": |
| g.Gotype = "unsafe.Pointer" |
| g.Ctype = "unsafe.Pointer" |
| case "C.bool": |
| g.Gotype = "bool" |
| case "C.ssize_t": |
| g.Gotype = "int" |
| case "C.uint64_t": |
| g.Gotype = "uint64" |
| case "C.uint32_t": |
| g.Gotype = "uint16" |
| case "C.uint16_t": |
| g.Gotype = "uint32" |
| case "C.const char *": |
| fallthrough |
| case "C.char *": |
| g.Gotype = "string" |
| g.Ctype = "C.CString" |
| g.ToC = func(v string) string { return fmt.Sprintf("%sC", v) } |
| g.Assign = func(v string) string { |
| return fmt.Sprintf("%sC := C.CString(%s)\n defer C.free(unsafe.Pointer(%sC))\n", v, v, v) |
| } |
| case "C.pn_seconds_t": |
| g.Gotype = "time.Duration" |
| g.ToGo = func(v string) string { return fmt.Sprintf("(time.Duration(%s) * time.Second)", v) } |
| case "C.pn_millis_t": |
| g.Gotype = "time.Duration" |
| g.ToGo = func(v string) string { return fmt.Sprintf("(time.Duration(%s) * time.Millisecond)", v) } |
| case "C.pn_timestamp_t": |
| g.Gotype = "time.Time" |
| g.ToC = func(v string) string { return fmt.Sprintf("pnTime(%s)", v) } |
| g.ToGo = func(v string) string { return fmt.Sprintf("goTime(%s)", v) } |
| case "C.pn_error_t *": |
| g.Gotype = "error" |
| g.ToGo = func(v string) string { return fmt.Sprintf("PnError(%s)", v) } |
| default: |
| pnId := regexp.MustCompile(" *pn_([a-z_]+)_t *\\*? *") |
| match := pnId.FindStringSubmatch(g.Ctype) |
| if match == nil { |
| panic(fmt.Errorf("unknown C type %#v", g.Ctype)) |
| } |
| g.Gotype = mixedCase(match[1]) |
| if !notStruct[g.Gotype] { |
| g.ToGo = g.goLiteral |
| g.ToC = func(v string) string { return v + ".pn" } |
| } |
| } |
| if g.ToGo == nil { |
| g.ToGo = g.goConvert // Use conversion by default. |
| } |
| if g.ToC == nil { |
| g.ToC = func(v string) string { return fmt.Sprintf("%s(%s)", g.Ctype, v) } |
| } |
| return |
| } |
| |
| type genArg struct { |
| Name string |
| genType |
| } |
| |
| var typeNameRe = regexp.MustCompile("^(.*( |\\*))([^ *]+)$") |
| |
| func splitArgs(argstr string) []genArg { |
| argstr = strings.Trim(argstr, " \n") |
| if argstr == "" { |
| return []genArg{} |
| } |
| args := make([]genArg, 0) |
| for _, item := range strings.Split(argstr, ",") { |
| item = strings.Trim(item, " \n") |
| typeName := typeNameRe.FindStringSubmatch(item) |
| if typeName == nil { |
| panic(fmt.Errorf("Can't split argument type/name %#v", item)) |
| } |
| cType := strings.Trim(typeName[1], " \n") |
| name := strings.Trim(typeName[3], " \n") |
| if name == "type" { |
| name = "type_" |
| } |
| args = append(args, genArg{name, mapType(cType)}) |
| } |
| return args |
| } |
| |
| func goArgs(args []genArg) string { |
| l := "" |
| for i, arg := range args { |
| if i != 0 { |
| l += ", " |
| } |
| l += arg.Name + " " + arg.Gotype |
| } |
| return l |
| } |
| |
| func cArgs(args []genArg) string { |
| l := "" |
| for _, arg := range args { |
| l += fmt.Sprintf(", %s", arg.ToC(arg.Name)) |
| } |
| return l |
| } |
| |
| func cAssigns(args []genArg) string { |
| l := "\n" |
| for _, arg := range args { |
| if arg.Assign != nil { |
| l += fmt.Sprintf("%s\n", arg.Assign(arg.Name)) |
| } |
| } |
| return l |
| } |
| |
| // Return the go name of the function or "" to skip the function. |
| func goFnName(api, fname string) string { |
| // Skip class, context and attachment functions. |
| if skipFnRe.FindStringSubmatch(api+"_"+fname) != nil { |
| return "" |
| } |
| switch api + "." + fname { |
| case "link.get_drain": |
| return "IsDrain" |
| default: |
| return mixedCaseTrim(fname, "get_") |
| } |
| } |
| |
| func apiWrapFns(api, header string, out io.Writer) { |
| fmt.Fprintf(out, "type %s struct{pn *C.pn_%s_t}\n", mixedCase(api), api) |
| fmt.Fprintf(out, "func (%c %s) IsNil() bool { return %c.pn == nil }\n", api[0], mixedCase(api), api[0]) |
| fmt.Fprintf(out, "func (%c %s) CPtr() unsafe.Pointer { return unsafe.Pointer(%c.pn) }\n", api[0], mixedCase(api), api[0]) |
| fn := regexp.MustCompile(fmt.Sprintf(`PN_EXTERN ([a-z0-9_ ]+ *\*?) *pn_%s_([a-z_]+)\(pn_%s_t *\*[a-z_]+ *,? *([^)]*)\)`, api, api)) |
| for _, m := range fn.FindAllStringSubmatch(header, -1) { |
| rtype, fname, argstr := mapType(m[1]), m[2], m[3] |
| gname := goFnName(api, fname) |
| if gname == "" { // Skip |
| continue |
| } |
| args := splitArgs(argstr) |
| fmt.Fprintf(out, "func (%c %s) %s", api[0], mixedCase(api), gname) |
| fmt.Fprintf(out, "(%s) %s { ", goArgs(args), rtype.Gotype) |
| fmt.Fprint(out, cAssigns(args)) |
| rtype.printBody(out, fmt.Sprintf("C.pn_%s_%s(%c.pn%s)", api, fname, api[0], cArgs(args))) |
| fmt.Fprintf(out, "}\n") |
| } |
| } |