Skip to content

Commit

Permalink
Revert "refactor: cgo usage and runtime code (#379)"
Browse files Browse the repository at this point in the history
This reverts commit cc2c515.
  • Loading branch information
Peefy committed Sep 16, 2024
1 parent 1580e22 commit c9aebec
Show file tree
Hide file tree
Showing 37 changed files with 13,195 additions and 1,542 deletions.
7 changes: 2 additions & 5 deletions .github/workflows/main_darwin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ jobs:
strategy:
matrix:
os: [ macos-12, macos-13, macos-13-xlarge, macos-14 ]
cgo: [ '1', '0' ]
runs-on: ${{ matrix.os }}
steps:
- name: Git checkout
Expand All @@ -23,7 +22,5 @@ jobs:
with:
go-version-file: go.mod

- name: Go test
env:
CGO_ENABLED: ${{ matrix.cgo }}
run: go test ./...
# Parallel tests
- run: go test ./...
10 changes: 5 additions & 5 deletions .github/workflows/main_linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ jobs:
strategy:
matrix:
os: [ ubuntu-20.04, ubuntu-22.04, ubuntu-latest ]
cgo: [ '1', '0' ]
runs-on: ${{ matrix.os }}
steps:
- name: Git checkout
Expand All @@ -22,10 +21,11 @@ jobs:
with:
go-version-file: go.mod

- name: Go test
env:
CGO_ENABLED: ${{ matrix.cgo }}
run: go test ./...
- name: CGO_ENABLED=0 go test
run: CGO_ENABLED=0 go test ./...

- name: CGO_ENABLED=1 go test
run: CGO_ENABLED=1 go test ./...

- run: go test -v -coverprofile=profile.cov ./...
- uses: shogo82148/actions-goveralls@v1
Expand Down
12 changes: 3 additions & 9 deletions .github/workflows/main_windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,7 @@ on:
- "releases/*"
jobs:
build-and-test:
strategy:
matrix:
os: [ windows-latest ]
cgo: [ '1', '0' ]
runs-on: ${{ matrix.os }}
runs-on: windows-2019
steps:
- name: Git checkout
uses: actions/checkout@v4
Expand All @@ -21,7 +17,5 @@ jobs:
with:
go-version-file: go.mod

- name: Go test
env:
CGO_ENABLED: ${{ matrix.cgo }}
run: go test ./...
# Parallel tests
- run: go test ./...
11 changes: 11 additions & 0 deletions kcl.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
"kcl-lang.io/kcl-go/pkg/kcl"
"kcl-lang.io/kcl-go/pkg/loader"
"kcl-lang.io/kcl-go/pkg/parser"
"kcl-lang.io/kcl-go/pkg/runtime"
"kcl-lang.io/kcl-go/pkg/tools/format"
"kcl-lang.io/kcl-go/pkg/tools/lint"
"kcl-lang.io/kcl-go/pkg/tools/list"
Expand Down Expand Up @@ -70,6 +71,16 @@ type (
ParseProgramResult = parser.ParseProgramResult
)

// InitKclvmPath init kclvm path.
func InitKclvmPath(kclvmRoot string) {
runtime.InitKclvmRoot(kclvmRoot)
}

// InitKclvmRuntime init kclvm process.
func InitKclvmRuntime(n int) {
runtime.InitRuntime(n)
}

// MustRun is like Run but panics if return any error.
func MustRun(path string, opts ...Option) *KCLResultList {
return kcl.MustRun(path, opts...)
Expand Down
17 changes: 17 additions & 0 deletions kcl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ package kcl_test

import (
"bytes"
"flag"
"os"
"path/filepath"
"reflect"
"sort"
"strconv"
"strings"
"testing"

Expand All @@ -23,6 +25,21 @@ import (
"kcl-lang.io/kcl-go/pkg/spec/gpyrpc"
)

const tEnvNumCpu = "KCL_GO_API_TEST_NUM_CPU"

func TestMain(m *testing.M) {
flag.Parse()

if s := os.Getenv(tEnvNumCpu); s != "" {
if x, err := strconv.Atoi(s); err == nil {
println(tEnvNumCpu, "=", s)
kcl.InitKclvmRuntime(x)
}
}

os.Exit(m.Run())
}

func TestStreamResult(t *testing.T) {
file, err := filepath.Abs("./testdata/stream/one_stream.k")
if err != nil {
Expand Down
85 changes: 85 additions & 0 deletions pkg/3rdparty/dlopen/dlopen_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright 2016 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//go:build linux || darwin
// +build linux darwin

// Package dlopen provides some convenience functions to dlopen a library and
// get its symbols.
package dlopen

// #cgo LDFLAGS: -ldl
// #include <stdlib.h>
// #include <dlfcn.h>
import "C"
import (
"errors"
"fmt"
"unsafe"
)

var ErrSoNotFound = errors.New("unable to open a handle to the library")

// LibHandle represents an open handle to a library (.so)
type LibHandle struct {
Handle unsafe.Pointer
LibName string
}

// GetHandle tries to get a handle to a library (.so), attempting to access it
// by the names specified in libs and returning the first that is successfully
// opened. Callers are responsible for closing the handler. If no library can
// be successfully opened, an error is returned.
func GetHandle(libs []string) (*LibHandle, error) {
for _, name := range libs {
libname := C.CString(name)
defer C.free(unsafe.Pointer(libname))
handle := C.dlopen(libname, C.RTLD_LAZY)
if handle != nil {
h := &LibHandle{
Handle: handle,
LibName: name,
}
return h, nil
}
}
return nil, ErrSoNotFound
}

// GetSymbolPointer takes a symbol name and returns a pointer to the symbol.
func (l *LibHandle) GetSymbolPointer(symbol string) (unsafe.Pointer, error) {
sym := C.CString(symbol)
defer C.free(unsafe.Pointer(sym))

C.dlerror()
p := C.dlsym(l.Handle, sym)
e := C.dlerror()
if e != nil {
return nil, fmt.Errorf("error resolving symbol %q: %v", symbol, errors.New(C.GoString(e)))
}

return p, nil
}

// Close closes a LibHandle.
func (l *LibHandle) Close() error {
C.dlerror()
C.dlclose(l.Handle)
e := C.dlerror()
if e != nil {
return fmt.Errorf("error closing %v: %v", l.LibName, errors.New(C.GoString(e)))
}

return nil
}
51 changes: 51 additions & 0 deletions pkg/3rdparty/dlopen/dlopen_windws.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//go:build windows
// +build windows

// Package dlopen provides some convenience functions to dlopen a library and
// get its symbols.
package dlopen

import (
"errors"
"unsafe"

"syscall"
)

var ErrSoNotFound = errors.New("unable to open a handle to the library")

// LibHandle represents an open handle to a library (.so)
type LibHandle struct {
Handle syscall.Handle
LibName string
}

func GetHandle(libs []string) (*LibHandle, error) {
if len(libs) == 0 {
return nil, ErrSoNotFound
}
name := libs[0]
dll, err := syscall.LoadLibrary(name)
if err != nil {
return nil, err
}
return &LibHandle{
Handle: dll,
LibName: name,
}, nil
}

// GetSymbolPointer takes a symbol name and returns a pointer to the symbol.
func (l *LibHandle) GetSymbolPointer(symbol string) (unsafe.Pointer, error) {
proc, err := syscall.GetProcAddress(l.Handle, symbol)
if err != nil {
return nil, err
}
return unsafe.Pointer(proc), nil
}

// Close closes a LibHandle.
func (l *LibHandle) Close() error {
_ = syscall.FreeLibrary(l.Handle)
return nil
}
7 changes: 2 additions & 5 deletions pkg/kcl/rpc_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,11 @@

package kcl

import (
"kcl-lang.io/kcl-go/pkg/service"
"kcl-lang.io/lib/go/api"
)
import "kcl-lang.io/kcl-go/pkg/service"

// Service returns the interaction interface between KCL Go SDK and KCL Rust core.
// When `go build tags=rpc` is opened, use the default RPC interaction logic to avoid CGO usage.
// When closed, use CGO and dynamic libraries to interact.
func Service() api.ServiceClient {
func Service() service.KclvmService {
return service.NewKclvmServiceClient()
}
6 changes: 3 additions & 3 deletions pkg/kcl/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
package kcl

import (
"kcl-lang.io/lib/go/api"
"kcl-lang.io/lib/go/native"
"kcl-lang.io/kcl-go/pkg/native"
"kcl-lang.io/kcl-go/pkg/service"
)

// Service returns the interaction interface between KCL Go SDK and KCL Rust core.
// When `go build tags=rpc` is opened, use the default RPC interaction logic to avoid CGO usage.
// When closed, use CGO and dynamic libraries to interact.
func Service() api.ServiceClient {
func Service() service.KclvmService {
return native.NewNativeServiceClient()
}
90 changes: 90 additions & 0 deletions pkg/native/cgo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//go:build cgo
// +build cgo

package native

/*
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
typedef struct kclvm_service kclvm_service;
kclvm_service * kclvm_service_new(void *f,uint64_t plugin_agent){
kclvm_service * (*new_service)(uint64_t);
new_service = (kclvm_service * (*)(uint64_t))f;
return new_service(plugin_agent);
}
void kclvm_service_delete(void *f,kclvm_service * c){
void (*delete_service)(kclvm_service *);
delete_service = (void (*)(kclvm_service *))f;
return delete_service(c);
}
void kclvm_service_free_string(void *f,const char * res) {
void (*free_string)(const char *);
free_string = (void (*)(const char *))f;
return free_string(res);
}
const char* kclvm_service_call_with_length(void *f,kclvm_service* c,const char * method,const char * args,size_t args_len,size_t * result_len){
const char* (*service_call_with_length)(kclvm_service*,const char *,const char *,size_t,size_t *);
service_call_with_length = (const char* (*)(kclvm_service*,const char *,const char *,size_t,size_t *))f;
return service_call_with_length(c,method,args,args_len,result_len);
}
*/
import "C"

// NewKclvmService takes a pluginAgent and returns a pointer of capi kclvm_service.
// pluginAgent is the address of C function pointer with type :const char * (*)(const char *method,const char *args,const char *kwargs),
// in which "method" is the fully qualified name of plugin method,
// and "args" ,"kwargs" and return value of pluginAgent are JSON string
func NewKclvmService(pluginAgent C.uint64_t) *C.kclvm_service {
const fnName = "kclvm_service_new"

newServ, err := lib.GetSymbolPointer(fnName)

if err != nil {
panic(err)
}

return C.kclvm_service_new(newServ, pluginAgent)
}

// NewKclvmService releases the memory of kclvm_service
func DeleteKclvmService(serv *C.kclvm_service) {
const fnName = "kclvm_service_delete"

deleteServ, err := lib.GetSymbolPointer(fnName)

if err != nil {
panic(err)
}

C.kclvm_service_delete(deleteServ, serv)
}

// KclvmServiceFreeString releases the memory of the return value of KclvmServiceCall
func KclvmServiceFreeString(str *C.char) {
const fnName = "kclvm_service_free_string"

freeString, err := lib.GetSymbolPointer(fnName)

if err != nil {
panic(err)
}

C.kclvm_service_free_string(freeString, str)
}

// KclvmServiceCall calls kclvm service by c api
// args should be serialized as protobuf byte stream
func KclvmServiceCall(serv *C.kclvm_service, method *C.char, args *C.char, args_len C.size_t) (*C.char, C.size_t) {
const fnName = "kclvm_service_call_with_length"

serviceCall, err := lib.GetSymbolPointer(fnName)

if err != nil {
panic(err)
}

var size C.size_t = C.SIZE_MAX
buf := C.kclvm_service_call_with_length(serviceCall, serv, method, args, args_len, &size)
return buf, size
}
Loading

0 comments on commit c9aebec

Please sign in to comment.