From fe19fca0e521e3765e4ecf6a87b0a3d622a52b92 Mon Sep 17 00:00:00 2001 From: rsgowman Date: Tue, 13 Feb 2018 17:46:09 -0500 Subject: Serialize and deserialize null (#783) * Build (grpc's) nanopb with -DPB_FIELD_16BIT We require (at least) 16 bit fields. (By default, nanopb uses 8 bit fields, ie allowing up to 256 field tags.) Also note that this patch adds this to grpc's nanopb, rather than to our nanopb. We'll need to eventually either: a) we instruct grpc to use our nanopb b) we rely on grpc's nanopb instead of using our own. (^ marked as a TODO for now.) * Add some dependant protos Imported from protobuf. Nanopb requires these to be present (though anything using libprotobuf does not, as these are already built into that.) * Add generated nanopb protos based off of newly added proto definitions * Build the nanopb protos * Serialize and deserialize null --- Firestore/CMakeLists.txt | 1 + Firestore/Protos/CMakeLists.txt | 45 +++++++ Firestore/Protos/nanopb/google/protobuf/any.pb.c | 36 +++++ Firestore/Protos/nanopb/google/protobuf/any.pb.h | 69 ++++++++++ Firestore/Protos/nanopb/google/protobuf/empty.pb.c | 34 +++++ Firestore/Protos/nanopb/google/protobuf/empty.pb.h | 66 +++++++++ .../Protos/nanopb/google/protobuf/wrappers.pb.c | 81 +++++++++++ .../Protos/nanopb/google/protobuf/wrappers.pb.h | 147 ++++++++++++++++++++ Firestore/Protos/protos/google/protobuf/any.proto | 149 +++++++++++++++++++++ .../Protos/protos/google/protobuf/empty.proto | 52 +++++++ .../Protos/protos/google/protobuf/wrappers.proto | 118 ++++++++++++++++ .../src/firebase/firestore/remote/CMakeLists.txt | 2 + .../src/firebase/firestore/remote/serializer.cc | 88 +++++++++++- .../src/firebase/firestore/remote/serializer.h | 112 +++++++++++++++- .../firebase/firestore/remote/serializer_test.cc | 71 +++++++++- cmake/external/grpc.cmake | 10 +- 16 files changed, 1069 insertions(+), 12 deletions(-) create mode 100644 Firestore/Protos/CMakeLists.txt create mode 100644 Firestore/Protos/nanopb/google/protobuf/any.pb.c create mode 100644 Firestore/Protos/nanopb/google/protobuf/any.pb.h create mode 100644 Firestore/Protos/nanopb/google/protobuf/empty.pb.c create mode 100644 Firestore/Protos/nanopb/google/protobuf/empty.pb.h create mode 100644 Firestore/Protos/nanopb/google/protobuf/wrappers.pb.c create mode 100644 Firestore/Protos/nanopb/google/protobuf/wrappers.pb.h create mode 100644 Firestore/Protos/protos/google/protobuf/any.proto create mode 100644 Firestore/Protos/protos/google/protobuf/empty.proto create mode 100644 Firestore/Protos/protos/google/protobuf/wrappers.proto diff --git a/Firestore/CMakeLists.txt b/Firestore/CMakeLists.txt index 25f27fa..28ac08f 100644 --- a/Firestore/CMakeLists.txt +++ b/Firestore/CMakeLists.txt @@ -68,3 +68,4 @@ include_directories(${FIREBASE_SOURCE_DIR}) include_directories(${FIREBASE_SOURCE_DIR}/Firestore/Protos/nanopb) add_subdirectory(core) +add_subdirectory(Protos) diff --git a/Firestore/Protos/CMakeLists.txt b/Firestore/Protos/CMakeLists.txt new file mode 100644 index 0000000..f20f702 --- /dev/null +++ b/Firestore/Protos/CMakeLists.txt @@ -0,0 +1,45 @@ +cc_library( + firebase_firestore_protos_nanopb + SOURCES + nanopb/firestore/local/maybe_document.pb.c + nanopb/firestore/local/maybe_document.pb.h + nanopb/firestore/local/mutation.pb.c + nanopb/firestore/local/mutation.pb.h + nanopb/firestore/local/target.pb.c + nanopb/firestore/local/target.pb.h + nanopb/google/api/annotations.pb.c + nanopb/google/api/annotations.pb.h + nanopb/google/api/http.pb.c + nanopb/google/api/http.pb.h + nanopb/google/firestore/v1beta1/common.pb.c + nanopb/google/firestore/v1beta1/common.pb.h + nanopb/google/firestore/v1beta1/document.pb.c + nanopb/google/firestore/v1beta1/document.pb.h + nanopb/google/firestore/v1beta1/firestore.pb.c + nanopb/google/firestore/v1beta1/firestore.pb.h + nanopb/google/firestore/v1beta1/query.pb.c + nanopb/google/firestore/v1beta1/query.pb.h + nanopb/google/firestore/v1beta1/write.pb.c + nanopb/google/firestore/v1beta1/write.pb.h + nanopb/google/protobuf/any.pb.c + nanopb/google/protobuf/any.pb.h + nanopb/google/protobuf/empty.pb.c + nanopb/google/protobuf/empty.pb.h + nanopb/google/protobuf/struct.pb.c + nanopb/google/protobuf/struct.pb.h + nanopb/google/protobuf/timestamp.pb.c + nanopb/google/protobuf/timestamp.pb.h + nanopb/google/protobuf/wrappers.pb.c + nanopb/google/protobuf/wrappers.pb.h + nanopb/google/rpc/status.pb.c + nanopb/google/rpc/status.pb.h + nanopb/google/type/latlng.pb.c + nanopb/google/type/latlng.pb.h + DEPENDS + nanopb +) + +target_compile_definitions( + firebase_firestore_protos_nanopb PUBLIC + -DPB_FIELD_16BIT +) diff --git a/Firestore/Protos/nanopb/google/protobuf/any.pb.c b/Firestore/Protos/nanopb/google/protobuf/any.pb.c new file mode 100644 index 0000000..b28d0ba --- /dev/null +++ b/Firestore/Protos/nanopb/google/protobuf/any.pb.c @@ -0,0 +1,36 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.8 at Mon Feb 12 11:03:06 2018. */ + +#include "any.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t google_protobuf_Any_fields[3] = { + PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_protobuf_Any, type_url, type_url, 0), + PB_FIELD( 2, BYTES , SINGULAR, CALLBACK, OTHER, google_protobuf_Any, value, type_url, 0), + PB_LAST_FIELD +}; + + +/* @@protoc_insertion_point(eof) */ diff --git a/Firestore/Protos/nanopb/google/protobuf/any.pb.h b/Firestore/Protos/nanopb/google/protobuf/any.pb.h new file mode 100644 index 0000000..10a722e --- /dev/null +++ b/Firestore/Protos/nanopb/google/protobuf/any.pb.h @@ -0,0 +1,69 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.8 at Mon Feb 12 11:03:06 2018. */ + +#ifndef PB_GOOGLE_PROTOBUF_ANY_PB_H_INCLUDED +#define PB_GOOGLE_PROTOBUF_ANY_PB_H_INCLUDED +#include + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Struct definitions */ +typedef struct _google_protobuf_Any { + pb_callback_t type_url; + pb_callback_t value; +/* @@protoc_insertion_point(struct:google_protobuf_Any) */ +} google_protobuf_Any; + +/* Default values for struct fields */ + +/* Initializer values for message structs */ +#define google_protobuf_Any_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define google_protobuf_Any_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} + +/* Field tags (for use in manual encoding/decoding) */ +#define google_protobuf_Any_type_url_tag 1 +#define google_protobuf_Any_value_tag 2 + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t google_protobuf_Any_fields[3]; + +/* Maximum encoded size of messages (where known) */ +/* google_protobuf_Any_size depends on runtime parameters */ + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define ANY_MESSAGES \ + + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/Firestore/Protos/nanopb/google/protobuf/empty.pb.c b/Firestore/Protos/nanopb/google/protobuf/empty.pb.c new file mode 100644 index 0000000..050af9c --- /dev/null +++ b/Firestore/Protos/nanopb/google/protobuf/empty.pb.c @@ -0,0 +1,34 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.8 at Mon Feb 12 11:03:06 2018. */ + +#include "empty.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t google_protobuf_Empty_fields[1] = { + PB_LAST_FIELD +}; + + +/* @@protoc_insertion_point(eof) */ diff --git a/Firestore/Protos/nanopb/google/protobuf/empty.pb.h b/Firestore/Protos/nanopb/google/protobuf/empty.pb.h new file mode 100644 index 0000000..466e1fd --- /dev/null +++ b/Firestore/Protos/nanopb/google/protobuf/empty.pb.h @@ -0,0 +1,66 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.8 at Mon Feb 12 11:03:06 2018. */ + +#ifndef PB_GOOGLE_PROTOBUF_EMPTY_PB_H_INCLUDED +#define PB_GOOGLE_PROTOBUF_EMPTY_PB_H_INCLUDED +#include + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Struct definitions */ +typedef struct _google_protobuf_Empty { + char dummy_field; +/* @@protoc_insertion_point(struct:google_protobuf_Empty) */ +} google_protobuf_Empty; + +/* Default values for struct fields */ + +/* Initializer values for message structs */ +#define google_protobuf_Empty_init_default {0} +#define google_protobuf_Empty_init_zero {0} + +/* Field tags (for use in manual encoding/decoding) */ + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t google_protobuf_Empty_fields[1]; + +/* Maximum encoded size of messages (where known) */ +#define google_protobuf_Empty_size 0 + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define EMPTY_MESSAGES \ + + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/Firestore/Protos/nanopb/google/protobuf/wrappers.pb.c b/Firestore/Protos/nanopb/google/protobuf/wrappers.pb.c new file mode 100644 index 0000000..41ab3c6 --- /dev/null +++ b/Firestore/Protos/nanopb/google/protobuf/wrappers.pb.c @@ -0,0 +1,81 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.8 at Mon Feb 12 11:03:06 2018. */ + +#include "wrappers.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t google_protobuf_DoubleValue_fields[2] = { + PB_FIELD( 1, DOUBLE , SINGULAR, STATIC , FIRST, google_protobuf_DoubleValue, value, value, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_protobuf_FloatValue_fields[2] = { + PB_FIELD( 1, FLOAT , SINGULAR, STATIC , FIRST, google_protobuf_FloatValue, value, value, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_protobuf_Int64Value_fields[2] = { + PB_FIELD( 1, INT64 , SINGULAR, STATIC , FIRST, google_protobuf_Int64Value, value, value, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_protobuf_UInt64Value_fields[2] = { + PB_FIELD( 1, UINT64 , SINGULAR, STATIC , FIRST, google_protobuf_UInt64Value, value, value, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_protobuf_Int32Value_fields[2] = { + PB_FIELD( 1, INT32 , SINGULAR, STATIC , FIRST, google_protobuf_Int32Value, value, value, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_protobuf_UInt32Value_fields[2] = { + PB_FIELD( 1, UINT32 , SINGULAR, STATIC , FIRST, google_protobuf_UInt32Value, value, value, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_protobuf_BoolValue_fields[2] = { + PB_FIELD( 1, BOOL , SINGULAR, STATIC , FIRST, google_protobuf_BoolValue, value, value, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_protobuf_StringValue_fields[2] = { + PB_FIELD( 1, STRING , SINGULAR, CALLBACK, FIRST, google_protobuf_StringValue, value, value, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_protobuf_BytesValue_fields[2] = { + PB_FIELD( 1, BYTES , SINGULAR, CALLBACK, FIRST, google_protobuf_BytesValue, value, value, 0), + PB_LAST_FIELD +}; + + +/* On some platforms (such as AVR), double is really float. + * These are not directly supported by nanopb, but see example_avr_double. + * To get rid of this error, remove any double fields from your .proto. + */ +PB_STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES) + +/* @@protoc_insertion_point(eof) */ diff --git a/Firestore/Protos/nanopb/google/protobuf/wrappers.pb.h b/Firestore/Protos/nanopb/google/protobuf/wrappers.pb.h new file mode 100644 index 0000000..0e98785 --- /dev/null +++ b/Firestore/Protos/nanopb/google/protobuf/wrappers.pb.h @@ -0,0 +1,147 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.8 at Mon Feb 12 11:03:06 2018. */ + +#ifndef PB_GOOGLE_PROTOBUF_WRAPPERS_PB_H_INCLUDED +#define PB_GOOGLE_PROTOBUF_WRAPPERS_PB_H_INCLUDED +#include + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Struct definitions */ +typedef struct _google_protobuf_BytesValue { + pb_callback_t value; +/* @@protoc_insertion_point(struct:google_protobuf_BytesValue) */ +} google_protobuf_BytesValue; + +typedef struct _google_protobuf_StringValue { + pb_callback_t value; +/* @@protoc_insertion_point(struct:google_protobuf_StringValue) */ +} google_protobuf_StringValue; + +typedef struct _google_protobuf_BoolValue { + bool value; +/* @@protoc_insertion_point(struct:google_protobuf_BoolValue) */ +} google_protobuf_BoolValue; + +typedef struct _google_protobuf_DoubleValue { + double value; +/* @@protoc_insertion_point(struct:google_protobuf_DoubleValue) */ +} google_protobuf_DoubleValue; + +typedef struct _google_protobuf_FloatValue { + float value; +/* @@protoc_insertion_point(struct:google_protobuf_FloatValue) */ +} google_protobuf_FloatValue; + +typedef struct _google_protobuf_Int32Value { + int32_t value; +/* @@protoc_insertion_point(struct:google_protobuf_Int32Value) */ +} google_protobuf_Int32Value; + +typedef struct _google_protobuf_Int64Value { + int64_t value; +/* @@protoc_insertion_point(struct:google_protobuf_Int64Value) */ +} google_protobuf_Int64Value; + +typedef struct _google_protobuf_UInt32Value { + uint32_t value; +/* @@protoc_insertion_point(struct:google_protobuf_UInt32Value) */ +} google_protobuf_UInt32Value; + +typedef struct _google_protobuf_UInt64Value { + uint64_t value; +/* @@protoc_insertion_point(struct:google_protobuf_UInt64Value) */ +} google_protobuf_UInt64Value; + +/* Default values for struct fields */ + +/* Initializer values for message structs */ +#define google_protobuf_DoubleValue_init_default {0} +#define google_protobuf_FloatValue_init_default {0} +#define google_protobuf_Int64Value_init_default {0} +#define google_protobuf_UInt64Value_init_default {0} +#define google_protobuf_Int32Value_init_default {0} +#define google_protobuf_UInt32Value_init_default {0} +#define google_protobuf_BoolValue_init_default {0} +#define google_protobuf_StringValue_init_default {{{NULL}, NULL}} +#define google_protobuf_BytesValue_init_default {{{NULL}, NULL}} +#define google_protobuf_DoubleValue_init_zero {0} +#define google_protobuf_FloatValue_init_zero {0} +#define google_protobuf_Int64Value_init_zero {0} +#define google_protobuf_UInt64Value_init_zero {0} +#define google_protobuf_Int32Value_init_zero {0} +#define google_protobuf_UInt32Value_init_zero {0} +#define google_protobuf_BoolValue_init_zero {0} +#define google_protobuf_StringValue_init_zero {{{NULL}, NULL}} +#define google_protobuf_BytesValue_init_zero {{{NULL}, NULL}} + +/* Field tags (for use in manual encoding/decoding) */ +#define google_protobuf_BytesValue_value_tag 1 +#define google_protobuf_StringValue_value_tag 1 +#define google_protobuf_BoolValue_value_tag 1 +#define google_protobuf_DoubleValue_value_tag 1 +#define google_protobuf_FloatValue_value_tag 1 +#define google_protobuf_Int32Value_value_tag 1 +#define google_protobuf_Int64Value_value_tag 1 +#define google_protobuf_UInt32Value_value_tag 1 +#define google_protobuf_UInt64Value_value_tag 1 + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t google_protobuf_DoubleValue_fields[2]; +extern const pb_field_t google_protobuf_FloatValue_fields[2]; +extern const pb_field_t google_protobuf_Int64Value_fields[2]; +extern const pb_field_t google_protobuf_UInt64Value_fields[2]; +extern const pb_field_t google_protobuf_Int32Value_fields[2]; +extern const pb_field_t google_protobuf_UInt32Value_fields[2]; +extern const pb_field_t google_protobuf_BoolValue_fields[2]; +extern const pb_field_t google_protobuf_StringValue_fields[2]; +extern const pb_field_t google_protobuf_BytesValue_fields[2]; + +/* Maximum encoded size of messages (where known) */ +#define google_protobuf_DoubleValue_size 9 +#define google_protobuf_FloatValue_size 5 +#define google_protobuf_Int64Value_size 11 +#define google_protobuf_UInt64Value_size 11 +#define google_protobuf_Int32Value_size 11 +#define google_protobuf_UInt32Value_size 6 +#define google_protobuf_BoolValue_size 2 +/* google_protobuf_StringValue_size depends on runtime parameters */ +/* google_protobuf_BytesValue_size depends on runtime parameters */ + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define WRAPPERS_MESSAGES \ + + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/Firestore/Protos/protos/google/protobuf/any.proto b/Firestore/Protos/protos/google/protobuf/any.proto new file mode 100644 index 0000000..c748667 --- /dev/null +++ b/Firestore/Protos/protos/google/protobuf/any.proto @@ -0,0 +1,149 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option go_package = "github.com/golang/protobuf/ptypes/any"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "AnyProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; + +// `Any` contains an arbitrary serialized protocol buffer message along with a +// URL that describes the type of the serialized message. +// +// Protobuf library provides support to pack/unpack Any values in the form +// of utility functions or additional generated methods of the Any type. +// +// Example 1: Pack and unpack a message in C++. +// +// Foo foo = ...; +// Any any; +// any.PackFrom(foo); +// ... +// if (any.UnpackTo(&foo)) { +// ... +// } +// +// Example 2: Pack and unpack a message in Java. +// +// Foo foo = ...; +// Any any = Any.pack(foo); +// ... +// if (any.is(Foo.class)) { +// foo = any.unpack(Foo.class); +// } +// +// Example 3: Pack and unpack a message in Python. +// +// foo = Foo(...) +// any = Any() +// any.Pack(foo) +// ... +// if any.Is(Foo.DESCRIPTOR): +// any.Unpack(foo) +// ... +// +// Example 4: Pack and unpack a message in Go +// +// foo := &pb.Foo{...} +// any, err := ptypes.MarshalAny(foo) +// ... +// foo := &pb.Foo{} +// if err := ptypes.UnmarshalAny(any, foo); err != nil { +// ... +// } +// +// The pack methods provided by protobuf library will by default use +// 'type.googleapis.com/full.type.name' as the type URL and the unpack +// methods only use the fully qualified type name after the last '/' +// in the type URL, for example "foo.bar.com/x/y.z" will yield type +// name "y.z". +// +// +// JSON +// ==== +// The JSON representation of an `Any` value uses the regular +// representation of the deserialized, embedded message, with an +// additional field `@type` which contains the type URL. Example: +// +// package google.profile; +// message Person { +// string first_name = 1; +// string last_name = 2; +// } +// +// { +// "@type": "type.googleapis.com/google.profile.Person", +// "firstName": , +// "lastName": +// } +// +// If the embedded message type is well-known and has a custom JSON +// representation, that representation will be embedded adding a field +// `value` which holds the custom JSON in addition to the `@type` +// field. Example (for message [google.protobuf.Duration][]): +// +// { +// "@type": "type.googleapis.com/google.protobuf.Duration", +// "value": "1.212s" +// } +// +message Any { + // A URL/resource name whose content describes the type of the + // serialized protocol buffer message. + // + // For URLs which use the scheme `http`, `https`, or no scheme, the + // following restrictions and interpretations apply: + // + // * If no scheme is provided, `https` is assumed. + // * The last segment of the URL's path must represent the fully + // qualified name of the type (as in `path/google.protobuf.Duration`). + // The name should be in a canonical form (e.g., leading "." is + // not accepted). + // * An HTTP GET on the URL must yield a [google.protobuf.Type][] + // value in binary format, or produce an error. + // * Applications are allowed to cache lookup results based on the + // URL, or have them precompiled into a binary to avoid any + // lookup. Therefore, binary compatibility needs to be preserved + // on changes to types. (Use versioned type names to manage + // breaking changes.) + // + // Schemes other than `http`, `https` (or the empty scheme) might be + // used with implementation specific semantics. + // + string type_url = 1; + + // Must be a valid serialized protocol buffer of the above specified type. + bytes value = 2; +} diff --git a/Firestore/Protos/protos/google/protobuf/empty.proto b/Firestore/Protos/protos/google/protobuf/empty.proto new file mode 100644 index 0000000..03cacd2 --- /dev/null +++ b/Firestore/Protos/protos/google/protobuf/empty.proto @@ -0,0 +1,52 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option go_package = "github.com/golang/protobuf/ptypes/empty"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "EmptyProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option cc_enable_arenas = true; + +// A generic empty message that you can re-use to avoid defining duplicated +// empty messages in your APIs. A typical example is to use it as the request +// or the response type of an API method. For instance: +// +// service Foo { +// rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); +// } +// +// The JSON representation for `Empty` is empty JSON object `{}`. +message Empty {} diff --git a/Firestore/Protos/protos/google/protobuf/wrappers.proto b/Firestore/Protos/protos/google/protobuf/wrappers.proto new file mode 100644 index 0000000..0194763 --- /dev/null +++ b/Firestore/Protos/protos/google/protobuf/wrappers.proto @@ -0,0 +1,118 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Wrappers for primitive (non-message) types. These types are useful +// for embedding primitives in the `google.protobuf.Any` type and for places +// where we need to distinguish between the absence of a primitive +// typed field and its default value. + +syntax = "proto3"; + +package google.protobuf; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option cc_enable_arenas = true; +option go_package = "github.com/golang/protobuf/ptypes/wrappers"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "WrappersProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; + +// Wrapper message for `double`. +// +// The JSON representation for `DoubleValue` is JSON number. +message DoubleValue { + // The double value. + double value = 1; +} + +// Wrapper message for `float`. +// +// The JSON representation for `FloatValue` is JSON number. +message FloatValue { + // The float value. + float value = 1; +} + +// Wrapper message for `int64`. +// +// The JSON representation for `Int64Value` is JSON string. +message Int64Value { + // The int64 value. + int64 value = 1; +} + +// Wrapper message for `uint64`. +// +// The JSON representation for `UInt64Value` is JSON string. +message UInt64Value { + // The uint64 value. + uint64 value = 1; +} + +// Wrapper message for `int32`. +// +// The JSON representation for `Int32Value` is JSON number. +message Int32Value { + // The int32 value. + int32 value = 1; +} + +// Wrapper message for `uint32`. +// +// The JSON representation for `UInt32Value` is JSON number. +message UInt32Value { + // The uint32 value. + uint32 value = 1; +} + +// Wrapper message for `bool`. +// +// The JSON representation for `BoolValue` is JSON `true` and `false`. +message BoolValue { + // The bool value. + bool value = 1; +} + +// Wrapper message for `string`. +// +// The JSON representation for `StringValue` is JSON string. +message StringValue { + // The string value. + string value = 1; +} + +// Wrapper message for `bytes`. +// +// The JSON representation for `BytesValue` is JSON string. +message BytesValue { + // The bytes value. + bytes value = 1; +} diff --git a/Firestore/core/src/firebase/firestore/remote/CMakeLists.txt b/Firestore/core/src/firebase/firestore/remote/CMakeLists.txt index a218e3b..7f528fb 100644 --- a/Firestore/core/src/firebase/firestore/remote/CMakeLists.txt +++ b/Firestore/core/src/firebase/firestore/remote/CMakeLists.txt @@ -20,6 +20,8 @@ cc_library( serializer.h serializer.cc DEPENDS + firebase_firestore_model + firebase_firestore_protos_nanopb grpc::grpc nanopb ) diff --git a/Firestore/core/src/firebase/firestore/remote/serializer.cc b/Firestore/core/src/firebase/firestore/remote/serializer.cc index d3cdd3f..e503d09 100644 --- a/Firestore/core/src/firebase/firestore/remote/serializer.cc +++ b/Firestore/core/src/firebase/firestore/remote/serializer.cc @@ -16,16 +16,94 @@ #include "Firestore/core/src/firebase/firestore/remote/serializer.h" -// TODO(rsgowman): These are (currently!) unnecessary includes. Adding for now -// to ensure we can find nanopb's generated header files. -#include "Firestore/Protos/nanopb/google/firestore/v1beta1/document.pb.h" -#include "Firestore/Protos/nanopb/google/protobuf/timestamp.pb.h" +#include +#include namespace firebase { namespace firestore { namespace remote { -Serializer::Serializer() { +using firebase::firestore::model::FieldValue; + +Serializer::TypedValue Serializer::EncodeFieldValue( + const FieldValue& field_value) { + Serializer::TypedValue proto_value{ + field_value.type(), google_firestore_v1beta1_Value_init_default}; + switch (field_value.type()) { + case FieldValue::Type::Null: + proto_value.value.null_value = google_protobuf_NullValue_NULL_VALUE; + break; + default: + // TODO(rsgowman): implement the other types + abort(); + } + return proto_value; +} + +void Serializer::EncodeTypedValue(const TypedValue& value, + std::vector* out_bytes) { + bool status; + // TODO(rsgowman): how large should the output buffer be? Do some + // investigation to see if we can get nanopb to tell us how much space it's + // going to need. + uint8_t buf[1024]; + pb_ostream_t stream = pb_ostream_from_buffer(buf, sizeof(buf)); + switch (value.type) { + case FieldValue::Type::Null: + status = pb_encode_tag(&stream, PB_WT_VARINT, + google_firestore_v1beta1_Value_null_value_tag); + if (!status) { + // TODO(rsgowman): figure out error handling + abort(); + } + + status = pb_encode_varint(&stream, value.value.null_value); + if (!status) { + // TODO(rsgowman): figure out error handling + abort(); + } + + out_bytes->insert(out_bytes->end(), buf, buf + stream.bytes_written); + + break; + + default: + // TODO(rsgowman): implement the other types + abort(); + } +} + +FieldValue Serializer::DecodeFieldValue( + const Serializer::TypedValue& value_proto) { + switch (value_proto.type) { + case FieldValue::Type::Null: + return FieldValue::NullValue(); + default: + // TODO(rsgowman): implement the other types + abort(); + } +} + +Serializer::TypedValue Serializer::DecodeTypedValue(const uint8_t* bytes, + size_t length) { + pb_istream_t stream = pb_istream_from_buffer(bytes, length); + pb_wire_type_t wire_type; + uint32_t tag; + bool eof; + bool status = pb_decode_tag(&stream, &wire_type, &tag, &eof); + if (!status || wire_type != PB_WT_VARINT) { + // TODO(rsgowman): figure out error handling + abort(); + } + + switch (tag) { + case google_firestore_v1beta1_Value_null_value_tag: + return Serializer::TypedValue{ + FieldValue::Type::Null, google_firestore_v1beta1_Value_init_default}; + default: + // TODO(rsgowman): figure out error handling + abort(); + } } } // namespace remote diff --git a/Firestore/core/src/firebase/firestore/remote/serializer.h b/Firestore/core/src/firebase/firestore/remote/serializer.h index 4dc6b9e..518cff4 100644 --- a/Firestore/core/src/firebase/firestore/remote/serializer.h +++ b/Firestore/core/src/firebase/firestore/remote/serializer.h @@ -17,16 +17,26 @@ #ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_REMOTE_SERIALIZER_H_ #define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_REMOTE_SERIALIZER_H_ +#include +#include +#include + +#include "Firestore/Protos/nanopb/google/firestore/v1beta1/document.pb.h" +#include "Firestore/core/src/firebase/firestore/model/field_value.h" +#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" + namespace firebase { namespace firestore { namespace remote { /** * @brief Converts internal model objects to their equivalent protocol buffer - * form. + * form, and protocol buffer objects to their equivalent bytes. * - * Methods starting with "Encode" convert to a protocol buffer and methods - * starting with "Decode" convert from a protocol buffer. + * Methods starting with "Encode" either convert from a model object to a + * protocol buffer or from a protocol buffer to bytes, and methods starting with + * "Decode" either convert from a protocol buffer to a model object or from + * bytes to a protocol buffer. * */ // TODO(rsgowman): Original docs also has this: "Throws an exception if a @@ -34,10 +44,102 @@ namespace remote { // interpret." Adjust for C++. class Serializer { public: - // TODO(rsgowman): Adjust ctor to accept a DatabaseID... once that exists. - Serializer(/*DatabaseID databaseId*/); + /** + * @brief Wraps (nanopb) google_firestore_v1beta1_Value with type information. + */ + struct TypedValue { + firebase::firestore::model::FieldValue::Type type; + google_firestore_v1beta1_Value value; + }; + + Serializer() { + } + // TODO(rsgowman): We eventually need the DatabaseId, but can't add it just + // yet since it's not used yet (which travis complains about). So for now, + // we'll create a parameterless ctor (above) that likely won't exist in the + // final version of this class. + ///** + // * @param database_id Must remain valid for the lifetime of this Serializer + // * object. + // */ + // explicit Serializer(const firebase::firestore::model::DatabaseId& + // database_id) + // : database_id_(database_id) { + //} + + /** + * Converts the FieldValue model passed into the Value proto equivalent. + * + * @param field_value the model to convert. + * @return the proto representation of the model. + */ + static Serializer::TypedValue EncodeFieldValue( + const firebase::firestore::model::FieldValue& field_value); + + /** + * @brief Converts the value proto passed into bytes. + * + * @param[out] out_bytes A buffer to place the output. The bytes will be + * appended to this vector. + */ + // TODO(rsgowman): error handling, incl return code. + static void EncodeTypedValue(const TypedValue& value, + std::vector* out_bytes); + + /** + * Converts from the proto Value format to the model FieldValue format + * + * @return The model equivalent of the proto data. + */ + static firebase::firestore::model::FieldValue DecodeFieldValue( + const Serializer::TypedValue& value_proto); + + /** + * @brief Converts from bytes to the nanopb proto. + * + * @param bytes The bytes to convert. It's assumed that exactly all of the + * bytes will be used by this conversion. + * @return The (nanopb) proto equivalent of the bytes. + */ + // TODO(rsgowman): error handling. + static TypedValue DecodeTypedValue(const uint8_t* bytes, size_t length); + + /** + * @brief Converts from bytes to the nanopb proto. + * + * @param bytes The bytes to convert. It's assumed that exactly all of the + * bytes will be used by this conversion. + * @return The (nanopb) proto equivalent of the bytes. + */ + // TODO(rsgowman): error handling. + static TypedValue DecodeTypedValue(const std::vector& bytes) { + return DecodeTypedValue(bytes.data(), bytes.size()); + } + + private: + // TODO(rsgowman): We don't need the database_id_ yet (but will eventually). + // const firebase::firestore::model::DatabaseId& database_id_; }; +inline bool operator==(const Serializer::TypedValue& lhs, + const Serializer::TypedValue& rhs) { + if (lhs.type != rhs.type) { + return false; + } + + switch (lhs.type) { + case firebase::firestore::model::FieldValue::Type::Null: + FIREBASE_DEV_ASSERT(lhs.value.null_value == + google_protobuf_NullValue_NULL_VALUE); + FIREBASE_DEV_ASSERT(rhs.value.null_value == + google_protobuf_NullValue_NULL_VALUE); + return true; + default: + // TODO(rsgowman): implement the other types + abort(); + } +} + } // namespace remote } // namespace firestore } // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/remote/serializer_test.cc b/Firestore/core/test/firebase/firestore/remote/serializer_test.cc index 1be5a87..35f417e 100644 --- a/Firestore/core/test/firebase/firestore/remote/serializer_test.cc +++ b/Firestore/core/test/firebase/firestore/remote/serializer_test.cc @@ -16,8 +16,15 @@ #include "Firestore/core/src/firebase/firestore/remote/serializer.h" -#include +#include #include +#include + +#include "Firestore/core/src/firebase/firestore/model/field_value.h" +#include "gtest/gtest.h" + +using firebase::firestore::model::FieldValue; +using firebase::firestore::remote::Serializer; TEST(Serializer, CanLinkToNanopb) { // This test doesn't actually do anything interesting as far as actually using @@ -26,3 +33,65 @@ TEST(Serializer, CanLinkToNanopb) { // the test. pb_ostream_from_buffer(NULL, 0); } + +// Fixture for running serializer tests. +class SerializerTest : public ::testing::Test { + public: + SerializerTest() : serializer(/*DatabaseId("p", "d")*/) { + } + Serializer serializer; + + void ExpectRoundTrip(const FieldValue& model, + const Serializer::TypedValue& proto, + FieldValue::Type type) { + EXPECT_EQ(type, model.type()); + EXPECT_EQ(type, proto.type); + Serializer::TypedValue actual_proto = serializer.EncodeFieldValue(model); + EXPECT_EQ(type, actual_proto.type); + EXPECT_EQ(proto, actual_proto); + EXPECT_EQ(model, serializer.DecodeFieldValue(proto)); + } + + void ExpectRoundTrip(const Serializer::TypedValue& proto, + std::vector bytes, + FieldValue::Type type) { + EXPECT_EQ(type, proto.type); + std::vector actual_bytes; + Serializer::EncodeTypedValue(proto, &actual_bytes); + EXPECT_EQ(bytes, actual_bytes); + Serializer::TypedValue actual_proto = Serializer::DecodeTypedValue(bytes); + EXPECT_EQ(type, actual_proto.type); + EXPECT_EQ(proto, actual_proto); + } +}; + +TEST_F(SerializerTest, EncodesNullModelToProto) { + FieldValue model = FieldValue::NullValue(); + Serializer::TypedValue proto{FieldValue::Type::Null, + google_firestore_v1beta1_Value_init_default}; + // sanity check (the _init_default above should set this to _NULL_VALUE) + EXPECT_EQ(google_protobuf_NullValue_NULL_VALUE, proto.value.null_value); + ExpectRoundTrip(model, proto, FieldValue::Type::Null); +} + +TEST_F(SerializerTest, EncodesNullProtoToBytes) { + Serializer::TypedValue proto{FieldValue::Type::Null, + google_firestore_v1beta1_Value_init_default}; + // sanity check (the _init_default above should set this to _NULL_VALUE) + EXPECT_EQ(google_protobuf_NullValue_NULL_VALUE, proto.value.null_value); + + /* NB: proto bytes were created via: + echo 'null_value: NULL_VALUE' \ + | ./build/external/protobuf/src/protobuf-build/src/protoc \ + -I./Firestore/Protos/protos \ + -I./build/external/protobuf/src/protobuf/src/ \ + --encode=google.firestore.v1beta1.Value \ + google/firestore/v1beta1/document.proto \ + > output.bin + */ + std::vector bytes{0x58, 0x00}; + ExpectRoundTrip(proto, bytes, FieldValue::Type::Null); +} + +// TODO(rsgowman): Test [en|de]coding multiple protos into the same output +// vector. diff --git a/cmake/external/grpc.cmake b/cmake/external/grpc.cmake index 0af6b31..d545087 100644 --- a/cmake/external/grpc.cmake +++ b/cmake/external/grpc.cmake @@ -68,7 +68,15 @@ else() PREFIX ${PROJECT_BINARY_DIR}/external/grpc - CMAKE_ARGS ${CMAKE_ARGS} + # TODO(rsgowman): We're currently building nanopb twice; once via grpc, and + # once via nanopb. The version from grpc is the one that actually ends up + # being used. We need to fix this such that either: + # a) we instruct grpc to use our nanopb + # b) we rely on grpc's nanopb instead of using our own. + # For now, we'll pass in the necessary nanopb cflags into grpc. (We require + # 16 bit fields. Without explicitly requesting this, nanopb uses 8 bit + # fields.) + CMAKE_ARGS ${CMAKE_ARGS};-DCMAKE_C_FLAGS=-DPB_FIELD_16BIT;DCMAKE_CXX_FLAGS=-DPB_FIELD_16BIT BUILD_COMMAND ${CMAKE_COMMAND} --build . --target grpc -- cgit v1.2.3