Skip to content

Commit

Permalink
Initial commit of go-protoc-gen-grpc
Browse files Browse the repository at this point in the history
  • Loading branch information
anuraaga committed Dec 16, 2023
0 parents commit 2abf0b0
Show file tree
Hide file tree
Showing 37 changed files with 783 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text=auto eol=lf
34 changes: 34 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: CI
on:
push:
branches:
- main
tags:
- "*"
pull_request:
workflow_dispatch:

jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
mode:
- cgo
- wazero
os:
- macos-12
- ubuntu-22.04
- windows-2022
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: actions/setup-go@v4
with:
go-version: '^1.21'

- name: run checks
run: go run mage check
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.idea
.vscode
build
45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# go-protoc-gen-grpc

go-protoc-gen-grpc is a distribution of the official gRPC protoc generation plugins from [grpc/grpc][1]
(this is not to be confused with protoc-gen-grpc-go, the compiler for generating Go gRPC stubs, which also
happens to be written in Go). It does not actually reimplement any functionality of gRPC in Go, instead compiling
the original source code to WebAssembly, and executing with the pure Go Wasm runtime [wazero][2].
This means that `go install` or `go run` can be used to execute it, with no need to rely on external
package managers such as Homebrew, on any platform that Go supports.

## Installation

Install the plugin you want using `go install`.

```bash
$ go install github.com/wasilibs/go-protoc-gen-grpc/cmd/protoc-gen-grpc_python@latest
```

As long as `$GOPATH/bin`, e.g. `~/go/bin` is on the `PATH`, you can use it with protoc as normal.

```bash
$ protoc --grpc_python_out=out/python -Iprotos protos/helloworld.proto
```

Note that the filenames of binaries in this repository match protoc conventions so `--plugin` is not needed.

For [buf][3] users, it can be convenient to use `go run` in `buf.gen.yaml`.

```yaml
version: v1
plugins:
- plugin: grpc_python
out: out/python
path: ["go", "run", "github.com/wasilibs/go-protoc-gen-grpc/cmd/protoc-gen-grpc_python@latest"]
```
If also using [go-protoc][4] for `protoc_path` when generating the non-gRPC protobuf stubs, and invoking
`buf` with `go run`, it is possible to have full protobuf/gRPC generation with no installation of tools,
besides Go itself, on any platform that Go supports. The above examples use `@latest`, but it is
recommended to specify a version, in which case all of the developers on your codebase will use the
same version of the tool with no special steps.

[1]: https://github.com/grpc/grpc
[2]: https://wazero.io/
[3]: https://buf.build/
[4]: https://github.com/wasilibs/go-protoc
16 changes: 16 additions & 0 deletions buildtools/wasm/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FROM ghcr.io/wasilibs/wasix-sdk:sha-fc94d60

RUN apt-get update && apt-get install -y binaryen git patch

RUN git clone --recursive https://github.com/grpc/grpc.git --branch v1.60.0 /workspace

ENV CXXFLAGS "${CXXFLAGS} -O3 -pthread -D_WASI_EMULATED_MMAN -D_WASI_EMULATED_PROCESS_CLOCKS"
ENV LDFLAGS $LDFLAGS -lwasi-emulated-process-clocks -lwasi-emulated-mman -Wl,--max-memory=4294967296

WORKDIR /workspace
RUN cmake -S . -B build
RUN cmake --build build --target plugins

RUN for f in build/*_plugin; do wasm-opt -o "$f".wasm --low-memory-unused --flatten --rereloop --converge -O3 "$f"; done

CMD ["bash", "-c", "cp build/*_plugin.wasm /out/"]
10 changes: 10 additions & 0 deletions cmd/protoc-gen-grpc_cpp/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package main

import (
"github.com/wasilibs/go-protoc-gen-grpc/internal/runner"
"github.com/wasilibs/go-protoc-gen-grpc/internal/wasm"
)

func main() {
runner.Run("protoc-gen-grpc_cpp", wasm.GRPCCPPPlugin)
}
10 changes: 10 additions & 0 deletions cmd/protoc-gen-grpc_csharp/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package main

import (
"github.com/wasilibs/go-protoc-gen-grpc/internal/runner"
"github.com/wasilibs/go-protoc-gen-grpc/internal/wasm"
)

func main() {
runner.Run("protoc-gen-grpc_csharp", wasm.GRPCCSharpPlugin)
}
10 changes: 10 additions & 0 deletions cmd/protoc-gen-grpc_node/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package main

import (
"github.com/wasilibs/go-protoc-gen-grpc/internal/runner"
"github.com/wasilibs/go-protoc-gen-grpc/internal/wasm"
)

func main() {
runner.Run("protoc-gen-grpc_node", wasm.GRPCNodePlugin)
}
10 changes: 10 additions & 0 deletions cmd/protoc-gen-grpc_objective_c/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package main

import (
"github.com/wasilibs/go-protoc-gen-grpc/internal/runner"
"github.com/wasilibs/go-protoc-gen-grpc/internal/wasm"
)

func main() {
runner.Run("protoc-gen-grpc_objective_c", wasm.GRPCObjectiveCPlugin)
}
10 changes: 10 additions & 0 deletions cmd/protoc-gen-grpc_php/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package main

import (
"github.com/wasilibs/go-protoc-gen-grpc/internal/runner"
"github.com/wasilibs/go-protoc-gen-grpc/internal/wasm"
)

func main() {
runner.Run("protoc-gen-grpc_php", wasm.GRPCPHPPlugin)
}
10 changes: 10 additions & 0 deletions cmd/protoc-gen-grpc_python/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package main

import (
"github.com/wasilibs/go-protoc-gen-grpc/internal/runner"
"github.com/wasilibs/go-protoc-gen-grpc/internal/wasm"
)

func main() {
runner.Run("protoc-gen-grpc_python", wasm.GRPCPythonPlugin)
}
10 changes: 10 additions & 0 deletions cmd/protoc-gen-grpc_ruby/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package main

import (
"github.com/wasilibs/go-protoc-gen-grpc/internal/runner"
"github.com/wasilibs/go-protoc-gen-grpc/internal/wasm"
)

func main() {
runner.Run("protoc-gen-grpc_ruby", wasm.GRPCRubyPlugin)
}
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/wasilibs/go-protoc-gen-grpc

go 1.19

require github.com/wasilibs/wazerox v0.0.0-20231215071156-a88739a1af2a
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github.com/wasilibs/wazerox v0.0.0-20231215071156-a88739a1af2a h1:qzlH+wVfxU42OCFyJf4n+9K02Uceg1mf+e9wXpa+DLA=
github.com/wasilibs/wazerox v0.0.0-20231215071156-a88739a1af2a/go.mod h1:IQNVyA4d1hWIe23mlMMuqXjyWMdndgSlNx6FqBkwPsM=
7 changes: 7 additions & 0 deletions go.work
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
go 1.19

use (
.
./mage
./magefiles
)
2 changes: 2 additions & 0 deletions go.work.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github.com/wasilibs/wazerox v0.0.0-20231215071156-a88739a1af2a h1:qzlH+wVfxU42OCFyJf4n+9K02Uceg1mf+e9wXpa+DLA=
github.com/wasilibs/wazerox v0.0.0-20231215071156-a88739a1af2a/go.mod h1:IQNVyA4d1hWIe23mlMMuqXjyWMdndgSlNx6FqBkwPsM=
117 changes: 117 additions & 0 deletions internal/runner/runner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package runner

import (
"context"
"crypto/rand"
"io/fs"
"log"
"os"
"strings"

"github.com/wasilibs/go-protoc-gen-grpc/internal/wasix_32v1"
wazero "github.com/wasilibs/wazerox"
"github.com/wasilibs/wazerox/api"
"github.com/wasilibs/wazerox/experimental"
"github.com/wasilibs/wazerox/experimental/sys"
"github.com/wasilibs/wazerox/experimental/sysfs"
"github.com/wasilibs/wazerox/imports/wasi_snapshot_preview1"
wzsys "github.com/wasilibs/wazerox/sys"
)

func Run(name string, wasm []byte) {
ctx := context.Background()

rt := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfig().WithCoreFeatures(api.CoreFeaturesV2|experimental.CoreFeaturesThreads))

wasi_snapshot_preview1.MustInstantiate(ctx, rt)
wasix_32v1.MustInstantiate(ctx, rt)

args := []string{name}
args = append(args, os.Args[1:]...)

fsCfg := wazero.NewFSConfig().(sysfs.FSConfig).WithSysFSMount(cmdFS{cwd: sysfs.DirFS("."), root: sysfs.DirFS("/")}, "/")
fsCfg = fsCfg.(sysfs.FSConfig).WithRawPaths()

cfg := wazero.NewModuleConfig().
WithSysNanosleep().
WithSysNanotime().
WithSysWalltime().
WithStderr(os.Stderr).
WithStdout(os.Stdout).
WithStdin(os.Stdin).
WithRandSource(rand.Reader).
WithArgs(args...).
WithFSConfig(fsCfg)
for _, env := range os.Environ() {
k, v, _ := strings.Cut(env, "=")
cfg = cfg.WithEnv(k, v)
}

_, err := rt.InstantiateWithConfig(ctx, wasm, cfg)
if err != nil {
if sErr, ok := err.(*wzsys.ExitError); ok {
os.Exit(int(sErr.ExitCode()))
}
log.Fatal(err)
}
}

type cmdFS struct {
cwd sys.FS
root sys.FS
}

func (fs cmdFS) OpenFile(path string, flag sys.Oflag, perm fs.FileMode) (sys.File, sys.Errno) {
return fs.fs(path).OpenFile(path, flag, perm)
}

func (fs cmdFS) Lstat(path string) (wzsys.Stat_t, sys.Errno) {
return fs.fs(path).Lstat(path)
}

func (fs cmdFS) Stat(path string) (wzsys.Stat_t, sys.Errno) {
return fs.fs(path).Stat(path)
}

func (fs cmdFS) Mkdir(path string, perm fs.FileMode) sys.Errno {
return fs.fs(path).Mkdir(path, perm)
}

func (fs cmdFS) Chmod(path string, perm fs.FileMode) sys.Errno {
return fs.fs(path).Chmod(path, perm)
}

func (fs cmdFS) Rename(from string, to string) sys.Errno {
return fs.fs(from).Rename(from, to)
}

func (fs cmdFS) Rmdir(path string) sys.Errno {
return fs.fs(path).Rmdir(path)
}

func (fs cmdFS) Unlink(path string) sys.Errno {
return fs.fs(path).Unlink(path)
}

func (fs cmdFS) Link(oldPath string, newPath string) sys.Errno {
return fs.fs(oldPath).Link(oldPath, newPath)
}

func (fs cmdFS) Symlink(oldPath string, linkName string) sys.Errno {
return fs.fs(oldPath).Symlink(oldPath, linkName)
}

func (fs cmdFS) Readlink(path string) (string, sys.Errno) {
return fs.fs(path).Readlink(path)
}

func (fs cmdFS) Utimens(path string, atim int64, mtim int64) sys.Errno {
return fs.fs(path).Utimens(path, atim, mtim)
}

func (fs cmdFS) fs(path string) sys.FS {
if len(path) > 0 && path[0] != '/' {
return fs.cwd
}
return fs.root
}
Loading

0 comments on commit 2abf0b0

Please sign in to comment.