Skip to content

Commit

Permalink
WIP calling simple grpc client library from zig
Browse files Browse the repository at this point in the history
  • Loading branch information
zachgrayio committed Jul 15, 2022
1 parent f592803 commit 14ead4b
Show file tree
Hide file tree
Showing 12 changed files with 255 additions and 70 deletions.
2 changes: 1 addition & 1 deletion .bazelrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# todo: add this line back when Zig toolchain is working for gRPC builds
#build --action_env BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1

build --experimental_show_artifacts
build --cxxopt='-std=c++14'
build --copt=-DGRPC_BAZEL_BUILD
build --disk_cache=~/.cache/zig_grpc_example_bazel
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
zig-cache/
zig-out/
bazel-*
.idea/
95 changes: 94 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,98 @@
},
"zig.buildOnSave": true,
"zigLanguageClient.path": "/usr/local/bin/zls",
"zls.path": "/usr/local/bin/zls"
"zls.path": "/usr/local/bin/zls",
"files.associations": {
"vector": "cpp",
"__bit_reference": "cpp",
"__bits": "cpp",
"__config": "cpp",
"__debug": "cpp",
"__errc": "cpp",
"__hash_table": "cpp",
"__locale": "cpp",
"__mutex_base": "cpp",
"__node_handle": "cpp",
"__nullptr": "cpp",
"__split_buffer": "cpp",
"__string": "cpp",
"__threading_support": "cpp",
"__tree": "cpp",
"__tuple": "cpp",
"any": "cpp",
"array": "cpp",
"atomic": "cpp",
"bit": "cpp",
"bitset": "cpp",
"cctype": "cpp",
"cfenv": "cpp",
"chrono": "cpp",
"cinttypes": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"compare": "cpp",
"complex": "cpp",
"concepts": "cpp",
"condition_variable": "cpp",
"csignal": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"exception": "cpp",
"forward_list": "cpp",
"fstream": "cpp",
"future": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"ios": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"limits": "cpp",
"list": "cpp",
"locale": "cpp",
"map": "cpp",
"memory": "cpp",
"mutex": "cpp",
"new": "cpp",
"numeric": "cpp",
"optional": "cpp",
"ostream": "cpp",
"queue": "cpp",
"random": "cpp",
"ratio": "cpp",
"regex": "cpp",
"scoped_allocator": "cpp",
"set": "cpp",
"sstream": "cpp",
"stack": "cpp",
"stdexcept": "cpp",
"streambuf": "cpp",
"string": "cpp",
"string_view": "cpp",
"system_error": "cpp",
"thread": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"typeindex": "cpp",
"typeinfo": "cpp",
"unordered_map": "cpp",
"unordered_set": "cpp",
"valarray": "cpp",
"variant": "cpp",
"__functional_base": "cpp",
"algorithm": "cpp",
"functional": "cpp",
"iterator": "cpp",
"utility": "cpp",
"*.inc": "cpp"
},
"cmake.configureOnOpen": false
}
15 changes: 11 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
.PHONY: build
build:
build: #clean
bazel build //...
zig build

.PHONY: run
run: build
zig build run -- --port=8000
.PHONY: run_greeters
run_greeters: run_greeter_server run_greeter_client

.PHONY: run_greeter_server
run_greeter_server: build
zig build run_greeter_server -- --port=8000

.PHONY: run_greeter_client
run_greeter_client: build
zig build run_greeter_client -- --port=8000

.PHONY: clean
clean:
Expand Down
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@

Noob attempt at getting up and running with gRPC and Zig.

## Prerequisites

- Zig code requires latest `zig` built from source against `master` or installed via homebrew with `brew install zig --HEAD`. If errors occur during installation, get latest Xcode on macOS.
- gRPC and proto code is built with Bazel; install with `brew install bazelisk`

## Running

- Zig code requires latest `zig` built from source against `master` or installed via homebrew with `brew install zig --HEAD`.
- If errors occur, get latest Xcode on macOS.
- gRPC and proto code is built with Bazel.
### Greeter client & server
- In a terminal, `make run_greeters -j2`
97 changes: 74 additions & 23 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,84 @@ pub fn build(b: *std.build.Builder) void {
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
const mode = b.standardReleaseOptions();

const exe = b.addExecutable("zig-grpc-example", "src/main.zig");
exe.addPackagePath("clap", "deps/zig-clap/clap.zig");
// Greeter server
const greeter_server = b.addExecutable("greeter_server", "src/greeter_server.zig");
greeter_server.addPackagePath("clap", "deps/zig-clap/clap.zig");
greeter_server.linkLibC();
greeter_server.linkLibCpp();
greeter_server.addIncludePath("bazel-bin/src/protos");
greeter_server.setTarget(target);
greeter_server.setBuildMode(mode);
greeter_server.install();

const run_greeter_server_cmd = greeter_server.run();
run_greeter_server_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| {
run_greeter_server_cmd.addArgs(args);
}

const run_greeter_server_step = b.step("run_greeter_server", "Run the greeter_server app");
run_greeter_server_step.dependOn(&run_greeter_server_cmd.step);

const greeter_server_tests = b.addTest("src/greeter_server.zig");
greeter_server_tests.setTarget(target);
greeter_server_tests.setBuildMode(mode);

const test_greeter_server_step = b.step("test_greeter_server", "Run unit tests");
test_greeter_server_step.dependOn(&greeter_server_tests.step);

// const c_flags = [_][]const u8{
// "-std=c++14",
// "-fvisibility=hidden",
// "-Wall",
// "-Werror=strict-prototypes",
// "-Werror=old-style-definition",
// "-Werror=missing-prototypes",
// "-Wno-missing-braces",
// // "-DBAZEL_BUILD=1"
// };

// Greeter client
const greeter_client = b.addExecutable("greeter_client", "src/greeter_client.zig");
greeter_client.addPackagePath("clap", "deps/zig-clap/clap.zig");
greeter_client.linkLibC();
greeter_client.linkLibCpp();

exe.linkLibCpp();
// exe.linkSystemLibrary("pthread");
// exe.linkSystemLibrary("dl");
exe.addIncludeDir("bazel-bin/src/protos");
// exe.defineCMacro("HAVE_PTHREAD", "1");
// exe.addCSourceFiles(&[_][]const u8{});

exe.setTarget(target);
exe.setBuildMode(mode);
exe.install();

const run_cmd = exe.run();
run_cmd.step.dependOn(b.getInstallStep());
greeter_client.addIncludePath("src/helloworld");
greeter_client.addIncludePath("bazel-bin");
greeter_client.addIncludePath("bazel-bin/src/protos");

// build greeter client CC with zig; an alternative to linking against the bazel-built version.
// note this doesn't quite work, as we'd need to link against more objectsin bazel-out manually.
// greeter_client.defineCMacro("BAZEL_BUILD", "1");
// greeter_client.addIncludePath("./");
// greeter_client.addCSourceFile("src/helloworld/greeter_client.cc", &c_flags);
// greeter_client.addIncludePath("bazel-zig-grpc-example/external/com_github_grpc_grpc/include");
// greeter_client.addIncludePath("bazel-zig-grpc-example/external/com_google_absl");
// greeter_client.addIncludePath("bazel-zig-grpc-example/external/com_google_protobuf/src");

// hack: use a `cc_binary` linked with linkstatic and linkshared to build a self-contained .so:
greeter_client.addObjectFile("bazel-bin/src/helloworld/libgreeter_client.so");
// instead of the more obvious
// greeter_client.addObjectFile("bazel-bin/src/helloworld/libgreeter_client.a");

greeter_client.setTarget(target);
greeter_client.setBuildMode(mode);
greeter_client.install();

const run_greeter_client_cmd = greeter_client.run();
run_greeter_client_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| {
run_cmd.addArgs(args);
run_greeter_client_cmd.addArgs(args);
}

const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
const run_greeter_client_step = b.step("run_greeter_client", "Run the greeter_client app");
run_greeter_client_step.dependOn(&run_greeter_client_cmd.step);

const exe_tests = b.addTest("src/main.zig");
exe_tests.setTarget(target);
exe_tests.setBuildMode(mode);
const greeter_client_tests = b.addTest("src/greeter_client.zig");
greeter_client_tests.setTarget(target);
greeter_client_tests.setBuildMode(mode);

const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&exe_tests.step);
const greeter_client_test_step = b.step("test_greeter_client", "Run unit tests");
greeter_client_test_step.dependOn(&greeter_client_tests.step);
}
15 changes: 14 additions & 1 deletion src/global.zig
Original file line number Diff line number Diff line change
@@ -1 +1,14 @@
// globals here
const std = @import("std");
const c = std.c;

pub const grpc = @cImport({
@cDefine("BAZEL_BUILD", 1);
@cInclude("helloworld.pb.h");
@cInclude("helloworld.grpc.pb.h");
@cInclude("keyvaluestore.pb.h");
@cInclude("keyvaluestore.grpc.pb.h");
});

pub const greeter_client = @cImport({
@cInclude("greeter_client.h");
});
18 changes: 18 additions & 0 deletions src/greeter_client.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const cli = @import("cli.zig");
const global = @import("global.zig");
const std = @import("std");

const debug = std.log.debug;
const grpc = global.grpc;
const greeter_client = global.greeter_client;

pub fn main() !void {
debug("Greeter Client", .{});
const args = try cli.parseArgs();
debug("port {}", .{args.port});
const target: [*c]const u8 = "localhost:3000";
const user: [*c]const u8 = "world";
debug("calling CC...", .{});
const response: [*c]const u8 = greeter_client.sayHello(target.*, user.*);
debug("response: {s}", .{response});
}
5 changes: 4 additions & 1 deletion src/main.zig → src/greeter_server.zig
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
const cli = @import("cli.zig");
const zig_grpc_example = @import("global.zig");
const global = @import("global.zig");
const std = @import("std");

const grpc = global.grpc;

pub fn main() !void {
std.log.debug("Greeter Server", .{});
const args = try cli.parseArgs();
std.log.debug("port {}", .{args.port});
}
12 changes: 10 additions & 2 deletions src/helloworld/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,17 @@
licenses(["notice"])

cc_binary(
# cc_library(
name = "greeter_client",
srcs = ["greeter_client.cc"],
srcs = [
"greeter_client.h",
"greeter_client.cc",
],
# hdrs = ["greeter_client.h"],
defines = ["BAZEL_BUILD"],
linkstatic = 1,
linkshared = True,
# alwayslink = True,
deps = [
"@com_github_grpc_grpc//:grpc++",
"//src/protos:helloworld_cc_grpc",
Expand Down Expand Up @@ -66,7 +74,7 @@ cc_binary(
],
)

cc_binary(
cc_library(
name = "greeter_server",
srcs = ["greeter_server.cc"],
defines = ["BAZEL_BUILD"],
Expand Down
41 changes: 7 additions & 34 deletions src/helloworld/greeter_client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

#ifdef BAZEL_BUILD
#include "src/protos/helloworld.grpc.pb.h"
#include "src/helloworld/greeter_client.h"
#else
#include "helloworld.grpc.pb.h"
#endif
Expand Down Expand Up @@ -71,38 +72,10 @@ class GreeterClient {
std::unique_ptr<Greeter::Stub> stub_;
};

int main(int argc, char** argv) {
// Instantiate the client. It requires a channel, out of which the actual RPCs
// are created. This channel models a connection to an endpoint specified by
// the argument "--target=" which is the only expected argument.
// We indicate that the channel isn't authenticated (use of
// InsecureChannelCredentials()).
std::string target_str;
std::string arg_str("--target");
if (argc > 1) {
std::string arg_val = argv[1];
size_t start_pos = arg_val.find(arg_str);
if (start_pos != std::string::npos) {
start_pos += arg_str.size();
if (arg_val[start_pos] == '=') {
target_str = arg_val.substr(start_pos + 1);
} else {
std::cout << "The only correct argument syntax is --target="
<< std::endl;
return 0;
}
} else {
std::cout << "The only acceptable argument is --target=" << std::endl;
return 0;
}
} else {
target_str = "localhost:50051";
}
GreeterClient greeter(
grpc::CreateChannel(target_str, grpc::InsecureChannelCredentials()));
std::string user("world");
std::string reply = greeter.SayHello(user);
std::cout << "Greeter received: " << reply << std::endl;

return 0;
const char* sayHello(char* target, char* user) {
// return strdup(std::string("test").c_str());
// std::cout << "test from cc" << std::endl;
GreeterClient greeter(grpc::CreateChannel(std::string(target), grpc::InsecureChannelCredentials()));
std::string reply = greeter.SayHello(std::string(user));
return strdup(reply.c_str());
}
Loading

0 comments on commit 14ead4b

Please sign in to comment.