Commit ea54158d authored by Kenton Varda's avatar Kenton Varda

Install schema.capnp and c++.capnp to $PREFIX/include/capnp. Make sure…

Install schema.capnp and c++.capnp to $PREFIX/include/capnp.  Make sure /usr/include and /usr/local/include are in the capnpc search path.  Fix Makefile bug related to ridiculous gmake implicit rules.  And update the docs a bit.
parent 452c84b2
...@@ -40,6 +40,15 @@ MAINTAINERCLEANFILES = \ ...@@ -40,6 +40,15 @@ MAINTAINERCLEANFILES = \
maintainer-clean-local: maintainer-clean-local:
-rm -rf build-aux -rm -rf build-aux
# gmake defines an implicit rule building n from n.o. Unfortunately, this triggers on our .capnp
# files because they generate .capnp.c++ which is compiled to .capnp.o. In addition to being
# nonsense, this leads to cyclic dependency issues and could even cause the .capnp files to be
# unexpectedly overwritten! We need to cancel the implicit rule by declaring an explicit one.
#
# I want the hours of my life back that I spent figuring this out.
$(capnpc_inputs) $(test_capnpc_inputs):
:
capnpc_inputs = \ capnpc_inputs = \
src/capnp/c++.capnp \ src/capnp/c++.capnp \
src/capnp/schema.capnp src/capnp/schema.capnp
...@@ -50,11 +59,7 @@ capnpc_outputs = \ ...@@ -50,11 +59,7 @@ capnpc_outputs = \
src/capnp/schema.capnp.c++ \ src/capnp/schema.capnp.c++ \
src/capnp/schema.capnp.h src/capnp/schema.capnp.h
# This should depend on $(capnpc_inputs), but then make mysteriously complains capnpc_middleman: $(capnpc_inputs)
# about a cyclic dependency. I don't know where it is coming from. I rummaged
# around in the generated Makefile a bit but couldn't figure it out. I give
# up. Automake is terrible.
capnpc_middleman:
$(CAPNPC) -oc++ $(capnpc_inputs) $(CAPNPC) -oc++ $(capnpc_inputs)
touch capnpc_middleman touch capnpc_middleman
...@@ -63,6 +68,10 @@ $(capnpc_outputs): capnpc_middleman ...@@ -63,6 +68,10 @@ $(capnpc_outputs): capnpc_middleman
includecapnpdir = $(includedir)/capnp includecapnpdir = $(includedir)/capnp
includekjdir = $(includedir)/kj includekjdir = $(includedir)/kj
dist_includecapnp_DATA = \
src/capnp/c++.capnp \
src/capnp/schema.capnp
includekj_HEADERS = \ includekj_HEADERS = \
src/kj/common.h \ src/kj/common.h \
src/kj/units.h \ src/kj/units.h \
...@@ -119,7 +128,8 @@ libcapnp_a_SOURCES= \ ...@@ -119,7 +128,8 @@ libcapnp_a_SOURCES= \
src/capnp/serialize.c++ \ src/capnp/serialize.c++ \
src/capnp/serialize-packed.c++ src/capnp/serialize-packed.c++
nodist_libcapnp_a_SOURCES = \ nodist_libcapnp_a_SOURCES = \
src/capnp/schema.capnp.c++ src/capnp/schema.capnp.c++ \
src/capnp/c++.capnp.c++
# Source files intentionally not included in the dist at this time: # Source files intentionally not included in the dist at this time:
# src/capnp/serialize-snappy* # src/capnp/serialize-snappy*
...@@ -138,11 +148,7 @@ test_capnpc_outputs = \ ...@@ -138,11 +148,7 @@ test_capnpc_outputs = \
src/capnp/test-import.capnp.c++ \ src/capnp/test-import.capnp.c++ \
src/capnp/test-import.capnp.h src/capnp/test-import.capnp.h
# This should depend on $(test_capnpc_inputs), but then make mysteriously complains test_capnpc_middleman: $(test_capnpc_inputs)
# about a cyclic dependency. I don't know where it is coming from. I rummaged
# around in the generated Makefile a bit but couldn't figure it out. I give
# up. Automake is terrible.
test_capnpc_middleman:
$(CAPNPC) -oc++ $(test_capnpc_inputs) $(CAPNPC) -oc++ $(test_capnpc_inputs)
touch test_capnpc_middleman touch test_capnpc_middleman
......
...@@ -108,7 +108,12 @@ main = do ...@@ -108,7 +108,12 @@ main = do
let isVerbose = not $ null [opt | opt@VerboseOpt <- options] let isVerbose = not $ null [opt | opt@VerboseOpt <- options]
let outputs = [(fn, dir) | OutputOpt _ fn dir <- options] let outputs = [(fn, dir) | OutputOpt _ fn dir <- options]
let searchPath = [dir | SearchPathOpt dir <- options]
-- TODO(someday): We should perhaps determine the compiler binary's location and search its
-- ../include as well. Also, there should perhaps be a way to tell the compiler not to search
-- these hard-coded default paths.
let searchPath = ["/usr/local/include", "/usr/include"] ++
[dir | SearchPathOpt dir <- options]
let verifyDirectoryExists dir = do let verifyDirectoryExists dir = do
exists <- doesDirectoryExist dir exists <- doesDirectoryExist dir
......
...@@ -169,7 +169,28 @@ To generate C++ code from your `.capnp` [interface definition](language.html), r ...@@ -169,7 +169,28 @@ To generate C++ code from your `.capnp` [interface definition](language.html), r
This will create `myproto.capnp.h` and `myproto.capnp.c++` in the same directory as `myproto.capnp`. This will create `myproto.capnp.h` and `myproto.capnp.c++` in the same directory as `myproto.capnp`.
## Primitive Types ### Setting a Namespace
You probably want your generated types to live in a C++ namespace. You will need to import
`/capnp/c++.capnp` and use the `namespace` annotation it defines:
{% highlight capnp %}
using Cxx = import "/capnp/c++.capnp";
$Cxx.namespace("foo::bar::baz");
{% endhighlight %}
Note that for this to work, `capnp/c++.capnp` must be located in the search path specified with
`-I` options. This file is found in the Cap'n Proto source repo, so you could invoke `capnpc` like
so:
capnpc -I$CAPNPROTO_GIT_ROOT/c++/src -oc++ myproto.capnp
As of this writing, the file is not automatically installed anywhere, but in the future it will
be.
## Types
### Primitive Types
Primitive types map to the obvious C++ types: Primitive types map to the obvious C++ types:
...@@ -180,7 +201,7 @@ Primitive types map to the obvious C++ types: ...@@ -180,7 +201,7 @@ Primitive types map to the obvious C++ types:
* `Float64` -> `double` * `Float64` -> `double`
* `Void` -> `::capnp::Void` (An enum with one value: `::capnp::Void::VOID`) * `Void` -> `::capnp::Void` (An enum with one value: `::capnp::Void::VOID`)
## Structs ### Structs
For each struct `Foo` in your interface, a C++ type named `Foo` generated. This type itself is For each struct `Foo` in your interface, a C++ type named `Foo` generated. This type itself is
really just a namespace; it contains two important inner classes: `Reader` and `Builder`. really just a namespace; it contains two important inner classes: `Reader` and `Builder`.
...@@ -253,7 +274,7 @@ void setMyListField(::capnp::List<double>::Reader value); ...@@ -253,7 +274,7 @@ void setMyListField(::capnp::List<double>::Reader value);
::capnp::List<double>::Builder initMyListField(size_t size); ::capnp::List<double>::Builder initMyListField(size_t size);
{% endhighlight %} {% endhighlight %}
## Unions ### Unions
For each union `foo` declared in the struct, the struct's reader and builder have a method For each union `foo` declared in the struct, the struct's reader and builder have a method
`getFoo()` which returns a reader/builder for the union. The union reader/builder has accessors `getFoo()` which returns a reader/builder for the union. The union reader/builder has accessors
...@@ -264,7 +285,7 @@ crashes in debug mode or returns garbage when `NDEBUG` is defined. ...@@ -264,7 +285,7 @@ crashes in debug mode or returns garbage when `NDEBUG` is defined.
See the [example](#example_usage) at the top of the page for an example of unions. See the [example](#example_usage) at the top of the page for an example of unions.
## Lists ### Lists
Lists are represented by the type `capnp::List<T>`, where `T` is any of the primitive types, Lists are represented by the type `capnp::List<T>`, where `T` is any of the primitive types,
any Cap'n Proto user-defined type, `capnp::Text`, `capnp::Data`, or `capnp::List<U>` any Cap'n Proto user-defined type, `capnp::Text`, `capnp::Data`, or `capnp::List<U>`
...@@ -289,7 +310,7 @@ the element at the given index to a newly-allocated value with the given size an ...@@ -289,7 +310,7 @@ the element at the given index to a newly-allocated value with the given size an
for it. Struct lists do not have an `init` method because all elements are initialized to empty for it. Struct lists do not have an `init` method because all elements are initialized to empty
values when the list is created. values when the list is created.
## Enums ### Enums
Cap'n Proto enums become C++11 "enum classes". That means they behave like any other enum, but Cap'n Proto enums become C++11 "enum classes". That means they behave like any other enum, but
the enum's values are scoped within the type. E.g. for an enum `Foo` with value `bar`, you must the enum's values are scoped within the type. E.g. for an enum `Foo` with value `bar`, you must
...@@ -304,7 +325,7 @@ version of the protocol, or if the message is corrupt or malicious. In C++11, e ...@@ -304,7 +325,7 @@ version of the protocol, or if the message is corrupt or malicious. In C++11, e
to have any value that is within the range of their base type, which for Cap'n Proto enums is to have any value that is within the range of their base type, which for Cap'n Proto enums is
`uint16_t`. `uint16_t`.
## Blobs (Text and Data) ### Blobs (Text and Data)
Blobs are manipulated using the classes `capnp::Text` and `capnp::Data`. These classes are, Blobs are manipulated using the classes `capnp::Text` and `capnp::Data`. These classes are,
again, just containers for inner classes `Reader` and `Builder`. These classes are iterable and again, just containers for inner classes `Reader` and `Builder`. These classes are iterable and
...@@ -312,7 +333,7 @@ implement `size()` and `operator[]` methods. `Builder::operator[]` even returns ...@@ -312,7 +333,7 @@ implement `size()` and `operator[]` methods. `Builder::operator[]` even returns
(unlike with `List<T>`). `Text::Reader` additionally has a method `cStr()` which returns a (unlike with `List<T>`). `Text::Reader` additionally has a method `cStr()` which returns a
NUL-terminated `const char*`. NUL-terminated `const char*`.
## Interfaces ### Interfaces
Interfaces (RPC) are not yet implemented at this time. Interfaces (RPC) are not yet implemented at this time.
...@@ -337,25 +358,6 @@ details. ...@@ -337,25 +358,6 @@ details.
There is an [example](#example_usage) of all this at the beginning of this page. There is an [example](#example_usage) of all this at the beginning of this page.
## Setting a Namespace
You probably want your generated types to live in a C++ namespace. You will need to import
`/capnp/c++.capnp` and use the `namespace` annotation it defines:
{% highlight capnp %}
Cxx = import "/capnp/c++.capnp";
$Cxx.namespace("foo::bar::baz");
{% endhighlight %}
Note that for this to work, `capnp/c++.capnp` must be located in the search path specified with
`-I` options. This file is found in the Cap'n Proto source repo, so you could invoke `capnpc` like
so:
capnpc -I$CAPNPROTO_GIT_ROOT/c++/src -oc++ myproto.capnp
As of this writing, the file is not automatically installed anywhere, but in the future it will
be.
## Dynamic Reflection ## Dynamic Reflection
Sometimes you want to write generic code that operates on arbitrary types, iterating over the Sometimes you want to write generic code that operates on arbitrary types, iterating over the
...@@ -572,9 +574,9 @@ learned the hard way: ...@@ -572,9 +574,9 @@ learned the hard way:
class. This actually poses a significant problem in practice -- there exist server binaries class. This actually poses a significant problem in practice -- there exist server binaries
containing literally hundreds of megabytes of compiled protobuf code. Cap'n Proto generated code, containing literally hundreds of megabytes of compiled protobuf code. Cap'n Proto generated code,
on the other hand, is almost entirely inlined accessors. The only things that go into `.capnp.o` on the other hand, is almost entirely inlined accessors. The only things that go into `.capnp.o`
files are default values for pointer fields, and only if they have non-empty defaults (which are files are default values for pointer fields (if needed, which is rare) and the encoded schema
unusual). (Eventually, the object files may also contain type descriptors, but those should also (just the raw bytes of a Cap'n-Proto-encoded schema structure). The latter could even be removed
be small.) if you don't use dynamic reflection.
* The C++ Protobuf implementation used lots of dynamic initialization code (that runs before * The C++ Protobuf implementation used lots of dynamic initialization code (that runs before
`main()`) to do things like register types in global tables. This proved problematic for `main()`) to do things like register types in global tables. This proved problematic for
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment