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

Support wasm based bindings #90

Merged
merged 4 commits into from
Oct 17, 2024
Merged
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
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
Loading