Generate the remaining consts. (#55)

* Generate the remaining consts.

There were a number of hand-written consts in go-libvirt, including flag
values for various libvirt functions. Remove these and generate them
instead, so that we now have a complete set, and the naming is
consistent. I used c-for-go to do this generation, but turned off any
cgo usage by the generated code - we don't want or need to introduce a
dependency on cgo just to get constants from C headers. All code is
still generated using 'go generate ./...', which now calls a wrapper
script for added robustness.

This change also returns to using Go types for flags for most libvirt
functions, instead of plain integers.
This commit is contained in:
Geoff Hickey
2018-01-03 15:19:28 -05:00
committed by GitHub
parent 1a220100bd
commit 59d541f193
10 changed files with 2475 additions and 577 deletions

View File

@@ -16,6 +16,7 @@ package lvgen
import (
"fmt"
"go/ast"
"io"
"os"
"strconv"
@@ -23,6 +24,8 @@ import (
"text/template"
"unicode"
"unicode/utf8"
"golang.org/x/tools/go/loader"
)
// If you're making changes to the generator, or troubleshooting the generated
@@ -375,6 +378,8 @@ func fixAbbrevs(s string) string {
// defined in the protocol file. If one or both of these structs is not defined
// then either the args or return values are empty.
func procLink() {
flagTypes := mapFlagTypes()
for ix, proc := range Gen.Procs {
argsName := proc.Name + "Args"
retName := proc.Name + "Ret"
@@ -383,6 +388,7 @@ func procLink() {
if hasArgs {
argsStruct := Gen.Structs[argsIx]
Gen.Procs[ix].ArgsStruct = argsStruct.Name
changeFlagType(proc.Name, &argsStruct, flagTypes)
Gen.Procs[ix].Args = argsStruct.Members
}
if hasRet {
@@ -393,6 +399,138 @@ func procLink() {
}
}
// mapFlagTypes builds a map of the C types which appear to correspond to the
// various flags fields in libvirt calls. Determining whether a type actually
// corresponds to a set of flags is done by pattern matching the type name;
// libvirt isn't completely consistent about the names of flag types, but they
// all seem to have one of three suffixes, so that's what we look for here.
//
// This code uses the loader package to load the constants file generated by
// c-for-go, which runs against libvirt's C sources. This file is generated by
// 'go generate ./...' prior to the lvgen/ generator being run.
func mapFlagTypes() map[string]ast.Expr {
pconf := loader.Config{}
f, err := pconf.ParseFile("../../const.gen.go", nil)
if err != nil {
panic(fmt.Sprintln("failed to read constants file: ", err))
}
pconf.CreateFromFiles("const", f)
prog, err := pconf.Load()
if err != nil {
panic(fmt.Sprintln("failed to load package: ", err))
}
cpkg := prog.Package("const")
tmap := make(map[string]ast.Expr)
ast.Inspect(cpkg.Files[0], func(n ast.Node) bool {
switch t := n.(type) {
case *ast.TypeSpec:
// There isn't a single name pattern that covers all of the flag
// types, so we'll collect all the types that map to int32 here.
if fmt.Sprintf("%s", t.Type) == "int32" {
tmap[t.Name.String()] = t.Type
}
}
return true
})
return tmap
}
// Many libvirt calls use flags whose values come from a set of definitions
// whose name we can't predict. So this map exists to do the translation for us.
// The only way to remove this fragile map would be to use the comments from the
// .c files in libvirt, which contain doxygen-style parameter comments that
// specify the valid value types for flags.
var flagMap = map[string]string{
"ConnectOpen": "ConnectFlags",
"DomainAddIothread": "DomainModificationImpact",
"DomainCoreDumpWithFormat": "DomainCoreDumpFlags",
"DomainCreateXML": "DomainCreateFlags",
"DomainCreateWithFiles": "DomainCreateFlags",
"DomainCreateXMLWithFiles": "DomainCreateFlags",
"DomainDefineXMLFlags": "DomainDefineFlags",
"DomainDelIothread": "DomainModificationImpact",
"DomainDestroyFlags": "DomainDestroyFlagsValues",
"DomainGetCPUStats": "TypedParameterFlags",
"DomainGetEmulatorPinInfo": "DomainModificationImpact",
"DomainGetInterfaceParameters": "DomainModificationImpact",
"DomainGetIothreadInfo": "DomainModificationImpact",
"DomainGetMetadata": "DomainModificationImpact",
"DomainGetPerfEvents": "DomainModificationImpact",
"DomainGetXMLDesc": "DomainXMLFlags",
"DomainManagedSaveDefineXML": "DomainSaveRestoreFlags",
"DomainManagedSaveGetXMLDesc": "DomainXMLFlags",
"DomainMemoryPeek": "DomainMemoryFlags",
"DomainMigratePerform3Params": "DomainMigrateFlags",
"DomainOpenChannel": "DomainChannelFlags",
"DomainOpenGraphicsFd": "DomainOpenGraphicsFlags",
"DomainPinEmulator": "DomainModificationImpact",
"DomainPinIothread": "DomainModificationImpact",
"DomainSetLifecycleAction": "DomainModificationImpact",
"DomainSetMemoryStatsPeriod": "DomainMemoryModFlags",
"DomainSetMetadata": "DomainModificationImpact",
"DomainSetPerfEvents": "DomainModificationImpact",
"DomainSetVcpu": "DomainModificationImpact",
"DomainShutdownFlags": "DomainShutdownFlagValues",
"DomainUndefineFlags": "DomainUndefineFlagsValues",
"StoragePoolCreateXML": "StoragePoolCreateFlags",
"StoragePoolGetXMLDesc": "StorageXMLFlags",
"StorageVolCreateXML": "StorageVolCreateFlags",
"StorageVolCreateXMLFrom": "StorageVolCreateFlags",
}
// findFlagType attempts to find a real type for the flags passed to a given
// libvirt routine.
func findFlagType(procName string, flagTypes map[string]ast.Expr) (string, bool) {
flagName, ok := flagMap[procName]
if ok {
// Verify the mapped name exists
if _, ok = flagTypes[flagName]; ok == false {
// If one of the manual flag mappings is wrong, complain but
// continue. This happens with older versions of libvirt.
fmt.Printf("manual flag type %v for %v not found, continuing", flagName, procName)
return "", false
}
return flagName, true
}
// Not in the manual map, so do a search using the 3 patterns libvirt uses.
tnames := [...]string{procName + "Flags", procName + "FlagValues", procName + "FlagsValues"}
for _, n := range tnames {
if _, ok := flagTypes[n]; ok == true {
return n, true
}
}
return "", false
}
// changeFlagType looks up the go type for a libvirt call's flags field. In C
// these flags are all uint32, and you have to consult the documentation to
// determine what the valid set of flags is for a given libvirt call. For Go
// we're attempting to do better by specifying an actual type so that the
// possible values are easier to determine. This is a heuristic, however, based
// on naming patterns in the libvirt code. To do better we would need to look at
// the doxygen-style comments in the libvirt sources.
//
// Failing to find a flags type isn't a fatal error, it just means that we'll
// leave the flags with a type of uint32.
func changeFlagType(procName string, s *Structure, flagTypes map[string]ast.Expr) {
for ix, d := range s.Members {
if d.Name == "Flags" {
tname, found := findFlagType(procName, flagTypes)
if found {
s.Members[ix].Type = tname
} else {
// If you're adding procedures to to the manual map, you may
// want to uncomment this to see what flag types are not found.
// fmt.Println("flags type for", procName, "not found")
}
}
}
}
//---------------------------------------------------------------------------
// Routines called by the parser's actions.
//---------------------------------------------------------------------------
@@ -592,7 +730,6 @@ func AddOptValue(identifier, itype string) {
atype := "[]" + itype
decl := NewDecl(identifier, atype)
newType := "Opt" + decl.Name
fmt.Printf("Adding mapping %v = %v\n", decl.Name, newType)
goEquivTypes[decl.Name] = newType
decl.Name = newType
addDecl(decl)