Skip to content

Commit

Permalink
macdriver: refactor generation in preparation for supporting more typ…
Browse files Browse the repository at this point in the history
…es (#117)
  • Loading branch information
tmc authored Jul 3, 2023
1 parent ac280d6 commit 2bea5af
Show file tree
Hide file tree
Showing 8 changed files with 269 additions and 195 deletions.
7 changes: 5 additions & 2 deletions gen/cmd/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ func main() {
loadFile("api/foundation/nsurl.objc.json"),
loadFile("api/foundation/nsurlrequest.objc.json"),
}},
// {"coregraphics", []schemaLoader{
// loadFile("api/coregraphics/cgrect.objc.json"),
// }},

{"cocoa", []schemaLoader{
loadFile("api/foundation/nsbundle.objc.json"),
Expand Down Expand Up @@ -99,7 +102,7 @@ func main() {
})
return nil
}),
loadFile("api/appkit/nsview.objc.json").Then(filterProps(func(p schema.Property) bool {
loadFile("api/appkit/nsview.objc.json").Then(filterClassProperties(func(p schema.Property) bool {
// only available on macOS 11+, causing build errors on GitHub
return p.Name != "safeAreaRect"
})).Then(func(s *schema.Schema) error {
Expand All @@ -114,7 +117,7 @@ func main() {
{"webkit", []schemaLoader{
loadFile("api/webkit/wknavigation.objc.json"),
loadFile("api/webkit/wkuserscript.objc.json"),
loadFile("api/webkit/wkwebview.objc.json").Then(filterProps(func(p schema.Property) bool {
loadFile("api/webkit/wkwebview.objc.json").Then(filterClassProperties(func(p schema.Property) bool {
return p.Name != "pageZoom"
})),
loadFile("api/webkit/wkwebviewconfiguration.objc.json"),
Expand Down
28 changes: 20 additions & 8 deletions gen/cmd/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func loadFile(filename string) schemaLoader {
return loadFileBase(filename).Then(filterDeprecated)
}

func filterMethods(pred func(schema.Method) bool) schemaUpdater {
func filterClassMethods(pred func(schema.Method) bool) schemaUpdater {
filter := func(in []schema.Method) []schema.Method {
var out []schema.Method
for _, m := range in {
Expand All @@ -75,13 +75,16 @@ func filterMethods(pred func(schema.Method) bool) schemaUpdater {
return out
}
return func(s *schema.Schema) error {
if s.Class == nil {
return nil
}
s.Class.TypeMethods = filter(s.Class.TypeMethods)
s.Class.InstanceMethods = filter(s.Class.InstanceMethods)
return nil
}
}

func filterProps(pred func(schema.Property) bool) schemaUpdater {
func filterClassProperties(pred func(schema.Property) bool) schemaUpdater {
filter := func(in []schema.Property) []schema.Property {
var out []schema.Property
for _, m := range in {
Expand All @@ -92,15 +95,19 @@ func filterProps(pred func(schema.Property) bool) schemaUpdater {
return out
}
return func(s *schema.Schema) error {
if s.Class == nil {
return nil
}
s.Class.TypeProperties = filter(s.Class.TypeProperties)
s.Class.InstanceProperties = filter(s.Class.InstanceProperties)
return nil
}
}

var filterDeprecated = filterMethods(func(m schema.Method) bool {
// filterDeprecated removes deprecated methods and properties from a class.
var filterDeprecated = filterClassMethods(func(m schema.Method) bool {
return !m.Deprecated
}).Then(filterProps(func(p schema.Property) bool {
}).Then(filterClassProperties(func(p schema.Property) bool {
return !p.Deprecated
}))

Expand All @@ -119,7 +126,9 @@ func loadSchemas(contents []schemaLoader) ([]*schema.Schema, error) {
func definedClasses(schemas []*schema.Schema) map[string]bool {
r := map[string]bool{}
for _, input := range schemas {
r[input.Class.Name] = true
if input.Class != nil {
r[input.Class.Name] = true
}
}
return r
}
Expand All @@ -129,7 +138,7 @@ func generate(basePackage string, packages []pkg) error {
for _, p := range packages {
schemas, err := loadSchemas(p.Contents)
if err != nil {
return err
return fmt.Errorf("loading schemas for package %q: %w", p.Name, err)
}
if err := generatePackage(p.Name, schemas, imports); err != nil {
return err
Expand Down Expand Up @@ -179,6 +188,9 @@ func generatePackage(name string, schemas []*schema.Schema, imports []gen.Packag
addFramework("AppKit")
}
for _, input := range schemas {
if input.Class == nil {
continue
}
for _, fw := range input.Class.Frameworks {
fw = strings.ReplaceAll(fw, " ", "")
// FIXME is there a better way to determine which includes and frameworks
Expand All @@ -194,12 +206,12 @@ func generatePackage(name string, schemas []*schema.Schema, imports []gen.Packag
}
pkg, err := gen.Convert(desc, combinedImports, schemas...)
if err != nil {
return fmt.Errorf("generating package %s: %w", name, err)
return fmt.Errorf("error converting package %s: %w", name, err)
}
outPath := path.Join(name, desc.Name+"_objc.gen.go")
var b bytes.Buffer
if err := pkg.Generate(&b); err != nil {
return fmt.Errorf("generating package %s: %w", name, err)
return fmt.Errorf("error generating package %s: %w", name, err)
}
code, err := format.Source(b.Bytes())
if err != nil {
Expand Down
81 changes: 81 additions & 0 deletions gen/convert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package gen

import (
"fmt"

"github.com/progrium/macschema/schema"
)

func Convert(desc PackageDescription, imports []PackageContents, schemas ...*schema.Schema) (*GoPackage, error) {
pkg := &GoPackage{
PackageDescription: desc,
}
consumedImports := map[Import]bool{
{Path: "unsafe"}: true,
{Path: "github.com/progrium/macdriver/objc"}: true,
}
for _, s := range schemas {
if s.Class != nil {
classDef, err := processClassSchema(pkg, s, imports, consumedImports)
if err != nil {
return pkg, fmt.Errorf("issue with class %s failed to parse declaration %+v: %w", s.Class.Name, s.Class, err)
}
pkg.Classes = append(pkg.Classes, classDef)
} else if s.Struct != nil {
return pkg, fmt.Errorf("%v: structs are not yet supported", s.Struct.Name)
} else {
return pkg, fmt.Errorf("invalid schema kind %v", s.Kind)
}
}
for imp := range consumedImports {
pkg.Imports = append(pkg.Imports, imp)
}
return pkg, nil
}

// processClassSchema converts a schema.Class into a ClassDef
func processClassSchema(pkg *GoPackage, s *schema.Schema, imports []PackageContents, consumedImports map[Import]bool) (ClassDef, error) {
cb := classBuilder{
Class: *s.Class,
Imports: imports,
consumedImports: consumedImports,
}
classDef := ClassDef{
Name: cb.Class.Name,
Base: "objc.Object",
}
decl, err := parseClassDeclaration(cb.Class.Declaration)
if err != nil {
return classDef, err
}

if decl.Base != "NSObject" {
if cls := cb.mapClass(decl.Base); cls != nil {
classDef.Base = cls.GoType
}
}

cb.EachTypeMethod(func(m schema.Method) {
defer ignoreIfUnimplemented(fmt.Sprintf("%s.%s", s.Class.Name, m.Name))

msg := cb.msgSend(m, true)
wrapper := MethodDef{
Name: fmt.Sprintf("%s_%s", cb.Class.Name, selectorNameToGoIdent(m.Name)),
WrappedFunc: cb.cgoWrapperFunc(m, true),
}

pkg.ClassMsgSendWrappers = append(pkg.ClassMsgSendWrappers, msg)
pkg.CGoWrapperFuncs = append(pkg.CGoWrapperFuncs, wrapper)
})
cb.EachInstanceMethod(func(m schema.Method) {
defer ignoreIfUnimplemented(fmt.Sprintf("%s.%s", s.Class.Name, m.Name))

method := cb.instanceMethod(m)
msg := cb.msgSend(m, false)

classDef.InstanceMethods = append(classDef.InstanceMethods, method)
pkg.MsgSendWrappers = append(pkg.MsgSendWrappers, msg)
})

return classDef, nil
}
Loading

0 comments on commit 2bea5af

Please sign in to comment.