Skip to content

Commit

Permalink
chore: remove cgo usage in the kcl go lib
Browse files Browse the repository at this point in the history
Signed-off-by: peefy <[email protected]>
  • Loading branch information
Peefy committed Sep 18, 2024
1 parent 9289f10 commit a3c009d
Show file tree
Hide file tree
Showing 22 changed files with 240 additions and 199 deletions.
43 changes: 1 addition & 42 deletions .github/workflows/go-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,47 +35,6 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.22
- name: Ready msys2
uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
update: true
install: git mingw-w64-x86_64-toolchain
path-type: inherit
if: matrix.os == 'windows-latest'
go-version: 1.23
- name: Go code test
run: go test ./...

cross-compile-test:
runs-on: macos-12
defaults:
run:
working-directory: "go"
steps:
- name: Git checkout
uses: actions/checkout@v2
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.22
- uses: korandoru/setup-zig@v1
with:
zig-version: master
- name: Set output
id: macos_sdk
run: echo "path=$(xcrun --show-sdk-path)" >> $GITHUB_OUTPUT
- name: Go cross compile test on Windows
run: |
CGO_ENABLED=1 GOOS=windows GOARCH=amd64 CC='zig cc -target x86_64-windows-gnu' go build ./...
- name: Go cross compile test on Linux
run: |
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 CC='zig cc -target x86_64-linux-musl' go build ./...
CGO_ENABLED=1 GOOS=linux GOARCH=arm64 CC='zig cc -target aarch64-linux-musl' go build ./...
- name: Go cross compile test on Macos
run: |
export SDK_PATH=$(xcrun --show-sdk-path)
CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 CC='zig cc -target x86_64-macos-none -F'"${SDK_PATH}"'/System/Library/Frameworks' go build ./...
CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 CC='zig cc -target aarch64-macos-none -F'"${SDK_PATH}"'/System/Library/Frameworks' go build ./...
env:
SDK_PATH: ${{ steps.macos_sdk.outputs.path }}
2 changes: 1 addition & 1 deletion .github/workflows/nodejs-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ jobs:
path: nodejs/*.node

macos:
runs-on: macos-latest
runs-on: macos-12
strategy:
matrix:
settings:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/zig-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
working-directory: "zig"
strategy:
matrix:
os: [macos-12, macos-latest, ubuntu-latest, windows-latest]
os: [macos-12, macos-latest, ubuntu-20.04, ubuntu-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
Expand Down
7 changes: 6 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,9 @@ module kcl-lang.io/lib

go 1.19

require google.golang.org/protobuf v1.34.2
require (
github.com/ebitengine/purego v0.7.1
google.golang.org/protobuf v1.34.2
)

require golang.org/x/sys v0.25.0 // indirect
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
github.com/ebitengine/purego v0.7.1 h1:6/55d26lG3o9VCZX8lping+bZcmShseiqlh2bnUDiPA=
github.com/ebitengine/purego v0.7.1/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
13 changes: 13 additions & 0 deletions go/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# KCL Artifact Library for Go

## Developing

### Prerequisites

+ Go 1.23+

### Build and Test

```shell
go test ./...
```
12 changes: 5 additions & 7 deletions go/install/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,34 +53,32 @@ func InstallKclvm(installRoot string) error {
if err != nil {
return err
}
binPath := filepath.Join(installRoot, "bin")

versionMatched, err := checkVersion(binPath)
versionMatched, err := checkVersion(installRoot)

if err != nil {
return err
}

// Install kclvm binary.
err = installBin(binPath, "kclvm_cli", lib.CliBin, versionMatched)
err = installBin(installRoot, "kclvm_cli", lib.CliBin, versionMatched)
if err != nil {
return err
}
// Install kclvm libs.
err = installLib(binPath, "kclvm_cli_cdylib", versionMatched)
err = installLib(installRoot, "kclvm_cli_cdylib", versionMatched)
if err != nil {
return err
}

if !versionMatched {
kclvmVersionPath := filepath.Join(binPath, "kclvm.version")
kclvmVersionPath := filepath.Join(installRoot, "kclvm.version")
err = os.WriteFile(kclvmVersionPath, []byte(getVersion()), os.FileMode(os.O_WRONLY|os.O_TRUNC))
if err != nil {
return err
}
}

os.Setenv("PATH", os.Getenv("PATH")+string(os.PathListSeparator)+binPath)
os.Setenv("PATH", os.Getenv("PATH")+string(os.PathListSeparator)+installRoot)

return nil
}
Binary file removed go/lib/windows-arm64/static/libkclvm_cli_cdylib.a
Binary file not shown.
41 changes: 0 additions & 41 deletions go/native/cgo.go

This file was deleted.

66 changes: 40 additions & 26 deletions go/native/client.go
Original file line number Diff line number Diff line change
@@ -1,45 +1,59 @@
//go:build cgo
// +build cgo

package native

/*
#include <stdlib.h>
#include <stdint.h>
typedef struct kclvm_service kclvm_service;
*/
import "C"
import (
"bytes"
"errors"
"runtime"
"strings"
"sync"
"unsafe"

"github.com/ebitengine/purego"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"kcl-lang.io/lib/go/api"
"kcl-lang.io/lib/go/plugin"
)

var libInit sync.Once

var (
lib uintptr
serviceNew func(uint64) uintptr
serviceDelete func(uintptr)
serviceCall func(uintptr, string, string, uint, *uint) uintptr
freeString func(uintptr)
)

type validator interface {
Validate() error
}

type NativeServiceClient struct {
client *C.kclvm_service
svc uintptr
}

func initLib() {
libInit.Do(func() {
lib, _ = loadServiceNativeLib()
purego.RegisterLibFunc(&serviceNew, lib, "kclvm_service_new")
purego.RegisterLibFunc(&serviceDelete, lib, "kclvm_service_delete")
purego.RegisterLibFunc(&serviceCall, lib, "kclvm_service_call_with_length")
purego.RegisterLibFunc(&freeString, lib, "kclvm_service_free_string")
})
}

func NewNativeServiceClient() api.ServiceClient {
return NewNativeServiceClientWithPluginAgent(plugin.GetInvokeJsonProxyPtr())
}

func NewNativeServiceClientWithPluginAgent(pluginAgent uint64) *NativeServiceClient {
initLib()
c := new(NativeServiceClient)
c.client = NewKclvmService(C.uint64_t(pluginAgent))
c.svc = serviceNew(pluginAgent)
runtime.SetFinalizer(c, func(x *NativeServiceClient) {
DeleteKclvmService(x.client)
x.client = nil
serviceDelete(x.svc)
closeLibrary(lib)
})
return c
}
Expand Down Expand Up @@ -68,20 +82,10 @@ func cApiCall[I interface {
if err != nil {
return nil, err
}
var cOutSize uint
cOut := serviceCall(c.svc, callName, string(inBytes), uint(len(inBytes)), &cOutSize)

cCallName := C.CString(callName)

defer C.free(unsafe.Pointer(cCallName))

cIn := C.CString(string(inBytes))

defer C.free(unsafe.Pointer(cIn))

cOut, cOutSize := KclvmServiceCall(c.client, cCallName, cIn, C.size_t(len(inBytes)))

defer KclvmServiceFreeString(cOut)

msg := C.GoBytes(unsafe.Pointer(cOut), C.int(cOutSize))
msg := GoByte(cOut, cOutSize)

if bytes.HasPrefix(msg, []byte("ERROR:")) {
return nil, errors.New(strings.TrimPrefix(string(msg), "ERROR:"))
Expand All @@ -96,6 +100,16 @@ func cApiCall[I interface {
return out, nil
}

// GoByte copies a null-terminated char* to a Go string.
func GoByte(c uintptr, length uint) []byte {
// We take the address and then dereference it to trick go vet from creating a possible misuse of unsafe.Pointer
ptr := *(*unsafe.Pointer)(unsafe.Pointer(&c))
if ptr == nil {
return []byte{}
}
return unsafe.Slice((*byte)(ptr), length)
}

func (c *NativeServiceClient) Ping(in *api.Ping_Args) (*api.Ping_Result, error) {
return cApiCall[*api.Ping_Args, *api.Ping_Result](c, "KclvmService.Ping", in)
}
Expand Down
65 changes: 65 additions & 0 deletions go/native/client_plugin_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//go:build cgo
// +build cgo

package native

import (
"strings"
"testing"

"kcl-lang.io/lib/go/api"
"kcl-lang.io/lib/go/plugin"
)

func init() {
// Add a plugin named hello
plugin.RegisterPlugin(plugin.Plugin{
Name: "hello",
MethodMap: map[string]plugin.MethodSpec{
"add": {
Body: func(args *plugin.MethodArgs) (*plugin.MethodResult, error) {
v := args.IntArg(0) + args.IntArg(1)
return &plugin.MethodResult{V: v}, nil
},
},
},
})
}

func TestExecProgramWithPlugin(t *testing.T) {
client := NewNativeServiceClient()
result, err := client.ExecProgram(&api.ExecProgram_Args{
KFilenameList: []string{"main.k"},
KCodeList: []string{code},
Args: []*api.Argument{
{
Name: "a",
Value: "1",
},
{
Name: "b",
Value: "2",
},
},
})
if err != nil {
t.Fatal(err)
}
if result.ErrMessage != "" {
t.Fatal("error message must be empty")
}
}

func TestExecProgramWithPluginError(t *testing.T) {
client := NewNativeServiceClient()
result, err := client.ExecProgram(&api.ExecProgram_Args{
KFilenameList: []string{"main.k"},
KCodeList: []string{code},
})
if err != nil {
t.Fatal(err)
}
if !strings.Contains(result.ErrMessage, "strconv.ParseInt: parsing \"<nil>\": invalid syntax") {
t.Fatal(result.ErrMessage)
}
}
Loading

0 comments on commit a3c009d

Please sign in to comment.