Skip to content

Commit 2bea5af

Browse files
authored
macdriver: refactor generation in preparation for supporting more types (#117)
1 parent ac280d6 commit 2bea5af

File tree

8 files changed

+269
-195
lines changed

8 files changed

+269
-195
lines changed

gen/cmd/gen.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ func main() {
2828
loadFile("api/foundation/nsurl.objc.json"),
2929
loadFile("api/foundation/nsurlrequest.objc.json"),
3030
}},
31+
// {"coregraphics", []schemaLoader{
32+
// loadFile("api/coregraphics/cgrect.objc.json"),
33+
// }},
3134

3235
{"cocoa", []schemaLoader{
3336
loadFile("api/foundation/nsbundle.objc.json"),
@@ -99,7 +102,7 @@ func main() {
99102
})
100103
return nil
101104
}),
102-
loadFile("api/appkit/nsview.objc.json").Then(filterProps(func(p schema.Property) bool {
105+
loadFile("api/appkit/nsview.objc.json").Then(filterClassProperties(func(p schema.Property) bool {
103106
// only available on macOS 11+, causing build errors on GitHub
104107
return p.Name != "safeAreaRect"
105108
})).Then(func(s *schema.Schema) error {
@@ -114,7 +117,7 @@ func main() {
114117
{"webkit", []schemaLoader{
115118
loadFile("api/webkit/wknavigation.objc.json"),
116119
loadFile("api/webkit/wkuserscript.objc.json"),
117-
loadFile("api/webkit/wkwebview.objc.json").Then(filterProps(func(p schema.Property) bool {
120+
loadFile("api/webkit/wkwebview.objc.json").Then(filterClassProperties(func(p schema.Property) bool {
118121
return p.Name != "pageZoom"
119122
})),
120123
loadFile("api/webkit/wkwebviewconfiguration.objc.json"),

gen/cmd/loader.go

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ func loadFile(filename string) schemaLoader {
6464
return loadFileBase(filename).Then(filterDeprecated)
6565
}
6666

67-
func filterMethods(pred func(schema.Method) bool) schemaUpdater {
67+
func filterClassMethods(pred func(schema.Method) bool) schemaUpdater {
6868
filter := func(in []schema.Method) []schema.Method {
6969
var out []schema.Method
7070
for _, m := range in {
@@ -75,13 +75,16 @@ func filterMethods(pred func(schema.Method) bool) schemaUpdater {
7575
return out
7676
}
7777
return func(s *schema.Schema) error {
78+
if s.Class == nil {
79+
return nil
80+
}
7881
s.Class.TypeMethods = filter(s.Class.TypeMethods)
7982
s.Class.InstanceMethods = filter(s.Class.InstanceMethods)
8083
return nil
8184
}
8285
}
8386

84-
func filterProps(pred func(schema.Property) bool) schemaUpdater {
87+
func filterClassProperties(pred func(schema.Property) bool) schemaUpdater {
8588
filter := func(in []schema.Property) []schema.Property {
8689
var out []schema.Property
8790
for _, m := range in {
@@ -92,15 +95,19 @@ func filterProps(pred func(schema.Property) bool) schemaUpdater {
9295
return out
9396
}
9497
return func(s *schema.Schema) error {
98+
if s.Class == nil {
99+
return nil
100+
}
95101
s.Class.TypeProperties = filter(s.Class.TypeProperties)
96102
s.Class.InstanceProperties = filter(s.Class.InstanceProperties)
97103
return nil
98104
}
99105
}
100106

101-
var filterDeprecated = filterMethods(func(m schema.Method) bool {
107+
// filterDeprecated removes deprecated methods and properties from a class.
108+
var filterDeprecated = filterClassMethods(func(m schema.Method) bool {
102109
return !m.Deprecated
103-
}).Then(filterProps(func(p schema.Property) bool {
110+
}).Then(filterClassProperties(func(p schema.Property) bool {
104111
return !p.Deprecated
105112
}))
106113

@@ -119,7 +126,9 @@ func loadSchemas(contents []schemaLoader) ([]*schema.Schema, error) {
119126
func definedClasses(schemas []*schema.Schema) map[string]bool {
120127
r := map[string]bool{}
121128
for _, input := range schemas {
122-
r[input.Class.Name] = true
129+
if input.Class != nil {
130+
r[input.Class.Name] = true
131+
}
123132
}
124133
return r
125134
}
@@ -129,7 +138,7 @@ func generate(basePackage string, packages []pkg) error {
129138
for _, p := range packages {
130139
schemas, err := loadSchemas(p.Contents)
131140
if err != nil {
132-
return err
141+
return fmt.Errorf("loading schemas for package %q: %w", p.Name, err)
133142
}
134143
if err := generatePackage(p.Name, schemas, imports); err != nil {
135144
return err
@@ -179,6 +188,9 @@ func generatePackage(name string, schemas []*schema.Schema, imports []gen.Packag
179188
addFramework("AppKit")
180189
}
181190
for _, input := range schemas {
191+
if input.Class == nil {
192+
continue
193+
}
182194
for _, fw := range input.Class.Frameworks {
183195
fw = strings.ReplaceAll(fw, " ", "")
184196
// FIXME is there a better way to determine which includes and frameworks
@@ -194,12 +206,12 @@ func generatePackage(name string, schemas []*schema.Schema, imports []gen.Packag
194206
}
195207
pkg, err := gen.Convert(desc, combinedImports, schemas...)
196208
if err != nil {
197-
return fmt.Errorf("generating package %s: %w", name, err)
209+
return fmt.Errorf("error converting package %s: %w", name, err)
198210
}
199211
outPath := path.Join(name, desc.Name+"_objc.gen.go")
200212
var b bytes.Buffer
201213
if err := pkg.Generate(&b); err != nil {
202-
return fmt.Errorf("generating package %s: %w", name, err)
214+
return fmt.Errorf("error generating package %s: %w", name, err)
203215
}
204216
code, err := format.Source(b.Bytes())
205217
if err != nil {

gen/convert.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package gen
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/progrium/macschema/schema"
7+
)
8+
9+
func Convert(desc PackageDescription, imports []PackageContents, schemas ...*schema.Schema) (*GoPackage, error) {
10+
pkg := &GoPackage{
11+
PackageDescription: desc,
12+
}
13+
consumedImports := map[Import]bool{
14+
{Path: "unsafe"}: true,
15+
{Path: "github.com/progrium/macdriver/objc"}: true,
16+
}
17+
for _, s := range schemas {
18+
if s.Class != nil {
19+
classDef, err := processClassSchema(pkg, s, imports, consumedImports)
20+
if err != nil {
21+
return pkg, fmt.Errorf("issue with class %s failed to parse declaration %+v: %w", s.Class.Name, s.Class, err)
22+
}
23+
pkg.Classes = append(pkg.Classes, classDef)
24+
} else if s.Struct != nil {
25+
return pkg, fmt.Errorf("%v: structs are not yet supported", s.Struct.Name)
26+
} else {
27+
return pkg, fmt.Errorf("invalid schema kind %v", s.Kind)
28+
}
29+
}
30+
for imp := range consumedImports {
31+
pkg.Imports = append(pkg.Imports, imp)
32+
}
33+
return pkg, nil
34+
}
35+
36+
// processClassSchema converts a schema.Class into a ClassDef
37+
func processClassSchema(pkg *GoPackage, s *schema.Schema, imports []PackageContents, consumedImports map[Import]bool) (ClassDef, error) {
38+
cb := classBuilder{
39+
Class: *s.Class,
40+
Imports: imports,
41+
consumedImports: consumedImports,
42+
}
43+
classDef := ClassDef{
44+
Name: cb.Class.Name,
45+
Base: "objc.Object",
46+
}
47+
decl, err := parseClassDeclaration(cb.Class.Declaration)
48+
if err != nil {
49+
return classDef, err
50+
}
51+
52+
if decl.Base != "NSObject" {
53+
if cls := cb.mapClass(decl.Base); cls != nil {
54+
classDef.Base = cls.GoType
55+
}
56+
}
57+
58+
cb.EachTypeMethod(func(m schema.Method) {
59+
defer ignoreIfUnimplemented(fmt.Sprintf("%s.%s", s.Class.Name, m.Name))
60+
61+
msg := cb.msgSend(m, true)
62+
wrapper := MethodDef{
63+
Name: fmt.Sprintf("%s_%s", cb.Class.Name, selectorNameToGoIdent(m.Name)),
64+
WrappedFunc: cb.cgoWrapperFunc(m, true),
65+
}
66+
67+
pkg.ClassMsgSendWrappers = append(pkg.ClassMsgSendWrappers, msg)
68+
pkg.CGoWrapperFuncs = append(pkg.CGoWrapperFuncs, wrapper)
69+
})
70+
cb.EachInstanceMethod(func(m schema.Method) {
71+
defer ignoreIfUnimplemented(fmt.Sprintf("%s.%s", s.Class.Name, m.Name))
72+
73+
method := cb.instanceMethod(m)
74+
msg := cb.msgSend(m, false)
75+
76+
classDef.InstanceMethods = append(classDef.InstanceMethods, method)
77+
pkg.MsgSendWrappers = append(pkg.MsgSendWrappers, msg)
78+
})
79+
80+
return classDef, nil
81+
}

0 commit comments

Comments
 (0)