Skip to content

Commit

Permalink
support architecture-specific methods and types
Browse files Browse the repository at this point in the history
  • Loading branch information
qmuntal committed Jun 6, 2023
1 parent 4c19ac3 commit a637b53
Show file tree
Hide file tree
Showing 10 changed files with 2,276 additions and 114 deletions.
6 changes: 5 additions & 1 deletion cmd/genwinmdsigs/ExampleTemplate.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ package windowsgenerated
import (
"unsafe"

"golang.org/x/sys/windows"
{{ if (eq .Arch "all") }}
"golang.org/x/sys/windows"
{{ end }}
)

var _ unsafe.Pointer

{{ if (eq .Arch "all") }}
type Guid windows.GUID
{{ end }}

{{.SysCalls}}
114 changes: 69 additions & 45 deletions cmd/genwinmdsigs/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,44 +67,63 @@ func Run() error {
return err
}

var b strings.Builder
b := map[genwinsyscallproto.Arch]*strings.Builder{
genwinsyscallproto.Arch386: {},
genwinsyscallproto.ArchAMD64: {},
genwinsyscallproto.ArchARM64: {},
genwinsyscallproto.ArchAll: {},
genwinsyscallproto.ArchNone: {},
}

if err := writePrototypes(&b, f, filterRegexp); err != nil {
if err := writePrototypes(b, f, filterRegexp); err != nil {
return err
}

content := b.String()
var finalContent string

if contentTemplate != nil {
var templateBuilder strings.Builder
if err := contentTemplate.Execute(&templateBuilder, struct{ SysCalls string }{content}); err != nil {
return err
for arch, w := range b {
if w.Len() == 0 {
continue
}
content := w.String()
var finalContent string

if contentTemplate != nil {
var templateBuilder strings.Builder
if err := contentTemplate.Execute(&templateBuilder, struct {
SysCalls string
Arch string
}{content, arch.String()}); err != nil {
return err
}
finalContent = templateBuilder.String()
} else {
finalContent = content
}
finalContent = templateBuilder.String()
} else {
finalContent = content
}

formattedContent, err := format.Source([]byte(finalContent))
if err != nil {
log.Printf("Unable to format generated code, writing unformatted code instead. Error: %v", err)
formattedContent = []byte(finalContent)
}
formattedContent, err := format.Source([]byte(finalContent))
if err != nil {
log.Printf("Unable to format generated code, writing unformatted code instead. Error: %v", err)
formattedContent = []byte(finalContent)
}

end := time.Now()
log.Printf("Time elapsed to produce sys signatures: %v\n", end.Sub(start))
end := time.Now()
log.Printf("Time elapsed to produce sys signatures: %v\n", end.Sub(start))

if *output != "" {
return os.WriteFile(*output, formattedContent, 0666)
if *output != "" {
target := *output
if arch != genwinsyscallproto.ArchAll {
target = strings.TrimSuffix(target, ".go") + "_" + arch.String() + ".go"
}
os.WriteFile(target, formattedContent, 0666)
} else {
log.Printf("Printing signature results for %s because no output path was specified:\n", arch)
log.Println("---")
log.Println(finalContent)
}
}
log.Println("Printing signature results because no output path was specified:")
log.Println("---")
log.Println(finalContent)
return nil
}

func writePrototypes(b *strings.Builder, f *winmd.Metadata, filterRegexp *regexp.Regexp) error {
func writePrototypes(b map[genwinsyscallproto.Arch]*strings.Builder, f *winmd.Metadata, filterRegexp *regexp.Regexp) error {
context, err := genwinsyscallproto.NewContext(f)
if err != nil {
return err
Expand All @@ -120,7 +139,7 @@ func writePrototypes(b *strings.Builder, f *winmd.Metadata, filterRegexp *regexp
continue
}

firstMethod := true
archSeen := make(map[genwinsyscallproto.Arch]bool)
for j := r.MethodList.Start; j < r.MethodList.End; j++ {
md, err := f.Tables.MethodDef.Record(j)
if err != nil {
Expand All @@ -131,25 +150,30 @@ func writePrototypes(b *strings.Builder, f *winmd.Metadata, filterRegexp *regexp
continue
}

// Write a comment describing this chunk of methods.
if firstMethod {
firstMethod = false
b.WriteString("\n\n// APIs for ")
b.WriteString(r.Namespace.String())
}
b.WriteString("\n")

if err := context.WriteMethod(b, j, md); err != nil {
// Include context in the error for diag purposes.
// writeSys may have partially written into b. This is actually convenient for diag.
lines := strings.Split(b.String(), "\n")
if len(lines) > 5 {
lines = lines[len(lines)-5:]
}
supportedArches := context.MethodDefSupportedArch(j)
for _, arch := range supportedArches.Unique() {
w := b[arch]

return fmt.Errorf(
"error context: \n---\n%v\n---\nfailed to write sys line for %v.Apis method %v: %v",
strings.Join(lines, "\n"), r.Namespace, md.Name, err)
// Write a comment describing this chunk of methods.
if !archSeen[arch] {
archSeen[arch] = true
w.WriteString("\n\n// APIs for ")
w.WriteString(r.Namespace.String())
}
w.WriteString("\n")

if err := context.WriteMethod(w, j, md, arch); err != nil {
// Include context in the error for diag purposes.
// writeSys may have partially written into b. This is actually convenient for diag.
lines := strings.Split(w.String(), "\n")
if len(lines) > 5 {
lines = lines[len(lines)-5:]
}

return fmt.Errorf(
"error context: \n---\n%v\n---\nfailed to write sys line for %v.Apis method %v: %v",
strings.Join(lines, "\n"), r.Namespace, md.Name, err)
}
}
}
}
Expand Down
47 changes: 35 additions & 12 deletions cmd/genwinmdsigs/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,38 +12,61 @@ import (
"testing"

"github.com/microsoft/go-winmd"
"github.com/microsoft/go-winmd/genwinsyscallproto"
)

func TestWriteMethod(t *testing.T) {
f, err := openTestWinmd()
if err != nil {
t.Fatal(err)
}
var b strings.Builder
r := regexp.MustCompile(`^(Windows\.Win32\.Storage\.FileSystem|Windows\.Win32\.Security\.Cryptography)::`)
if err := writePrototypes(&b, f, r); err != nil {
t.Fatal(err)
b := map[genwinsyscallproto.Arch]*strings.Builder{
genwinsyscallproto.Arch386: {},
genwinsyscallproto.ArchAMD64: {},
genwinsyscallproto.ArchARM64: {},
genwinsyscallproto.ArchAll: {},
genwinsyscallproto.ArchNone: {},
}
formattedContent, err := format.Source([]byte(b.String()))
if err != nil {
r := regexp.MustCompile(`^(Windows\.Win32\.Storage\.FileSystem|Windows\.Win32\.Security\.Cryptography|Windows\.Win32\.System\.Diagnostics\.Debug)::`)
if err := writePrototypes(b, f, r); err != nil {
t.Fatal(err)
}
Check(t, "go test ./cmd/genwinmdsigs", filepath.Join("testdata", "prototypes.golden.go"), string(formattedContent))
for arch, w := range b {
formattedContent, err := format.Source([]byte(w.String()))
if err != nil {
t.Fatal(err)
}
target := "prototypes.golden"
if arch != genwinsyscallproto.ArchAll {
target += "_" + arch.String()
}
target += ".go"
Check(t, "go test ./cmd/genwinmdsigs", filepath.Join("testdata", target), string(formattedContent))
}
}

func TestFullFile(t *testing.T) {
f, err := openTestWinmd()
if err != nil {
t.Fatal(err)
}
var b strings.Builder
if err := writePrototypes(&b, f, nil); err != nil {
t.Fatal(err)
b := map[genwinsyscallproto.Arch]*strings.Builder{
genwinsyscallproto.Arch386: {},
genwinsyscallproto.ArchAMD64: {},
genwinsyscallproto.ArchARM64: {},
genwinsyscallproto.ArchAll: {},
genwinsyscallproto.ArchNone: {},
}
_, err = format.Source([]byte(b.String()))
if err != nil {
if err := writePrototypes(b, f, nil); err != nil {
t.Fatal(err)
}
for _, w := range b {
_, err = format.Source([]byte(w.String()))
if err != nil {
t.Fatal(err)
}
}

// The generated source code is ~4 MB, so don't write it to source control as a golden file.
// This test only checks that the generation process doesn't fail and doesn't take an
// exceptionally long time.
Expand Down

0 comments on commit a637b53

Please sign in to comment.