Skip to content

Commit

Permalink
Support wasm based bindings (#90)
Browse files Browse the repository at this point in the history
* support wasm based bindings

* update go version

* fix test case

* remove debug code
  • Loading branch information
goccy authored Oct 17, 2024
1 parent c66e25e commit 1c5cc39
Show file tree
Hide file tree
Showing 880 changed files with 36,521 additions and 1,085,118 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
go-version: [ "1.20", "1.21" ]
go-version: [ "1.22.0", "1.23" ]
runs-on: ${{ matrix.os }}
steps:
- name: checkout
Expand All @@ -20,7 +20,7 @@ jobs:
with:
go-version: ${{ matrix.go-version }}
- name: build dot command
run: go build -v cmd/dot/dot.go
run: cd ./cmd/dot && go build -v .
- name: test
if: ${{ matrix.os != 'windows-latest' }}
run: go test -race -v ./...
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
*.o
*.svg
*.png
*.jpg
.DS_Store
bin
33 changes: 33 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
export

CONTAINER_NAME := graphviz-wasm
IMAGE_NAME := graphviz-wasm
GOBIN := $(PWD)/bin
PATH := $(GOBIN):internal/tools/nori/bin:$(PATH)

.PHONY: tools
tools: nori
go install github.com/bufbuild/buf/cmd/[email protected]

fmt/buf:
buf format --write

generate/wasm: container/build
$(eval CONTAINER_ID := $(shell docker create graphviz-wasm))
docker cp "$(CONTAINER_ID):/work/graphviz.wasm" ./internal/wasm/graphviz.wasm

container/build:
docker build ./internal/wasm/build -t $(IMAGE_NAME) --build-arg GRAPHVIZ_VERSION=$(shell cat graphviz.version)

container/prune:
docker container prune

.PHONY: generate/buf
generate/buf:
$(GOBIN)/buf generate
mv bind.c internal/wasm/build
mv bind.go internal/wasm/

.PHONY: nori
nori:
make build -C ./internal/tools/nori
99 changes: 44 additions & 55 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
# go-graphviz [![Go](https://github.com/goccy/go-graphviz/workflows/Go/badge.svg)](https://github.com/goccy/go-graphviz/actions) [![GoDoc](https://godoc.org/github.com/goccy/go-graphviz?status.svg)](https://pkg.go.dev/github.com/goccy/go-graphviz)

Go bindings for Graphviz ( port of version `2.40.1` )
Go bindings for Graphviz

<img src="https://user-images.githubusercontent.com/209884/90976476-64e84000-e578-11ea-9596-fb4a7d3b11a6.png" width="400px"></img>

# Features

Graphviz version is [here](./graphviz.version)

- Pure Go Library
- No need to install Graphviz library ( ~`brew install graphviz`~ or ~`apt-get install graphviz`~ )
- Supports parsing for DOT language
- Supports rendering graph in pure Go
- Supports switch renderer to your own
- Supports type safed property setting
- `gvc` `cgraph` `cdt` are available as sub package
- The Graphviz library has been converted to WebAssembly (WASM) and embedded it, so it works consistently across all environments
- Supports encoding/decoding for DOT language
- Supports custom renderer for custom format
- Supports setting graph properties in a type-safe manner

## Currently supported Layout
## Supported Layout

`circo` `dot` `fdp` `neato` `nop` `nop1` `nop2` `osage` `patchwork` `sfdp` `twopi`

## Currently supported format
## Supported Format

`dot` `svg` `png` `jpg`

The above are the formats supported by default. You can also add custom formats.

# Installation

```bash
Expand All @@ -43,32 +47,28 @@ import (
)

func main() {
g := graphviz.New()
ctx := context.Background()
g, err := graphviz.New(ctx)
if err != nil { panic(err )}

graph, err := g.Graph()
if err != nil {
log.Fatal(err)
}
if err != nil { panic(err) }
defer func() {
if err := graph.Close(); err != nil {
log.Fatal(err)
}
if err := graph.Close(); err != nil { panic(err) }
g.Close()
}()
n, err := graph.CreateNode("n")
if err != nil {
log.Fatal(err)
}
m, err := graph.CreateNode("m")
if err != nil {
log.Fatal(err)
}
e, err := graph.CreateEdge("e", n, m)
if err != nil {
log.Fatal(err)
}
n, err := graph.CreateNodeByName("n")
if err != nil { panic(err) }

m, err := graph.CreateNodeByName("m")
if err != nil { panic(err) }

e, err := graph.CreateEdgeByName("e", n, m)
if err != nil { panic(err) }
e.SetLabel("e")

var buf bytes.Buffer
if err := g.Render(graph, "dot", &buf); err != nil {
if err := g.Render(ctx, graph, "dot", &buf); err != nil {
log.Fatal(err)
}
fmt.Println(buf.String())
Expand All @@ -79,40 +79,33 @@ func main() {

```go
path := "/path/to/dot.gv"
b, err := ioutil.ReadFile(path)
if err != nil {
log.Fatal(err)
}
b, err := os.ReadFile(path)
if err != nil { panic(err) }
graph, err := graphviz.ParseBytes(b)
```

## 3. Render Graph

```go
g := graphviz.New()
ctx := context.Background()
g, err := graphviz.New(ctx)
if err != nil { panic(err) }

graph, err := g.Graph()
if err != nil {
log.Fatal(err)
}
if err != nil { panic(err) }

// create your graph

// 1. write encoded PNG data to buffer
var buf bytes.Buffer
if err := g.Render(graph, graphviz.PNG, &buf); err != nil {
log.Fatal(err)
}
if err := g.Render(ctx, graph, graphviz.PNG, &buf); err != nil { panic(err) }

// 2. get as image.Image instance
image, err := g.RenderImage(graph)
if err != nil {
log.Fatal(err)
}
image, err := g.RenderImage(ctx, graph)
if err != nil { panic(err) }

// 3. write to file directly
if err := g.RenderFilename(graph, graphviz.PNG, "/path/to/graph.png"); err != nil {
log.Fatal(err)
}
if err := g.RenderFilename(ctx, graph, graphviz.PNG, "/path/to/graph.png"); err != nil { panic(err) }
```

# Tool
Expand All @@ -122,7 +115,7 @@ if err := g.RenderFilename(graph, graphviz.PNG, "/path/to/graph.png"); err != ni
### Installation

```bash
$ go get github.com/goccy/go-graphviz/cmd/dot
$ go install github.com/goccy/go-graphviz/cmd/dot@latest
```

### Usage
Expand All @@ -142,14 +135,10 @@ Help Options:

# How it works

<img width = "600px" src="https://user-images.githubusercontent.com/209884/75105919-48685b00-565c-11ea-8add-ebd5545f5399.png"></img>

`go-graphviz` has four layers.

1. `graphviz` package provides facade interface for manipulating all features of graphviz library
2. `gvc` `cgraph` `cdt` are sub packages ( FYI: C library section in https://www.graphviz.org/documentation )
3. `internal/ccall` package provides bridge interface between Go and C
4. `go-graphviz` includes full graphviz sources
1. Generates bindings between Go and C from [Protocol Buffers file](./internal/wasm/bind.proto).
2. Builds graphviz.wasm on the [docker container](./internal/wasm/build/Dockerfile).
3. Uses Graphviz functionality from a sub-packages ( `cdt` `cgraph` `gvc` ) via the `internal/wasm` package.
4. `graphviz` package provides facade interface for all sub packages.

# License

Expand Down
35 changes: 21 additions & 14 deletions _examples/rw.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@ package main

import (
"bytes"
"context"
"log"

"github.com/goccy/go-graphviz"
)

func renderDOTGraph() ([]byte, error) {
g := graphviz.New()
func renderDOTGraph(ctx context.Context) ([]byte, error) {
g, err := graphviz.New(ctx)
if err != nil {
return nil, err
}
graph, err := g.Graph()
if err != nil {
return nil, err
Expand All @@ -19,61 +23,64 @@ func renderDOTGraph() ([]byte, error) {
}
g.Close()
}()
n, err := graph.CreateNode("n")
n, err := graph.CreateNodeByName("n")
if err != nil {
return nil, err
}
m, err := graph.CreateNode("m")
m, err := graph.CreateNodeByName("m")
if err != nil {
return nil, err
}
e, err := graph.CreateEdge("e", n, m)
e, err := graph.CreateEdgeByName("e", n, m)
if err != nil {
return nil, err
}
e.SetLabel("e")
var buf bytes.Buffer
if err := g.Render(graph, "dot", &buf); err != nil {
if err := g.Render(ctx, graph, "dot", &buf); err != nil {
return nil, err
}
return buf.Bytes(), nil
}

func _main() error {
graphBytes, err := renderDOTGraph()
func _main(ctx context.Context) error {
graphBytes, err := renderDOTGraph(ctx)
if err != nil {
return err
}
graph, err := graphviz.ParseBytes(graphBytes)
if err != nil {
return err
}
n, err := graph.Node("n")
n, err := graph.NodeByName("n")
if err != nil {
return err
}
l, err := graph.CreateNode("l")
l, err := graph.CreateNodeByName("l")
if err != nil {
return err
}
e2, err := graph.CreateEdge("e2", n, l)
e2, err := graph.CreateEdgeByName("e2", n, l)
if err != nil {
return err
}
e2.SetLabel("e2")
g := graphviz.New()
g, err := graphviz.New(ctx)
if err != nil {
return err
}
defer func() {
if err := graph.Close(); err != nil {
log.Fatal(err)
}
g.Close()
}()
g.RenderFilename(graph, "png", "rw.png")
g.RenderFilename(ctx, graph, "png", "rw.png")
return nil
}

func main() {
if err := _main(); err != nil {
if err := _main(context.Background()); err != nil {
log.Fatalf("%+v", err)
}
}
18 changes: 11 additions & 7 deletions _examples/simple.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@ package main

import (
"bytes"
"context"
"fmt"
"log"

"github.com/goccy/go-graphviz"
)

func _main() error {
g := graphviz.New()
func _main(ctx context.Context) error {
g, err := graphviz.New(ctx)
if err != nil {
return err
}
graph, err := g.Graph()
if err != nil {
return err
Expand All @@ -20,29 +24,29 @@ func _main() error {
}
g.Close()
}()
n, err := graph.CreateNode("n")
n, err := graph.CreateNodeByName("n")
if err != nil {
return err
}
m, err := graph.CreateNode("m")
m, err := graph.CreateNodeByName("m")
if err != nil {
return err
}
e, err := graph.CreateEdge("e", n, m)
e, err := graph.CreateEdgeByName("e", n, m)
if err != nil {
return err
}
e.SetLabel("e")
var buf bytes.Buffer
if err := g.Render(graph, "dot", &buf); err != nil {
if err := g.Render(ctx, graph, "dot", &buf); err != nil {
log.Fatalf("%+v", err)
}
fmt.Println(buf.String())
return nil
}

func main() {
if err := _main(); err != nil {
if err := _main(context.Background()); err != nil {
log.Fatal(err)
}
}
Loading

0 comments on commit 1c5cc39

Please sign in to comment.