diff --git a/capnpc-go/capnpc-go.go b/capnpc-go/capnpc-go.go index 9849fe7b..5b7627c5 100644 --- a/capnpc-go/capnpc-go.go +++ b/capnpc-go/capnpc-go.go @@ -1,4 +1,7 @@ /* +Copyright (c) 2013-2023 Sandstorm Development Group, Inc. and contributors +Licensed under the MIT License: + capnpc-go is the Cap'n proto code generator for Go. It reads a CodeGeneratorRequest from stdin and for a file foo.capnp it writes foo.capnp.go. This is usually invoked from `capnp compile -ogo`. diff --git a/capnpc-go/capnpc-go_test.go b/capnpc-go/capnpc-go_test.go index 16bf6f00..d8c8ed44 100644 --- a/capnpc-go/capnpc-go_test.go +++ b/capnpc-go/capnpc-go_test.go @@ -1,3 +1,7 @@ +/* +Copyright (c) 2013-2023 Sandstorm Development Group, Inc. and contributors +Licensed under the MIT License: +*/ package main import ( @@ -6,6 +10,7 @@ import ( "go/parser" "go/token" "os" + "os/exec" "path/filepath" "sort" "strconv" @@ -467,3 +472,144 @@ func nodeListString(n []*node) string { b.WriteByte(']') return b.String() } + +func setupTempDir() (string, error) { + dir, err := os.MkdirTemp("", "capnpc-go_test") + if err != nil { + return "", fmt.Errorf("setupTempDir(capnpc-go_test): %v", err) + } + + // Add go.mod to the new dir, minimal contents + err = os.WriteFile(filepath.Join(dir, "go.mod"), + []byte("module capnpc_go_test"), 0660) + if err != nil { + return "", fmt.Errorf("setupTempDir: write go.mod: %v", err) + } + + // Add go.work to the new dir, minimal contents + modRoot, err := os.Getwd() + if err != nil { + return "", fmt.Errorf("setupTempDir: Getwd: %v", err) + } + modRoot = filepath.Dir(modRoot) + + err = os.WriteFile(filepath.Join(dir, "go.work"), + []byte(fmt.Sprintf("use %s", modRoot)), 0660) + if err != nil { + return "", fmt.Errorf("setupTempDir: write go.work: %v", err) + } + return dir, nil +} + +// This test arises because capnproto-c++ has a file named: +// * src/capnp/persistent.capnp +// * Also found in this repo: std/capnp/persistent.capnp +// +// It contains two definitions: +// interface Persistent {} +// annotation persistent(interface, field) :Void; +// +// testdata/persistent-simple.capnp is a minimal reproducible test case for the +// collision. +func TestPersistent(t *testing.T) { + // This test is equivalent to: + // `capnp compile -ogo persistent-simple.capnp` + // + // Or the equivalent in-repo commands before a go install: + // ``` + // go build; + // capnp compile --no-standard-import -I../std -o- \ + // testdata/persistent-simple.capnp | \ + // capnpc-go -promises=0 -schemas=0 -structstrings=0 + // ``` + + defaultOptions := genoptions{ + promises: false, + schemas: false, + structStrings: false, + } + tests := []struct { + fname string + opts genoptions + }{ + {"persistent-simple.capnp.out", defaultOptions}, + } + for _, test := range tests { + dir, err := setupTempDir() + if err != nil { + t.Fatalf("%s: %v", test.fname, err) + break; + } + defer os.RemoveAll(dir) + genfname := test.fname+".go" + + data, err := readTestFile(test.fname) + if err != nil { + t.Errorf("reading %s: %v", test.fname, err) + continue + } + msg, err := capnp.Unmarshal(data) + if err != nil { + t.Errorf("Unmarshaling %q: %v", test.fname, err) + continue + } + req, err := schema.ReadRootCodeGeneratorRequest(msg) + if err != nil { + t.Errorf("Reading code generator request %q: %v", test.fname, err) + continue + } + reqFiles, err := req.RequestedFiles() + if err != nil { + t.Errorf("Reading code generator request %q: RequestedFiles: %v", test.fname, err) + continue + } + if reqFiles.Len() < 1 { + t.Errorf("Reading code generator request %q: %d RequestedFiles", test.fname, reqFiles.Len()) + continue + } + nodes, err := buildNodeMap(req) + if err != nil { + t.Errorf("buildNodeMap %q: %v", test.fname, err) + continue + } + g := newGenerator(reqFiles.At(0).Id(), nodes, test.opts) + err = g.defineFile() + if err != nil { + reqFname, _ := reqFiles.At(0).Filename() + t.Errorf("defineFile %q %+v: file %q: %v", test.fname, test.opts, reqFname, err) + continue + } + src := g.generate() + genfpath := filepath.Join(dir, genfname) + err = os.WriteFile(genfpath, []byte(src), 0660) + if err != nil { + t.Fatalf("Writing generated code %q: %v", genfpath, err) + break + } + + // Relies on persistent-simple.capnp with $Go.package("persistent_simple") + // not being ("main"). Thus `go build` skips writing an executable. + args := []string{ + "build", "-C", dir, genfname, + } + cmd := exec.Command("go", args...) + cmd.Stdin = strings.NewReader("") + var sout strings.Builder + cmd.Stdout = &sout + var serr strings.Builder + cmd.Stderr = &serr + if err = cmd.Run(); err != nil { + if gotcode, ok := err.(*exec.ExitError); ok { + exitcode := gotcode.ExitCode() + t.Errorf("go %+v exitcode:%d", args, exitcode) + t.Errorf("sout:\n%s", sout.String()) + t.Errorf("serr:\n%s", serr.String()) + t.Errorf("\n%s:\n%s", genfname, src) + continue + } else { + t.Errorf("go %+v: %v", args, err) + continue + } + } + } +} diff --git a/capnpc-go/nodes.go b/capnpc-go/nodes.go index 8a71a2ee..66b770db 100644 --- a/capnpc-go/nodes.go +++ b/capnpc-go/nodes.go @@ -258,6 +258,17 @@ func resolveName(nodes nodeMap, n *node, base, name string, file *node) error { name = parseAnnotations(na).Rename(name) if base == "" { n.Name = strings.Title(name) + if n.Which() == schema.Node_Which_annotation && n.Name[0] != name[0] { + // Names that had a lowercase first letter change to uppercase and + // now might collide with a similar-named node. + // + // This rule forces Annotations to have a trailing underscore. The + // idea is to use a consistent naming rule that works even if there + // is no name collision yet. If a node is added later, names will + // not get mixed up or require a big refactor downstream. + // See also: persistent.capnp + n.Name = strings.Title(name) + "_" + } } else { n.Name = base + "_" + name } diff --git a/capnpc-go/templates/interfaceClient b/capnpc-go/templates/interfaceClient index f43c31e3..58a7aac3 100644 --- a/capnpc-go/templates/interfaceClient +++ b/capnpc-go/templates/interfaceClient @@ -60,7 +60,7 @@ func (c {{$.Node.Name}}) Release() { // Resolve blocks until the capability is fully resolved or the Context // expires. -func (c {{$.Node.Name}}) Resolve(ctx context.Context) error { +func (c {{$.Node.Name}}) Resolve(ctx {{$.G.Imports.Context}}.Context) error { return capnp.Client(c).Resolve(ctx) } diff --git a/capnpc-go/testdata/persistent-simple.capnp b/capnpc-go/testdata/persistent-simple.capnp new file mode 100644 index 00000000..e7dfa03c --- /dev/null +++ b/capnpc-go/testdata/persistent-simple.capnp @@ -0,0 +1,36 @@ +# Copyright (c) 2023 Sandstorm Development Group, Inc. and contributors +# Licensed under the MIT License: +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +using Go = import "go.capnp"; + +@0xbcfe42e3392b05a8; + +$Go.package("persistent_simple"); +$Go.import("capnproto.org/go/capnp/v3/capnpc-go/testdata/persistent-simple"); + +interface Persistent { +} + +annotation persistent(interface, field) :Void; + +struct ThisStructOnlyNeededToGetImportsToWork { + value @0 :Int64; +} diff --git a/capnpc-go/testdata/persistent-simple.capnp.out b/capnpc-go/testdata/persistent-simple.capnp.out new file mode 100644 index 00000000..686d7b90 Binary files /dev/null and b/capnpc-go/testdata/persistent-simple.capnp.out differ diff --git a/std/capnp/compat/json/json.capnp.go b/std/capnp/compat/json/json.capnp.go index ace5fdda..8f3df669 100644 --- a/std/capnp/compat/json/json.capnp.go +++ b/std/capnp/compat/json/json.capnp.go @@ -10,12 +10,12 @@ import ( strconv "strconv" ) -const Name = uint64(0xfa5b1fd61c2e7c3d) -const Flatten = uint64(0x82d3e852af0336bf) -const Discriminator = uint64(0xcfa794e8d19a0162) -const Base64 = uint64(0xd7d879450a253e4b) -const Hex = uint64(0xf061e22f0ae5c7b5) -const Notification = uint64(0xa0a054dea32fd98c) +const Name_ = uint64(0xfa5b1fd61c2e7c3d) +const Flatten_ = uint64(0x82d3e852af0336bf) +const Discriminator_ = uint64(0xcfa794e8d19a0162) +const Base64_ = uint64(0xd7d879450a253e4b) +const Hex_ = uint64(0xf061e22f0ae5c7b5) +const Notification_ = uint64(0xa0a054dea32fd98c) type Value capnp.Struct type Value_Which uint16 diff --git a/std/capnp/cxx/c++.capnp.go b/std/capnp/cxx/c++.capnp.go index 337774f4..7795bf51 100644 --- a/std/capnp/cxx/c++.capnp.go +++ b/std/capnp/cxx/c++.capnp.go @@ -6,8 +6,8 @@ import ( schemas "capnproto.org/go/capnp/v3/schemas" ) -const Namespace = uint64(0xb9c6f99ebf805f2c) -const Name = uint64(0xf264a779fef191ce) +const Namespace_ = uint64(0xb9c6f99ebf805f2c) +const Name_ = uint64(0xf264a779fef191ce) const schema_bdf87d7bb8304e81 = "x\xda\x12\x08w`1\xe4\x15gb`\x0a\x94`e" + "\xfb\xaf\x13\xdf\xb0\x7f\xde\xcfc;\x19\x02\xb9X\x19\xff" + "7\xfa\x19\xec\xa8\xae\xfd\xb1\x97\x81\x81Q\xf0\xe3\"\xc1" + diff --git a/std/capnp/persistent.capnp b/std/capnp/persistent.capnp index cd16a23a..9d353c9e 100644 --- a/std/capnp/persistent.capnp +++ b/std/capnp/persistent.capnp @@ -108,7 +108,7 @@ interface Persistent@0xc8cb212fcd9f5691(SturdyRef, Owner) { } } -annotation persistent(interface, field) :Void $Go.name("PersistentAnnotation"); +annotation persistent(interface, field) :Void; # Apply this annotation to interfaces for objects that will always be persistent, instead of # extending the Persistent capability, since the correct type parameters to Persistent depend on # the realm, which is orthogonal to the interface type and therefore should not be defined diff --git a/std/capnp/persistent/persistent.capnp.go b/std/capnp/persistent/persistent.capnp.go index 89185a79..58dd1edf 100644 --- a/std/capnp/persistent/persistent.capnp.go +++ b/std/capnp/persistent/persistent.capnp.go @@ -11,7 +11,7 @@ import ( context "context" ) -const PersistentAnnotation = uint64(0xf622595091cafb67) +const Persistent_ = uint64(0xf622595091cafb67) type Persistent capnp.Client @@ -332,34 +332,32 @@ func (p Persistent_SaveResults_Future) SturdyRef() *capnp.Future { return p.Future.Field(0, nil) } -const schema_b8630836983feed7 = "x\xdat\x91\xbfk\x14A\x1c\xc5\xbfof\xd7\xbd\x83" + - "\x0b\xb9\xb9\x11rE\xe0T\x10A4\xf1\x07X\xa4\xb9" + - "\xdb\x14j\xa5;w \xc4\xca5\x8e\x1a\xb8\xcc\x1d;" + - "\xb3\x11+I\xafE:\xad\xac\x02\xea?\xe0\x8fF\xb4" + - "\x12%\x88\x95X\xd8\xa4\xb3Ql\xb4\x10\x19Y\xf1." + - "K\xbc\xb4\xdf\xe1\xcd\xfb\xbc\xf7\xea_:\xc1\xc9\xa9\xf7" + - "\x9c\x98:\x16\xee\xf3/\xbfu\xee\xbe:\x7f\xf3)\x89" + - "Y\xf8\x8dK\x0f\xb7\xe6\x0f\xbe{C!\xa2:No" + - "a\x11\xf23\"\"\xf9\x09m*\xbd\x0b\xc1\xfd\xc7\xaf" + - "\xed\xfbg*\xcb\xcf\x88\xa8\x0e\xf9\x1b\xdb\xb2\xca\x8e\x10" + - "\xc9\xc3\xec\x9c\xccY$s6\xe3o\xfcz\xbb\x91," + - "\x1d\xfaA\x1fDx\x00%\x09d\xce\xb6\xe5:\x8b\xe4" + - ":k\xf5\x1e1\x0e\x82\xdf\xb4'\x9aK/\x06?'" + - "\xb1<`\x0b\x90\x8fY\xc1\xb2\xc9\xdat\xc1\x0fuf" + - "W\xac\xd3\x81qs\xcb\xe9\xd0\x0c\x17\x92\x7f\x17\xe3\xe6" + - "z\xe9\x9a\xeej\x9b\xf7\x9d\xa5\x04P\x01\x0f\x88\x02\x10" + - "\x89\xa9.\x91\xaaq\xa8&\x83\xb7.\xcf\xae\xdd\xeej" + - "\xc2u4P\xf2$B\x830\xf6`\xbb=\"m\x9c" + - "\xaa\xa0\x8c\\\xbd\\\xea\xb2z\xd5\x17\x08I\x9a\xa5\xc4" + - "W\xad\x1f\xf1P\xd4wV\x05<$\x1aK1\x92\x09" + - "q\x94(\xae!\x9e\x858\x1eM\xdbtM\x0b\xb4T" + - "\xc0J`(2L:v\x90\x00q\x05\"\xec\x8a\xea" + - ")\xdf\xdbI\xd6\xbax\xcb\xe8lB\x96\xe1N\x96\xbf" + - "\x1d\x81\xf9\xef\xf7\xe6g\x1aW\x9e\xbf\xa6\xc2 n\x02" + - "\xb5\xc2\xee\x89\x1fU\x0b\xe3bc\x06.\x9dv+\x03" + - "C\xc1\xf8W\xbe\xd7\x0a\xed\xa2\x83U\xbbk\x84E\"" + - "U\xe1P\xfb\x19\xeeX\x9d\xf6\xcf\x0e24\xc2\xff\x17" + - "\xf8\x13\x00\x00\xff\xff\x80\x98\xd3O" +const schema_b8630836983feed7 = "x\xdat\x90\xbfk\x14A\x1c\xc5\xdf\x9b\xd9u\xef " + + "\xc1\x9b\x1b\xc1\x14\x81\xa8\x8d\"\x18\x7f\x81E\x9a\xbb\x8d" + + "\xa0^!\xee\xec\xa1\x10\xbb5\x8e?\xe0\xb2Yv6" + + "\x09V\xfe\x036\xe9\xec\xc4\xc2\xd2\xc2J\xb1\x11;Q" + + ",\x82\x85\x95M\xfe\x00\xed\x14\xb1\x18Y0\x97\xe5\xbc" + + "\xb43\xdf\xc7\xfb|^\xe7K?8?\xbb%!\xcc" + + "\xa9\xf0\x90\x7f\xf7\xa3\xff\xe4\xfd\xb5\x07\xaf\xa1\xe6\xe9\xb7" + + "o=\xfb|\xf6\xf8\xa7\x0f\x08\x19ux\xf19\x97\xa9" + + "_1\x02\xf4K\xf6\xd0\xf8WJ\xfa\xaf\xdf{O/" + + "\xb5V\xdf\x00\xe8P\xefpW\x7f\xe3I@\xff\xe6U" + + "=\x10\x91\x1e\x88\xa3\xfe\xfe\x9f\x8f\xdb\xc9\xca\x89\x9f\xd8" + + "Q\xe116\"\xd4\x03\xb1\xabo\x8a\x08\x18&B\x12" + + "\xf4/\xdc\xb9\xb9\x95\xb7\xeb\xbf\xa6\x91\xc4b\x89\xda\xd4" + + "\xd7\xfa\xba\xe8\xe1\xb2/l\xe9\x1e\xba\xca\x06y\xb5\xb8" + + "\x9a\x15y\xb1\x94\xfc{\xc9\xab\xc5a\xb6iS\xeb6" + + "F\x95CB\x9a@\x06@@@\xcd\xa6\x80\x99\x914" + + "s\x82\xdeU\x1b\xe5\xddG\xa9\x05\xef\xb1\xcbF'\xc0" + + ".8\xee\x10\x93\x1d\x91\xcd+\xd3b\x13\xb9}\xbb\xb1" + + "d\xfb\x8e\xaf\x11\x92\xac\xcc \xd7\x9c\xdf\xe3A4\xaa" + + "\x9c\x09d\x08\x8c\xa3\xdc\x8b)u\x1a\x88g\x18\xcfS" + + "\x9d\x89\x0e\xbbl\xd3*.\x98@4\xc0X;L{" + + "\xec3!\xe3\x16U\x98\xaa\xf6\x05?\xdc7[\xb8\xb1" + + "\x95\xdbr\x8aK\xb1\xef\x92\x90\x90\xe3\x13y\xd0\xa4\xbd" + + "Zh\xcdM,\xba\x0c\x98\x96\xa49\"\xf8\xd8\xd9l" + + "te\xbdd7\xfc\x7f\xce\xbf\x01\x00\x00\xff\xff\x92\xdc" + + "\xc1$" func RegisterSchema(reg *schemas.Registry) { reg.Register(&schemas.Schema{ diff --git a/std/fixups.patch b/std/fixups.patch index e83d9fb3..e11e594b 100644 --- a/std/fixups.patch +++ b/std/fixups.patch @@ -38,14 +38,3 @@ interface @17 :Void; # The only interface value that can be represented statically is "null", whose methods always ---- a/capnp/persistent.capnp 2021-10-21 15:12:33.501158612 -0400 -+++ b/capnp/persistent.capnp 2021-10-21 15:13:31.930030219 -0400 -@@ -108,7 +108,7 @@ - } - } - --annotation persistent(interface, field) :Void; -+annotation persistent(interface, field) :Void $Go.name("PersistentAnnotation"); - # Apply this annotation to interfaces for objects that will always be persistent, instead of - # extending the Persistent capability, since the correct type parameters to Persistent depend on - # the realm, which is orthogonal to the interface type and therefore should not be defined diff --git a/std/go/go.capnp.go b/std/go/go.capnp.go index f93b51bd..1537b5aa 100644 --- a/std/go/go.capnp.go +++ b/std/go/go.capnp.go @@ -6,13 +6,13 @@ import ( schemas "capnproto.org/go/capnp/v3/schemas" ) -const Package = uint64(0xbea97f1023792be0) -const Import = uint64(0xe130b601260e44b5) -const Doc = uint64(0xc58ad6bd519f935e) -const Tag = uint64(0xa574b41924caefc7) -const Notag = uint64(0xc8768679ec52e012) -const Customtype = uint64(0xfa10659ae02f2093) -const Name = uint64(0xc2b96012172f8df1) +const Package_ = uint64(0xbea97f1023792be0) +const Import_ = uint64(0xe130b601260e44b5) +const Doc_ = uint64(0xc58ad6bd519f935e) +const Tag_ = uint64(0xa574b41924caefc7) +const Notag_ = uint64(0xc8768679ec52e012) +const Customtype_ = uint64(0xfa10659ae02f2093) +const Name_ = uint64(0xc2b96012172f8df1) const schema_d12a1c51fedd6c88 = "x\xda\x12\x98\xe8\xc0b\xc8{\x9c\x89\x81)P\x81\x95" + "\xed\xff\xf1\xf7\xa7T$\xb7\x94,e\x08\xe4d\xe5\xf8" + "\xdf\x91s\xf7_\xa0\x8c\xd6E\x06\x06FaO\xc6," +