Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 6f8f180

Browse files
committedSep 18, 2024·
chore: remove cgo usage in the kcl go lib
Signed-off-by: peefy <xpf6677@163.com>
1 parent 9289f10 commit 6f8f180

22 files changed

+242
-178
lines changed
 

‎.github/workflows/go-test.yaml

+3-21
Original file line numberDiff line numberDiff line change
@@ -58,24 +58,6 @@ jobs:
5858
- name: Set up Go
5959
uses: actions/setup-go@v2
6060
with:
61-
go-version: 1.22
62-
- uses: korandoru/setup-zig@v1
63-
with:
64-
zig-version: master
65-
- name: Set output
66-
id: macos_sdk
67-
run: echo "path=$(xcrun --show-sdk-path)" >> $GITHUB_OUTPUT
68-
- name: Go cross compile test on Windows
69-
run: |
70-
CGO_ENABLED=1 GOOS=windows GOARCH=amd64 CC='zig cc -target x86_64-windows-gnu' go build ./...
71-
- name: Go cross compile test on Linux
72-
run: |
73-
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 CC='zig cc -target x86_64-linux-musl' go build ./...
74-
CGO_ENABLED=1 GOOS=linux GOARCH=arm64 CC='zig cc -target aarch64-linux-musl' go build ./...
75-
- name: Go cross compile test on Macos
76-
run: |
77-
export SDK_PATH=$(xcrun --show-sdk-path)
78-
CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 CC='zig cc -target x86_64-macos-none -F'"${SDK_PATH}"'/System/Library/Frameworks' go build ./...
79-
CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 CC='zig cc -target aarch64-macos-none -F'"${SDK_PATH}"'/System/Library/Frameworks' go build ./...
80-
env:
81-
SDK_PATH: ${{ steps.macos_sdk.outputs.path }}
61+
go-version: 1.23
62+
- name: Go test
63+
run: go test ./...

‎.github/workflows/nodejs-test.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ jobs:
163163
path: nodejs/*.node
164164

165165
macos:
166-
runs-on: macos-latest
166+
runs-on: macos-12
167167
strategy:
168168
matrix:
169169
settings:

‎.github/workflows/zig-test.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
working-directory: "zig"
3030
strategy:
3131
matrix:
32-
os: [macos-12, macos-latest, ubuntu-latest, windows-latest]
32+
os: [macos-12, macos-latest, ubuntu-20.04, ubuntu-latest]
3333
runs-on: ${{ matrix.os }}
3434
steps:
3535
- uses: actions/checkout@v4

‎go.mod

+6-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,9 @@ module kcl-lang.io/lib
22

33
go 1.19
44

5-
require google.golang.org/protobuf v1.34.2
5+
require (
6+
github.com/ebitengine/purego v0.7.1
7+
google.golang.org/protobuf v1.34.2
8+
)
9+
10+
require golang.org/x/sys v0.25.0 // indirect

‎go.sum

+4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
github.com/ebitengine/purego v0.7.1 h1:6/55d26lG3o9VCZX8lping+bZcmShseiqlh2bnUDiPA=
2+
github.com/ebitengine/purego v0.7.1/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ=
13
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
4+
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
5+
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
26
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
37
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
48
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=

‎go/README.md

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# KCL Artifact Library for Go
2+
3+
## Developing
4+
5+
### Prerequisites
6+
7+
+ Go 1.23+
8+
9+
### Build and Test
10+
11+
```shell
12+
go test ./...
13+
```

‎go/install/install.go

+5-7
Original file line numberDiff line numberDiff line change
@@ -53,34 +53,32 @@ func InstallKclvm(installRoot string) error {
5353
if err != nil {
5454
return err
5555
}
56-
binPath := filepath.Join(installRoot, "bin")
57-
58-
versionMatched, err := checkVersion(binPath)
56+
versionMatched, err := checkVersion(installRoot)
5957

6058
if err != nil {
6159
return err
6260
}
6361

6462
// Install kclvm binary.
65-
err = installBin(binPath, "kclvm_cli", lib.CliBin, versionMatched)
63+
err = installBin(installRoot, "kclvm_cli", lib.CliBin, versionMatched)
6664
if err != nil {
6765
return err
6866
}
6967
// Install kclvm libs.
70-
err = installLib(binPath, "kclvm_cli_cdylib", versionMatched)
68+
err = installLib(installRoot, "kclvm_cli_cdylib", versionMatched)
7169
if err != nil {
7270
return err
7371
}
7472

7573
if !versionMatched {
76-
kclvmVersionPath := filepath.Join(binPath, "kclvm.version")
74+
kclvmVersionPath := filepath.Join(installRoot, "kclvm.version")
7775
err = os.WriteFile(kclvmVersionPath, []byte(getVersion()), os.FileMode(os.O_WRONLY|os.O_TRUNC))
7876
if err != nil {
7977
return err
8078
}
8179
}
8280

83-
os.Setenv("PATH", os.Getenv("PATH")+string(os.PathListSeparator)+binPath)
81+
os.Setenv("PATH", os.Getenv("PATH")+string(os.PathListSeparator)+installRoot)
8482

8583
return nil
8684
}
-21.7 MB
Binary file not shown.

‎go/native/cgo.go

-41
This file was deleted.

‎go/native/client.go

+40-26
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,59 @@
1-
//go:build cgo
2-
// +build cgo
3-
41
package native
52

6-
/*
7-
#include <stdlib.h>
8-
#include <stdint.h>
9-
typedef struct kclvm_service kclvm_service;
10-
*/
11-
import "C"
123
import (
134
"bytes"
145
"errors"
156
"runtime"
167
"strings"
8+
"sync"
179
"unsafe"
1810

11+
"github.com/ebitengine/purego"
1912
"google.golang.org/protobuf/proto"
2013
"google.golang.org/protobuf/reflect/protoreflect"
2114
"kcl-lang.io/lib/go/api"
2215
"kcl-lang.io/lib/go/plugin"
2316
)
2417

18+
var libInit sync.Once
19+
20+
var (
21+
lib uintptr
22+
serviceNew func(uint64) uintptr
23+
serviceDelete func(uintptr)
24+
serviceCall func(uintptr, string, string, uint, *uint) uintptr
25+
freeString func(uintptr)
26+
)
27+
2528
type validator interface {
2629
Validate() error
2730
}
2831

2932
type NativeServiceClient struct {
30-
client *C.kclvm_service
33+
svc uintptr
34+
}
35+
36+
func initLib() {
37+
libInit.Do(func() {
38+
lib, _ = loadServiceNativeLib()
39+
purego.RegisterLibFunc(&serviceNew, lib, "kclvm_service_new")
40+
purego.RegisterLibFunc(&serviceDelete, lib, "kclvm_service_delete")
41+
purego.RegisterLibFunc(&serviceCall, lib, "kclvm_service_call_with_length")
42+
purego.RegisterLibFunc(&freeString, lib, "kclvm_service_free_string")
43+
})
3144
}
3245

3346
func NewNativeServiceClient() api.ServiceClient {
3447
return NewNativeServiceClientWithPluginAgent(plugin.GetInvokeJsonProxyPtr())
3548
}
3649

3750
func NewNativeServiceClientWithPluginAgent(pluginAgent uint64) *NativeServiceClient {
51+
initLib()
3852
c := new(NativeServiceClient)
39-
c.client = NewKclvmService(C.uint64_t(pluginAgent))
53+
c.svc = serviceNew(pluginAgent)
4054
runtime.SetFinalizer(c, func(x *NativeServiceClient) {
41-
DeleteKclvmService(x.client)
42-
x.client = nil
55+
serviceDelete(x.svc)
56+
closeLibrary(lib)
4357
})
4458
return c
4559
}
@@ -68,20 +82,10 @@ func cApiCall[I interface {
6882
if err != nil {
6983
return nil, err
7084
}
85+
var cOutSize uint
86+
cOut := serviceCall(c.svc, callName, string(inBytes), uint(len(inBytes)), &cOutSize)
7187

72-
cCallName := C.CString(callName)
73-
74-
defer C.free(unsafe.Pointer(cCallName))
75-
76-
cIn := C.CString(string(inBytes))
77-
78-
defer C.free(unsafe.Pointer(cIn))
79-
80-
cOut, cOutSize := KclvmServiceCall(c.client, cCallName, cIn, C.size_t(len(inBytes)))
81-
82-
defer KclvmServiceFreeString(cOut)
83-
84-
msg := C.GoBytes(unsafe.Pointer(cOut), C.int(cOutSize))
88+
msg := GoByte(cOut, cOutSize)
8589

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

103+
// GoByte copies a null-terminated char* to a Go string.
104+
func GoByte(c uintptr, length uint) []byte {
105+
// We take the address and then dereference it to trick go vet from creating a possible misuse of unsafe.Pointer
106+
ptr := *(*unsafe.Pointer)(unsafe.Pointer(&c))
107+
if ptr == nil {
108+
return []byte{}
109+
}
110+
return unsafe.Slice((*byte)(ptr), length)
111+
}
112+
99113
func (c *NativeServiceClient) Ping(in *api.Ping_Args) (*api.Ping_Result, error) {
100114
return cApiCall[*api.Ping_Args, *api.Ping_Result](c, "KclvmService.Ping", in)
101115
}

‎go/native/client_plugin_test.go

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//go:build cgo
2+
// +build cgo
3+
4+
package native
5+
6+
import (
7+
"strings"
8+
"testing"
9+
10+
"kcl-lang.io/lib/go/api"
11+
"kcl-lang.io/lib/go/plugin"
12+
)
13+
14+
func init() {
15+
// Add a plugin named hello
16+
plugin.RegisterPlugin(plugin.Plugin{
17+
Name: "hello",
18+
MethodMap: map[string]plugin.MethodSpec{
19+
"add": {
20+
Body: func(args *plugin.MethodArgs) (*plugin.MethodResult, error) {
21+
v := args.IntArg(0) + args.IntArg(1)
22+
return &plugin.MethodResult{V: v}, nil
23+
},
24+
},
25+
},
26+
})
27+
}
28+
29+
func TestExecProgramWithPlugin(t *testing.T) {
30+
client := NewNativeServiceClient()
31+
result, err := client.ExecProgram(&api.ExecProgram_Args{
32+
KFilenameList: []string{"main.k"},
33+
KCodeList: []string{code},
34+
Args: []*api.Argument{
35+
{
36+
Name: "a",
37+
Value: "1",
38+
},
39+
{
40+
Name: "b",
41+
Value: "2",
42+
},
43+
},
44+
})
45+
if err != nil {
46+
t.Fatal(err)
47+
}
48+
if result.ErrMessage != "" {
49+
t.Fatal("error message must be empty")
50+
}
51+
}
52+
53+
func TestExecProgramWithPluginError(t *testing.T) {
54+
client := NewNativeServiceClient()
55+
result, err := client.ExecProgram(&api.ExecProgram_Args{
56+
KFilenameList: []string{"main.k"},
57+
KCodeList: []string{code},
58+
})
59+
if err != nil {
60+
t.Fatal(err)
61+
}
62+
if !strings.Contains(result.ErrMessage, "strconv.ParseInt: parsing \"<nil>\": invalid syntax") {
63+
t.Fatal(result.ErrMessage)
64+
}
65+
}

0 commit comments

Comments
 (0)
Please sign in to comment.