© P.J. McNerney 2020
P. McNerneyBeginning Bazelhttps://doi.org/10.1007/978-1-4842-5194-2_6

6. Protocol Buffers and Bazel

P. J. McNerney1 
(1)
Blackhawk, CO, USA
 

In the last chapter, you created a simple echo server and client, demonstrating some of the power of Bazel to navigate and manage multiple languages with minimal setup. A noted shortcoming from that example stems from the definition of the transmitted object: both languages required independent definitions of the object. Over time, this easily can cause a literal breakdown in communication as two (or more) definitions of the transmitted object drift out of sync.

In this chapter, we are going to introduce a construct to handle this very problem, the Protocol Buffer (often referred to as protobuf). Yet another creation from Google, Protocol Buffers provide a way to describe the structure of objects in a declarative and type-safe fashion and provide a wire format for serialization. Protocol Buffer definitions are intrinsically language agnostic. Once created, a Protocol Buffer definition can then be compiled into a particular language (there is vast language support for Protocol Buffers) in order to read/write from the wire format into a language native object.

While Protocol Buffers are not wedded to Bazel per se, Bazel provides some fantastic support for them, making it easy to add them to a project and make use of them across multiple languages. In addition, due to Bazel’s dependency management, it is also very easy to make a change at the Protocol Buffer definition and ensure that all the dependent projects can at least compile against the new definition.

Setting Up Your Workspace

We will start first with adding some very basic support to a WORKSPACE file for working with Protocol Buffers. Let’s create a new directory for our work:
$ mkdir chapter_06
$ cd chapter_06
chapter_06$ touch WORKSPACE
Now let’s pull in the rules for working with Protocol Buffers. Open your WORKSPACE file and add the following.
http_archive(
    name = "rules_proto",
    strip_prefix = "rules_proto-97d8af4dc474595af3900dd85cb3a29ad28cc313",
    urls = ["https://github.com/bazelbuild/rules_proto/archive/97d8af4dc474595af3900dd85cb3a29ad28cc313.tar.gz",],
)
load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains")
rules_proto_dependencies()
rules_proto_toolchains()
Listing 6-1

Adding support for Protocol Buffers

Save your WORKSPACE file.
As should be familiar from prior chapters, we are first retrieving the Bazel repository with the required functionality and then calling setup code particular to that repository (i.e., rules_proto_dependencies() and rules_proto_toolchains()).

Creating Your First Protocol Buffer

Having completed setup, we are ready to start creating some Protocol Buffers. Let’s create a directory for our code and an initial file for our Protocol Buffer definition:
chapter_06$ mkdir src
chapter_06$ cd src
chapter_06/src$ touch transmission_object.proto

Note

An astute reader will note that in the prior chapter, we had started off with creating our BUILD file before working with the code. This switch here is intentional, and we will address it very shortly.

Let’s create a basic message for transmission. Open your transmission_object.proto file and add the following.
syntax = "proto3";
package transmission_object;
message TransmissionObject {
    float value = 1;
    string message = 2;
}
Listing 6-2

Defining a simple Protocol Buffer

Save that to transmission_object.proto.

Let’s take a brief moment to examine what we have written. The initial line (syntax) is used to specify to the compiler what version of Protocol Buffers is being used here (at the time of this writing, the latest version of Protocol Buffers is version 3).

The following line (package) is used to define the conceptual package that this Protocol Buffer is defined within. This is very similar to the Java or Go notions of packages.

Finally, we have the definition of the message itself. This most closely resembles a C-style struct, with a name given to the object (TransmissionObject) and a basic set of type-specified fields (i.e., value and message, with types float and string, respectively). The one minor addition here is the addition of a field number; this is used to define unique identifiers to each of the members of the message. This is important since these are used to add and remove data members while keeping backward compatibility.

Now let’s create the build target for the Protocol Buffer. Add the following to your BUILD file .
load("@rules_proto//proto:defs.bzl", "proto_library")
proto_library(
    name = "transmission_object_proto",
    srcs = ["transmission_object.proto"],
)
Listing 6-3

Creating the build target for the Protocol Buffer

Save your BUILD file.

Note

Those already familiar with Bazel might find the explicit inclusion of proto_library to be strange. The proto_library rule used to be a part of the core of Bazel itself. This is an example of the evolution of Bazel by moving specific constructs out of the core and into explicit packages.

At this stage, we technically have enough to start building our Protocol Buffer, so let’s do that:
chapter_06/src$ bazel build :transmission_object_proto
INFO: Analysed target //src:transmission_object_proto (16 packages loaded, 624 targets configured).
INFO: Found 1 target...
Target //src:transmission_object_proto up-to-date:
  bazel-genfiles/src/transmission_object_proto-descriptor-set.proto.bin
INFO: Elapsed time: 80.442s, Critical Path: 24.91s
INFO: 184 processes: 184 darwin-sandbox.
INFO: Build completed successfully, 187 total actions

One of the first things that you should notice is that, for at least the first time you build the Protocol Buffer target, there is a noticeable delay compared to executions in prior chapters. In this case, the dependency for the Protocol Buffer compiler is both being pulled down and being compiled for your target machine. Once the protobuf compiler is itself compiled, then it can then compile your protobuf definition.

Don’t worry. This particular slowdown should only be limited to the first time you actually run the Protocol Buffer compiler; once created, the Protocol Buffer compiler will remain cached (until you change a dependency or execute bazel clean).

Using the Protocol Buffer in Java

Although we have successfully compiled the Protocol Buffer, all we really have done is create a language-agnostic descriptor for it; in order to really make use of it, we will need to create a language-specific target for it. We will start with creating one in Java. Once again, we take advantage of the fact that Java, being one of the built-in languages of Bazel, comes with support built-in for Java-based Protocol Buffers.

Creating the Java Proto Library Target

Open your BUILD file and add the following.
java_proto_library(
    name = "transmission_object_java_proto",
    deps = [":transmission_object_proto"],
)
Listing 6-4

Creating the Java Protocol Buffer library

Save the BUILD file. Let’s build the newly created target:
chapter_06/src$ bazel build :transmission_object_java_proto
INFO: Analysed target //src:transmission_object_java_proto (2 packages loaded, 350 targets configured).
INFO: Found 1 target...
Target //src:transmission_object_java_proto up-to-date:
  bazel-bin/src/libtransmission_object_proto-speed.jar
  bazel-genfiles/src/transmission_object_proto-speed-src.jar
INFO: Elapsed time: 0.703s, Critical Path: 0.45s
INFO: 2 processes: 1 darwin-sandbox, 1 worker.
INFO: Build completed successfully, 3 total actions

Congratulations! You have a target that we can actually use in a Java program.

Note

In this case, you did not need to explicitly load java_proto_library. However, given Bazel’s evolution, bear in mind that some future iteration may require you to explicitly load the rule.

Using Your Java Protocol Buffer Target

In the last chapter, you had created a simple Java echo client using JSON. Here, we will make use of almost the same code for the Protocol Buffer example, with only a few minor changes.

Create EchoClient.java within your src directory and add the following (changes from the prior chapter in bold).
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import transmission_object.TransmissionObjectOuterClass.TransmissionObject;
public class EchoClient {
    public static void main (String args[]) {
        System.out.println("Spinning up the Echo Client in Java...");
        try {
            final Socket socketToServer = new Socket("localhost", 1234);
            // Note we don't need the second BufferedReader here.
            final BufferedReader commandLineInput = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("Waiting on input from the user...");
            final String inputFromUser = commandLineInput.readLine();
            if (inputFromUser != null) {
                System.out.println("Received by Java: " + inputFromUser);
                TransmissionObject transmissionObject = TransmissionObject.                  newBuilder()
                      .setMessage(inputFromUser)
                      .setValue(3.145f)
                      .build();
                transmissionObject.writeTo(socketToServer.getOutputStream());
                TransmissionObject receivedObject = TransmissionObject.parseFrom(socketToServer.getInputStream());
                System.out.println("Received Message from server: ");
                System.out.println(receivedObject);
            }
            socketToServer.close();
        } catch (Exception e) {
            System.err.println("Error: " + e);
        }
    }
}
Listing 6-5

Protocol Buffer version of the echo client

Save this to EchoClient.java.

Let’s take a moment to examine the preceding code. The import statement that brings in our generated Protocol Buffer is comprised of three main components:
  • transmission_object
    • This is the package as specified within the original transmission_object.proto file.

  • TransmissionObjectOuterClass
    • This is a class generated to encapsulate any messages contained within the Protocol Buffer definition.

    • This is an artifact of Java’s one (outer)-class-per-file rule; technically, we could have had multiple messages within our Protocol Buffer file, but we can only have a single class within a Java file.

    • This allows us to create multiple Protocol Buffer messages for use in Java.

  • TransmissionObject
    • The actual Java object that represents the original Protocol Buffer message.

Within the code itself, the Java Protocol Buffer instance is created using a builder pattern, which allows you to set the various fields and then generate an invariant instance of the TransmissionObject. This object is then able to directly write itself to an output stream as well as parse itself from an input stream.

Finally, let’s create the build target so we can actually create our new version of EchoClient. Open your BUILD file and add the following (again, changes from last chapter in bold).
java_binary(
    name = "echo_client",
    srcs = ["EchoClient.java"],
    main_class = "EchoClient",
    deps = [":transmission_object_java_proto"],
)
Listing 6-6

Creating the BUILD target for the EchoClient

Now we can build the target:
chapter_06/src$ bazel build :echo_client
INFO: Analysed target //src:echo_client (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target //src:echo_client up-to-date:
  bazel-bin/src/echo_client.jar
  bazel-bin/src/echo_client
INFO: Elapsed time: 0.219s, Critical Path: 0.06s
INFO: 1 process: 1 worker.
INFO: Build completed successfully, 2 total actions

Congratulations! You’ve successfully updated your client to use Protocol Buffers. However, once again you have a client with nothing to connect to. Now, we will make the necessary changes to the server side to also handle our Protocol Buffer definition.

Note

One might be tempted to run this new version of our client against the last chapter’s server. Although you are welcome to do so, it is important to know that this will not work, since the client and server are talking into different protocols (JSON vs. Protocol Buffer); the bytes are going to be interpreted differently.

Although Protocol Buffers do support translation to/from JSON, you would need to explicitly specify that within the code.

Using the Protocol Buffer in Go

In the last section, we were able to take advantage of the fact that Java is one of the built-in languages of Bazel to jump right into development. However, since Go is not one of those core languages, we will need to do some additional setup. Fortunately, most of this will look familiar from prior chapters.

Open your WORKSPACE file and add the following in bold prior to the specification for retrieving rules_proto.
http_archive(
    name = "io_bazel_rules_go",
    urls = ["https://github.com/bazelbuild/rules_go/releases/download/v0.19.5/rules_go-v0.19.5.tar.gz"],
)
load("@io_bazel_rules_go//go:deps.bzl", "go_rules_dependencies", "go_register_toolchains")
go_rules_dependencies()
go_register_toolchains()
http_archive(
    name = "rules_proto",
    strip_prefix = "rules_proto-97d8af4dc474595af3900dd85cb3a29ad28cc313",
    urls = ["https://github.com/bazelbuild/rules_proto/archive/97d8af4dc474595af3900dd85cb3a29ad28cc313.tar.gz",],
)
load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains")
rules_proto_dependencies()
rules_proto_toolchains()
Listing 6-7

Adding the Go rules to the project

Save your WORKSPACE file.j

Note

In this particular instance, we specified to load io_bazel_rules_go prior to rules_proto. The reason is that there can be conflicts between the underlying dependencies between these two packages. Ordering them in this fashion removes the issue. However, this is an item to watch out for as you construct your WORKSPACE dependencies moving forward.

Creating the Go Proto Library Target

As with the inclusion of the Go functionality into our project, we will need to explicitly bring the necessary rules into our BUILD file as we create our Go proto library target.

Open your BUILD file and add the following.
load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
go_proto_library(
    name = "transmission_object_go_proto",
    proto = ":transmission_object_proto",
    importpath = "transmission_object"
)
Listing 6-8

Creating the Go proto library target

Save your BUILD file. Let’s build your new target:
chapter_06/src$ bazel build :transmission_object_go_proto
INFO: Analysed target //src:transmission_object_go_proto (21 packages loaded, 6358 targets configured).
INFO: Found 1 target...
...
Target //src:transmission_object_go_proto up-to-date:
  bazel-bin/src/darwin_amd64_stripped/transmission_object_go_proto%/transmission_object.a
INFO: Elapsed time: 5.486s, Critical Path: 3.75s
INFO: 52 processes: 52 darwin-sandbox.
INFO: Build completed successfully, 54 total actions

As with your prior experience when creating the Java Protocol Buffer target, you likely will notice a slightly longer-than-normal build time. Once again, this is normal, since the language-specific (i.e., Go) plug-in is being compiled; as before, after the first time, this is cached, and later builds will go much quicker.

Once again, congratulations, since we now have a target that we can actually use in our Go program. Now let’s modify our echo server to take advantage of this.

Using Your Go Protocol Buffer Target

As with the echo client, in the last chapter, you had created a version of the echo server that bounced back the received JSON message (with some modifications). As before, we are going to be able to make some slight changes to our original program to handle Protocol Buffers.

Create the file echo_server.go in src and add the following to it (as before, changes from the prior chapter in bold).
package main
import (
      "fmt"
      "log"
      "net"
      "transmission_object”
      "github.com/golang/protobuf/proto"
)
func main() {
      log.Println("Spinning up the Echo Server in Go...")
      listen, error := net.Listen("tcp", ":1234")
      if error != nil {
              log.Panicln("Unable to listen: " + error.Error())
      }
      defer listen.Close()
      connection, error := listen.Accept()
      if error != nil {
              log.Panicln("Cannot accept a connection! Error: " + error.Error())
      }
      log.Println("Receiving on a new connection")
      defer connection.Close()
      defer log.Println("Connection now closed.")
      buffer := make([]byte, 2048)
      size, error := connection.Read(buffer)
      if error != nil {
              log.Panicln(
                  "Unable to read from the buffer! Error: " + error.Error())
      }
      data := buffer[:size]
      transmissionObject := &transmission_object.TransmissionObject{}
      error = proto.Unmarshal(data, transmissionObject)
      if error != nil {
              log.Panicln(
                  "Unable to unmarshal the buffer! Error: " + error.Error())
      }
      log.Println("Message = " + transmissionObject.GetMessage())
      log.Println("Value = " +
          fmt.Sprintf("%f", transmissionObject.GetValue()))
      transmissionObject.Message = "Echoed from Go: " +
          transmissionObject.GetMessage()
      transmissionObject.Value = 2 ∗ transmissionObject.GetValue()
      message, error := proto.Marshal(transmissionObject)
      if error != nil {
              log.Panicln("Unable to marshal the object! Error: " + error.Error())
      }
      connection.Write(message)
}
Listing 6-9

Protocol Buffer version of the Go server

Save this to echo_server.go.

While the changes are not a complete drop-in replacement for the JSON, the final result is extremely close to what we had in the previous chapter.

Of particular note, we have to bring in a dependency on the proto library itself (github.com/golang/protobuf/proto) in order to perform the unmarshaling/marshaling of the object from/to the data streams. Unlike the previous dependency on the encoding package in Go, we will need to account for this when we specify the dependencies within the BUILD file.

Open the BUILD file and add the following to create the necessary build target (differences from the last chapter in bold).
load("@io_bazel_rules_go//go:def.bzl", "go_binary")
go_binary(
    name = "echo_server",
    srcs = ["echo_server.go"],
    deps = [
         ":transmission_object_go_proto",
         "@com_github_golang_protobuf//proto:go_default_library",
    ],
)
Listing 6-10

Adding the echo server build target

Save your BUILD file.

Dependencies From Dependencies

An astute reader will notice that the dependency we have specified for the go_default_library is not actually specified within the WORKSPACE file; however, the preceding code still compiles without complaint.

The source of this additional dependency stems from the function that we called to set up the additional dependencies for the Go rules (i.e., go_rules_dependencies), which pulled in additional dependencies, including the above-listed one.

Although technically this is “explicitly” specified within the WORKSPACE file, it is obfuscated by the use of the dependencies function. In this case, we are taking advantage of the fact that all of these versions of the particular dependencies are meant to work in concert.

If this is too implicit, then a couple things can be done: (1) Explicitly specify a dependency within the WORKSPACE file; this will replace the version of the implicit dependency. (2) Pull the dependency into your project (e.g., through a third_party directory).

The decision on which route to pursues relates to how tightly you want to control your dependencies. (1) may be easier as a way to quickly get up and running and make it easier to change dependencies later on. However, again, (2) provides the strongest guarantee for build reproducibility.

Now we can build our echo server with Protocol Buffer support:
chapter_06/src$ bazel build :echo_server
INFO: Analysed target //src:echo_server (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target //src:echo_server up-to-date:
  bazel-bin/src/darwin_amd64_stripped/echo_server
INFO: Elapsed time: 0.169s, Critical Path: 0.00s
INFO: 0 processes.
INFO: Build completed successfully, 1 total action

Echo Using Protocol Buffers

Having reconstructed our echo client and server with Protocol Buffers, we are now ready to have them start talking to each other again.

Open a terminal and start running the server:
chapter_06/src$ bazel run :echo_server
INFO: Analysed target //src:echo_server (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target //src:echo_server up-to-date:
  bazel-bin/src/darwin_amd64_stripped/echo_server
INFO: Elapsed time: 0.169s, Critical Path: 0.00s
INFO: 0 processes.
INFO: Build completed successfully, 1 total action
INFO: Build completed successfully, 1 total action
2019/06/18 23:39:53 Spinning up the Echo Server in Go...
Now let’s open a separate terminal and start up the client:
chapter_06/src$ bazel run :echo_client
INFO: Analysed target //src:echo_client (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target //src:echo_client up-to-date:
  bazel-bin/src/echo_client.jar
  bazel-bin/src/echo_client
INFO: Elapsed time: 0.249s, Critical Path: 0.10s
INFO: 1 process: 1 worker.
INFO: Build completed successfully, 2 total actions
INFO: Build completed successfully, 2 total actions
Spinning up the Echo Client in Java...
Waiting on input from the user...
Now, let’s give it a little bit of text:
chapter_06/src$ bazel run :echo_client
<omitted from above>
Spinning up the Echo Client in Java...
Waiting on input from the user...
Waiting on input from the user...
My Client Message
Received by Java: My Client Message
Received Message from server:
value: 6.29
message: "Echoed from Go: My Client Message"
Now let’s check out the console output from the echo server:
chapter_06/src$ bazel run :echo_server
<omitted from above>
2019/06/18 23:41:50 Receiving on a new connection
2019/06/18 23:43:34 Message = My Client Message
2019/06/18 23:43:34 Value = 3.145000
2019/06/18 23:43:34 Connection now closed.

Congratulations! You’ve recreated the echo client/server using Protocol Buffers.

Dependency Tracking and Management

Compared to the previous chapter, there are some slight formatting differences, but the outputs are effectively the same. This begs an obvious question: Why did we reinvent everything from the last chapter? The answer lies in how we manage changes to our selected transmission object; this, in turn, showcases the ability of Bazel to perform dependency management, even across multiple languages.

In the last chapter, the dependency trees for our echo client and server were as follows:
../images/481224_1_En_6_Chapter/481224_1_En_6_Fig1_HTML.png
Figure 6-1

Dependency trees for the JSON echo client and server

As noted earlier, a major downfall is that the definitions for the JSON objects are specific to each language without reference to one another. Any change in the API (i.e., by changing the JSON object) needs to be done in both locations, making it prone to errors when you change it in one place and not the other.

Compare this to the dependency tree created for our Protocol Buffer echo client and server:
../images/481224_1_En_6_Chapter/481224_1_En_6_Fig2_HTML.png
Figure 6-2

Dependency tree for the Protocol Buffer echo client and server

Now the dependency tree for the Protocol Buffer echo client and server is still relatively simple and likely familiar to anyone coding at scale. However, the remarkable aspects of it are the following: (a) we are tying together dependencies across three languages (i.e., Java, Go, Protocol Buffer); (b) by doing so, we are solving the API change management problem from the JSON client; and (c) we have done so using relatively little setup code.

Change Management in Action

Having set up our build dependency tree, now let’s see it work in practice. First, we will ensure that all of our targets are already up to date.

Make sure that all of your targets are built using the special all target:
chapter_06/src$ bazel build :all
INFO: Analysed 5 targets (43 packages loaded, 7657 targets configured).
INFO: Found 5 targets...
INFO: Elapsed time: 12.039s, Critical Path: 1.03s
INFO: 0 processes.
INFO: Build completed successfully, 1 total action
Now let’s check the timestamp on our build targets of echo_client and echo_server:
chapter_06/src$ ll ../bazel-bin/src/echo_client.jar
-r-xr-xr-x  1 pj  wheel  12168 Jun 18 22:06 ../bazel-bin/src/echo_client.jar
chapter_06/src$ ll ../bazel-bin/src/darwin_amd64_stripped/echo_server
-r-xr-xr-x  1 pj  wheel  3595880 Jun 18 23:14 ../bazel-bin/src/darwin_amd64_stripped/echo_server
Make a trivial change to the echo_sever.go, but still ensure it still compiles (e.g., change some text in a log statement). Now let’s rebuild everything again and recheck the timestamps:
chapter_06/src$ bazel build :all
INFO: Analysed 5 targets (1 packages loaded, 126 targets configured).
INFO: Found 5 targets...
INFO: Elapsed time: 1.103s, Critical Path: 0.55s
INFO: 2 processes: 2 darwin-sandbox.
INFO: Build completed successfully, 3 total actions
chapter_06/src$ ll ../bazel-bin/src/echo_client.jar
-r-xr-xr-x  1 pj  wheel  12168 Jun 18 22:06 ../bazel-bin/src/echo_client.jar
chapter_06/src$ ll ../bazel-bin/src/darwin_amd64_stripped/echo_server
-r-xr-xr-x  1 pj  wheel  3595880 Jun 20 03:10 ../bazel-bin/src/darwin_amd64_stripped/echo_server
Unsurprisingly, Bazel only had to rebuild echo_server since our changes were confined only to that target.
../images/481224_1_En_6_Chapter/481224_1_En_6_Fig3_HTML.png
Figure 6-3

Changes to the echo_server only affect a single target

However, let’s make a more substantial change; let’s remove a field from the TransmissionObject message.
syntax = "proto3";
package transmission_object;
message TransmissionObject {
    float value = 1;
   // string message = 2;
}
Listing 6-11

Removing the Message field from TransmissionObject

Now, let’s attempt to rebuild both echo_client and echo_server:

Note

You will notice that we add the flag keep_going (or a shortened version of -k) to the build command. Without this, the “build everything” command would stop at the first failure; using it, we see all targets that are failing.

chapter_06/src$ bazel build --keep_going :all
INFO: Analysed 5 targets (0 packages loaded, 0 targets configured).
INFO: Found 5 targets...
ERROR: chapter_06/src/BUILD:12:1: Couldn't build file src/echo_client.jar: Building src/echo_
client.jar (1 source file) failed (Exit 1)
src/EchoClient.java:22: error: cannot find symbol
                    .setMessage(inputFromUser)
                    ^
  symbol:   method setMessage(String)
  location: class Builder
ERROR: chapter_06/src/BUILD:27:1: Couldn't build file src/darwin_amd64_stripped/echo_server%/
src/echo_server.a: GoCompile src/darwin_amd64_stripped/echo_server%/src/echo_server.a failed (Exit 1) builder failed: error executing command bazel-out/host/bin/external/go_sdk/builder compile -sdk external/go_sdk -installsuffix darwin_amd64 -src src/echo_server.go -arc ... (remaining 12 argument(s) skipped)
Use --sandbox_debug to see verbose messages from the sandbox
compile: error running compiler: exit status 2
/private/var/tmp/_bazel_pj/e24198bf4e647dabf052e612ba765c04/sandbox/darwin-sandbox/1/execroot/__main__/src/echo_server.go:41:47: transmissionObject
.GetMessage undefined (type *transmission_object.TransmissionObject has no field or method GetMessage)
/private/var/tmp/_bazel_pj/e24198bf4e647dabf052e612ba765c04/sandbox/darwin-sandbox/1/execroot/__main__/src/echo_server.go:44:20: transmissionObject.Message undefined (type *transmission_object.TransmissionObject has no field or method Message)
/private/var/tmp/_bazel_pj/e24198bf4e647dabf052e612ba765c04/sandbox/darwin-sandbox/1/execroot/__main__/src/echo_server.go:44:70: transmissionObject.GetMessage undefined (type *transmission_object.TransmissionObject has no field or method GetMessage)
INFO: Elapsed time: 0.364s, Critical Path: 0.18s
INFO: 0 processes.
FAILED: Build did NOT complete successfully
In this case, by changing the base dependency, we have dirtied our entire dependency tree:
../images/481224_1_En_6_Chapter/481224_1_En_6_Fig4_HTML.png
Figure 6-4

Changes to the transmission_object_proto affect all targets

If you’d like, you can double check the modification date after you fix the code by restoring the field.

Final Word

In this chapter, you were able to very simply add and use the necessary functionality for Protocol Buffers. Along the way, you also got to see firsthand the abilities of Bazel to very easily and powerfully manage build dependencies, even between code written in multiple languages. Although the examples here truly only scratched the surface, already you should be able to see the possibilities provided by a simple and standard declarative build language.

Protocol Buffers also reinforced Bazel’s capabilities at handling multiple languages with ease. At the same time, you also got a glimpse into easily using Protocol Buffers for serialization across multiple languages.

In later chapters, we will be returning to more use of Protocol Buffers with Bazel. For the moment, however, we will take a step back and look at some facilities that Bazel provides for code organization.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.143.4.181