Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add C++ grpc example #827

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .bazelrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
build --cxxopt='-std=c++17'

# On macOS with an installed oppenssl, this prevent Bazel from looking in /usr/local/include and failing the boringssl compile.
# This does on the other hand brak the python interpreter in rules_python
# build --sandbox_block_path=/usr/local

# Ensure rules_haskell picks up the correct toolchain on Mac.
build --action_env=BAZEL_USE_CPP_ONLY_TOOLCHAIN=1

Expand Down
5 changes: 4 additions & 1 deletion src/cpp/hello_world/greet/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ cc_library(
name = "greet",
srcs = ["greet.cc"],
hdrs = ["greet.h"],
visibility = ["//src/cpp/hello_world:__pkg__"],
visibility = [
"//src/cpp/hello_world:__pkg__",
"//src/cpp/hello_world/server:__pkg__",
],
deps = ["@fmt"],
)

Expand Down
33 changes: 33 additions & 0 deletions src/cpp/hello_world/server/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
load("@bazel_skylib//rules:build_test.bzl", "build_test")
load("@io_bazel_rules_go//go:def.bzl", "go_test")
load("@rules_cc//cc:defs.bzl", "cc_binary")

cc_binary(
name = "server",
srcs = ["server.cc"],
deps = [
"//src/cpp/hello_world/greet",
"//src/proto/helloworld:helloworld_cc_grpc",
"@gflags",
"@grpc//:grpc++",
"@grpc//:grpc++_reflection",
],
)

build_test(
name = "server_build_test",
targets = [":server"],
)

go_test(
name = "server_test",
size = "medium",
timeout = "short",
srcs = ["server_test.go"],
data = [":server"],
deps = [
"//src/proto/helloworld",
"@io_bazel_rules_go//go/tools/bazel:go_default_library",
"@org_golang_google_grpc//:go_default_library",
],
)
45 changes: 45 additions & 0 deletions src/cpp/hello_world/server/server.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include <iostream>

#include "gflags/gflags.h"
#include <grpcpp/ext/proto_server_reflection_plugin.h>
#include <grpcpp/grpcpp.h>
#include <grpcpp/health_check_service_interface.h>

#include "src/proto/helloworld/helloworld.grpc.pb.h"
#include "src/cpp/hello_world/greet/greet.h"

using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
using helloworld::HelloReply;
using helloworld::HelloRequest;
using helloworld::Greeter;

DEFINE_string(address, "0.0.0.0:50051", "address to bind to");

class GreeterServiceImpl final : public Greeter::Service {
Status SayHello(ServerContext *context,
const HelloRequest *request,
HelloReply *reply) override {
reply->set_message(greet::greet(request->name()));
return Status::OK;
}
};

int
main(int argc, char **argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true);

GreeterServiceImpl service;

grpc::EnableDefaultHealthCheckService(true);
grpc::reflection::InitProtoReflectionServerBuilderPlugin();
ServerBuilder builder;
builder.AddListeningPort(FLAGS_address, grpc::InsecureServerCredentials());
builder.RegisterService(&service);
std::unique_ptr<Server> server(builder.BuildAndStart());
std::cerr << "Server listening on " << FLAGS_address << std::endl;
server->Wait();
return 0;
}
111 changes: 111 additions & 0 deletions src/cpp/hello_world/server/server_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package main_test

import (
"context"
"fmt"
"log"
"net"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"testing"
"time"

"github.com/bazelbuild/rules_go/go/tools/bazel"
"github.com/satreix/everest/src/proto/helloworld"
"google.golang.org/grpc"
)

func TestEnd2End(t *testing.T) {
const iface = "127.0.0.1"
port, err := freeTCPPort("tcp", iface)
if err != nil {
t.Fatalf("could not get a free port: %s", err)
}
addr := fmt.Sprintf("%s:%d", iface, port)
t.Logf("addr: %s", addr)

serverBin, err := findBinary("//src/cpp/hello_world/server", "server")
if err != nil {
t.Fatalf("error finding server: %s", err)
}
t.Log("server: " + serverBin)

serverCmd := exec.Command(serverBin, fmt.Sprintf("--address=%s", addr))
if err := serverCmd.Start(); err != nil {
t.Fatalf("error starting server: %s", err)
}
defer serverCmd.Process.Kill()
t.Logf("serverCmd: %s", strings.Join(serverCmd.Args, " "))

// FIXME make this not time based but instead retry the assertion
time.Sleep(2 * time.Second)

conn, err := grpc.Dial(addr, grpc.WithInsecure())
if err != nil {
t.Fatalf("Failed to connect: %v", err)
}
defer conn.Close()

client := helloworld.NewGreeterClient(conn)

resp, err := client.SayHello(context.Background(), &helloworld.HelloRequest{
Name: "Alice",
})
if err != nil {
t.Fatalf("SayHello failed: %v", err)
}

expectedMessage := "Hello, Alice!"
if resp.Message != expectedMessage {
t.Fatalf("Unexpected response: got %q, want %q", resp.Message, expectedMessage)
}
}

func findBinary(pkg, name string) (string, error) {
// workaround for external targets
if strings.HasPrefix(pkg, "@") {
pkg = "external/" + strings.TrimPrefix(pkg, "@")
}
pkg = strings.TrimPrefix(pkg, "//")

rfs, err := bazel.ListRunfiles()
if err != nil {
return "", err
}
for idx, rf := range rfs {
if rf.ShortPath == filepath.Join(pkg, name) {
return rf.Path, nil
}

log.Printf("%d. %#v", idx, rf)
}

bin, found := bazel.FindBinary(pkg, name)
if !found {
return "", fmt.Errorf("couldn't find binary for %s:%s", pkg, name)
}

fi, err := os.Stat(bin)
if err != nil {
panic(err.Error())
}

if fi.Mode().IsDir() {
return bin + "/" + runtime.GOOS + "_" + runtime.GOARCH + "_stripped/" + name, nil
}

return bin, nil
}

func freeTCPPort(network, iface string) (int, error) {
l, err := net.Listen(network, iface+":0")
if err != nil {
return 0, err
}
defer l.Close()

return l.Addr().(*net.TCPAddr).Port, nil
}
15 changes: 15 additions & 0 deletions src/proto/helloworld/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
load("@grpc//bazel:cc_grpc_library.bzl", "cc_grpc_library")
load("@grpc-java//:java_grpc_library.bzl", "java_grpc_library")
load("@io_bazel_rules_go//go:def.bzl", "go_library")
load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
load("@rules_cc//cc:defs.bzl", "cc_proto_library")
load("@rules_java//java:defs.bzl", "java_proto_library")
load("@rules_proto//proto:defs.bzl", "proto_library")
load("@rules_rust//proto/protobuf:defs.bzl", "rust_grpc_library")
Expand All @@ -14,6 +16,19 @@ proto_library(
visibility = ["//visibility:public"],
)

cc_proto_library(
name = "helloworld_cc_proto",
deps = [":helloworld_proto"],
)

cc_grpc_library(
name = "helloworld_cc_grpc",
srcs = [":helloworld_proto"],
grpc_only = True,
visibility = ["//src/cpp/hello_world/server:__pkg__"],
deps = [":helloworld_cc_proto"],
)

go_proto_library(
name = "helloworld_go_proto",
compilers = ["@io_bazel_rules_go//proto:go_grpc"],
Expand Down
Loading