diff --git a/.bazelrc b/.bazelrc index 3a6a58f..f6c69c0 100644 --- a/.bazelrc +++ b/.bazelrc @@ -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 \ No newline at end of file diff --git a/.gitignore b/.gitignore index e87d24a..bb55f01 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ zig-cache/ zig-out/ bazel-* +.idea/ \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index fb561ec..6084126 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -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 } \ No newline at end of file diff --git a/Makefile b/Makefile index 71c2873..2e74d74 100644 --- a/Makefile +++ b/Makefile @@ -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: diff --git a/README.md b/README.md index 17788be..6eea8db 100644 --- a/README.md +++ b/README.md @@ -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` \ No newline at end of file diff --git a/build.zig b/build.zig index 21dba00..430deb4 100644 --- a/build.zig +++ b/build.zig @@ -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); } diff --git a/src/global.zig b/src/global.zig index d9c494d..6b1d899 100644 --- a/src/global.zig +++ b/src/global.zig @@ -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"); +}); diff --git a/src/greeter_client.zig b/src/greeter_client.zig new file mode 100644 index 0000000..8e5d686 --- /dev/null +++ b/src/greeter_client.zig @@ -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}); +} diff --git a/src/main.zig b/src/greeter_server.zig similarity index 60% rename from src/main.zig rename to src/greeter_server.zig index 7f95e29..de27876 100644 --- a/src/main.zig +++ b/src/greeter_server.zig @@ -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}); } diff --git a/src/helloworld/BUILD b/src/helloworld/BUILD index 7b89e4b..ad053f8 100644 --- a/src/helloworld/BUILD +++ b/src/helloworld/BUILD @@ -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", @@ -66,7 +74,7 @@ cc_binary( ], ) -cc_binary( +cc_library( name = "greeter_server", srcs = ["greeter_server.cc"], defines = ["BAZEL_BUILD"], diff --git a/src/helloworld/greeter_client.cc b/src/helloworld/greeter_client.cc index cf2a6aa..a9b16ba 100644 --- a/src/helloworld/greeter_client.cc +++ b/src/helloworld/greeter_client.cc @@ -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 @@ -71,38 +72,10 @@ class GreeterClient { std::unique_ptr 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()); } diff --git a/src/helloworld/greeter_client.h b/src/helloworld/greeter_client.h new file mode 100644 index 0000000..ed1a4d1 --- /dev/null +++ b/src/helloworld/greeter_client.h @@ -0,0 +1,14 @@ +#ifndef ZIG_GRPC_EXAMPLE_FOO_H +#define ZIG_GRPC_EXAMPLE_FOO_H + +#ifdef __cplusplus +extern "C" { +#endif + +const char* sayHello(char* target, char* user); + +#ifdef __cplusplus +} +#endif + +#endif //ZIG_GRPC_EXAMPLE_FOO_H