Skip to content

Commit f66cf1f

Browse files
authored
feat(bindings/go): Add windows platform support (#5992)
* feat(bindings/go): support windows platform Hanchin Hsieh <[email protected]> * ci(bindings/go): add support for testing in windows Hanchin Hsieh <[email protected]> * fix: ci lint Hanchin Hsieh <[email protected]> * fix: nil ptr Hanchin Hsieh <[email protected]> * fix: ci go binding windows Hanchin Hsieh <[email protected]>
1 parent 220395a commit f66cf1f

File tree

14 files changed

+226
-40
lines changed

14 files changed

+226
-40
lines changed

.github/scripts/test_go_binding/matrix.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ build:
2626
goos: "darwin"
2727
goarch: "arm64"
2828
os: "macos-latest"
29+
- target: "x86_64-pc-windows-msvc"
30+
cc: "cl.exe"
31+
goos: "windows"
32+
goarch: "amd64"
33+
os: "windows-latest"
2934
service:
3035
- "fs"
3136

.github/workflows/ci_bindings_go.yml

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ on:
3131
- "bindings/c/**"
3232
- "bindings/go/**"
3333
- ".github/workflows/ci_bindings_go.yml"
34+
- ".github/scripts/test_go_binding/matrix.yaml"
3435
workflow_dispatch:
3536

3637
concurrency:
@@ -82,10 +83,17 @@ jobs:
8283
path: "tools"
8384
- name: Setup Rust toolchain
8485
uses: ./.github/actions/setup
85-
- name: Setup Target
86+
- name: Setup Target (Linux/macOS)
87+
if: runner.os != 'Windows'
8688
env:
8789
TARGET: ${{ matrix.build.target }}
8890
run: rustup target add $TARGET
91+
- name: Setup Target (Windows)
92+
if: runner.os == 'Windows'
93+
env:
94+
TARGET: ${{ matrix.build.target }}
95+
run: |
96+
rustup target add $env:TARGET
8997
- uses: actions/setup-go@v5
9098
with:
9199
go-version: stable
@@ -104,16 +112,20 @@ jobs:
104112
- name: Install dependencies (macOS)
105113
if: ${{ matrix.build.os == 'macos-latest' }}
106114
run: brew install zstd libffi
107-
- name: Build C Binding
115+
- name: Install dependencies (Windows)
116+
if: ${{ matrix.build.os == 'windows-latest' }}
117+
uses: ilammy/msvc-dev-cmd@v1
118+
- name: Build C Binding (Linux/macOS)
108119
working-directory: bindings/c
120+
if: runner.os != 'Windows'
109121
env:
110122
VERSION: "latest"
111123
SERVICE: ${{ matrix.service }}
112124
TARGET: ${{ matrix.build.target }}
113125
CC: ${{ matrix.build.cc }}
114126
OS: ${{ matrix.build.os }}
115127
run: |
116-
cargo build --target $TARGET --release
128+
cargo build --target $TARGET --release
117129
DIR=$GITHUB_WORKSPACE/libopendal_c_${VERSION}_${SERVICE}_$TARGET
118130
mkdir $DIR
119131
if [ ${OS} == 'ubuntu-latest' ]; then
@@ -122,29 +134,65 @@ jobs:
122134
SO=dylib
123135
fi
124136
zstd -19 ./target/$TARGET/release/libopendal_c.$SO -o $DIR/libopendal_c.$TARGET.$SO.zst
137+
- name: Build C Binding (Windows)
138+
working-directory: bindings/c
139+
if: runner.os == 'Windows'
140+
env:
141+
VERSION: "latest"
142+
SERVICE: ${{ matrix.service }}
143+
TARGET: ${{ matrix.build.target }}
144+
CC: ${{ matrix.build.cc }}
145+
OS: ${{ matrix.build.os }}
146+
run: |
147+
cargo build --target $env:TARGET --release
148+
$DIR="$env:GITHUB_WORKSPACE\libopendal_c_${env:VERSION}_${env:SERVICE}_${env:TARGET}"
149+
Rename-Item -Path "./target/$env:TARGET/release/opendal_c.dll" -NewName "libopendal_c.dll"
150+
New-Item -ItemType Directory -Force -Path $DIR
151+
zstd -19 "./target/${env:TARGET}/release/libopendal_c.dll" -o "$DIR/libopendal_c.${env:TARGET}.dll.zst"
125152
- name: Build Go Artifact
126153
working-directory: tools/internal/generate
127154
env:
128155
MATRIX: '{"build": [${{ toJson(matrix.build) }}], "service": ["${{ matrix.service }}"]}'
129156
VERSION: "latest"
130157
run: |
131158
go run generate.go
132-
- name: Setup Go Workspace
159+
- name: Setup Go Workspace (Linux/macOS)
133160
env:
134161
SERVICE: ${{ matrix.service }}
135162
working-directory: bindings/go/tests
163+
if: runner.os != 'Windows'
136164
run: |
137165
go work init
138166
go work use ..
139167
go work use ./behavior_tests
140168
go work use $GITHUB_WORKSPACE/$(echo $SERVICE | sed 's/-/_/g')
141-
- name: Run tests
169+
- name: Setup Go Workspace (Windows)
170+
env:
171+
SERVICE: ${{ matrix.service }}
172+
working-directory: bindings/go/tests
173+
if: runner.os == 'Windows'
174+
run: |
175+
go work init
176+
go work use ..
177+
go work use ./behavior_tests
178+
go work use $env:GITHUB_WORKSPACE/$($env:SERVICE -replace '-','_')
179+
- name: Run tests (Linux/macOS)
142180
env:
143181
OPENDAL_TEST: ${{ matrix.service }}
144-
OPENDAL_FS_ROOT: "/tmp/opendal/"
182+
OPENDAL_FS_ROOT: runner.temp
145183
working-directory: bindings/go/tests/behavior_tests
184+
if: runner.os != 'Windows'
146185
run: |
147186
if [ ${{ matrix.build.os }} == 'macos-latest' ]; then
148187
export DYLD_FALLBACK_LIBRARY_PATH=$DYLD_FALLBACK_LIBRARY_PATH:/opt/homebrew/opt/libffi/lib
149188
fi
150189
CGO_ENABLE=0 go test -v -run TestBehavior
190+
- name: Run tests (Windows)
191+
env:
192+
OPENDAL_TEST: ${{ matrix.service }}
193+
OPENDAL_FS_ROOT: runner.temp
194+
working-directory: bindings/go/tests/behavior_tests
195+
if: runner.os == 'Windows'
196+
run: |
197+
$env:CGO_ENABLE = "0"
198+
go test -v -run TestBehavior

bindings/go/delete.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import (
2424
"unsafe"
2525

2626
"github.com/jupiterrider/ffi"
27-
"golang.org/x/sys/unix"
2827
)
2928

3029
// Delete removes the file or directory at the specified path.
@@ -55,7 +54,7 @@ var withOperatorDelete = withFFI(ffiOpts{
5554
aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer},
5655
}, func(ctx context.Context, ffiCall func(rValue unsafe.Pointer, aValues ...unsafe.Pointer)) operatorDelete {
5756
return func(op *opendalOperator, path string) error {
58-
bytePath, err := unix.BytePtrFromString(path)
57+
bytePath, err := BytePtrFromString(path)
5958
if err != nil {
6059
return err
6160
}

bindings/go/ffi.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,11 @@ import (
2424
"errors"
2525
"unsafe"
2626

27-
"github.com/ebitengine/purego"
2827
"github.com/jupiterrider/ffi"
2928
)
3029

3130
func contextWithFFIs(path string) (ctx context.Context, cancel context.CancelFunc, err error) {
32-
libopendal, err := purego.Dlopen(path, purego.RTLD_LAZY|purego.RTLD_GLOBAL)
31+
libopendal, err := LoadLibrary(path)
3332
if err != nil {
3433
return
3534
}
@@ -41,7 +40,7 @@ func contextWithFFIs(path string) (ctx context.Context, cancel context.CancelFun
4140
}
4241
}
4342
cancel = func() {
44-
purego.Dlclose(libopendal)
43+
_ = FreeLibrary(libopendal)
4544
}
4645
return
4746
}
@@ -83,7 +82,7 @@ func withFFI[T any](
8382
); status != ffi.OK {
8483
return nil, errors.New(status.String())
8584
}
86-
fn, err := purego.Dlsym(libopendal, opts.sym.String())
85+
fn, err := GetProcAddress(libopendal, opts.sym.String())
8786
if err != nil {
8887
return nil, err
8988
}

bindings/go/go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ go 1.22.4
2222
toolchain go1.22.5
2323

2424
require (
25-
github.com/ebitengine/purego v0.7.1
26-
github.com/jupiterrider/ffi v0.1.0
25+
github.com/ebitengine/purego v0.8.2
26+
github.com/jupiterrider/ffi v0.4.0
2727
golang.org/x/sys v0.24.0
2828
)

bindings/go/go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
github.com/ebitengine/purego v0.7.1 h1:6/55d26lG3o9VCZX8lping+bZcmShseiqlh2bnUDiPA=
22
github.com/ebitengine/purego v0.7.1/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ=
3+
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
4+
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
35
github.com/jupiterrider/ffi v0.1.0 h1:OI6ZHZJW1Io1PcfqGeLk/CBLj+//8aNdOuo558ykQQo=
46
github.com/jupiterrider/ffi v0.1.0/go.mod h1:tyr9EitV+PW99I6137IDwdO6ZzNyFp/noXNSfU3OYqk=
7+
github.com/jupiterrider/ffi v0.4.0 h1:7mhlrfiBZa0kHhh2DV7mGAdXN/D8zDeu8UlaBO+ZSko=
8+
github.com/jupiterrider/ffi v0.4.0/go.mod h1:1QCaf2VVPpGyIeU3RqQ2rHYrAPT8m9l0GhQupVYQB24=
59
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
610
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=

bindings/go/lister.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import (
2424
"unsafe"
2525

2626
"github.com/jupiterrider/ffi"
27-
"golang.org/x/sys/unix"
2827
)
2928

3029
// Check verifies if the operator is functioning correctly.
@@ -312,7 +311,7 @@ var withOperatorList = withFFI(ffiOpts{
312311
aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer},
313312
}, func(ctx context.Context, ffiCall func(rValue unsafe.Pointer, aValues ...unsafe.Pointer)) operatorList {
314313
return func(op *opendalOperator, path string) (*opendalLister, error) {
315-
bytePath, err := unix.BytePtrFromString(path)
314+
bytePath, err := BytePtrFromString(path)
316315
if err != nil {
317316
return nil, err
318317
}
@@ -400,7 +399,7 @@ var withEntryName = withFFI(ffiOpts{
400399
unsafe.Pointer(&bytePtr),
401400
unsafe.Pointer(&e),
402401
)
403-
return unix.BytePtrToString(bytePtr)
402+
return BytePtrToString(bytePtr)
404403
}
405404
})
406405

@@ -419,6 +418,6 @@ var withEntryPath = withFFI(ffiOpts{
419418
unsafe.Pointer(&bytePtr),
420419
unsafe.Pointer(&e),
421420
)
422-
return unix.BytePtrToString(bytePtr)
421+
return BytePtrToString(bytePtr)
423422
}
424423
})

bindings/go/operator.go

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import (
2424
"unsafe"
2525

2626
"github.com/jupiterrider/ffi"
27-
"golang.org/x/sys/unix"
2827
)
2928

3029
// Copy duplicates a file from the source path to the destination path.
@@ -111,7 +110,7 @@ var withOperatorNew = withFFI(ffiOpts{
111110
}, func(ctx context.Context, ffiCall func(rValue unsafe.Pointer, aValues ...unsafe.Pointer)) operatorNew {
112111
return func(scheme Scheme, opts *operatorOptions) (op *opendalOperator, err error) {
113112
var byteName *byte
114-
byteName, err = unix.BytePtrFromString(scheme.Name())
113+
byteName, err = BytePtrFromString(scheme.Name())
115114
if err != nil {
116115
return
117116
}
@@ -177,11 +176,11 @@ var withOperatorOptionsSet = withFFI(ffiOpts{
177176
byteKey *byte
178177
byteValue *byte
179178
)
180-
byteKey, err = unix.BytePtrFromString(key)
179+
byteKey, err = BytePtrFromString(key)
181180
if err != nil {
182181
return err
183182
}
184-
byteValue, err = unix.BytePtrFromString(value)
183+
byteValue, err = BytePtrFromString(value)
185184
if err != nil {
186185
return err
187186
}
@@ -226,11 +225,11 @@ var withOperatorCopy = withFFI(ffiOpts{
226225
byteSrc *byte
227226
byteDest *byte
228227
)
229-
byteSrc, err = unix.BytePtrFromString(src)
228+
byteSrc, err = BytePtrFromString(src)
230229
if err != nil {
231230
return err
232231
}
233-
byteDest, err = unix.BytePtrFromString(dest)
232+
byteDest, err = BytePtrFromString(dest)
234233
if err != nil {
235234
return err
236235
}
@@ -259,11 +258,11 @@ var withOperatorRename = withFFI(ffiOpts{
259258
byteSrc *byte
260259
byteDest *byte
261260
)
262-
byteSrc, err = unix.BytePtrFromString(src)
261+
byteSrc, err = BytePtrFromString(src)
263262
if err != nil {
264263
return err
265264
}
266-
byteDest, err = unix.BytePtrFromString(dest)
265+
byteDest, err = BytePtrFromString(dest)
267266
if err != nil {
268267
return err
269268
}

bindings/go/operator_info.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import (
2424
"unsafe"
2525

2626
"github.com/jupiterrider/ffi"
27-
"golang.org/x/sys/unix"
2827
)
2928

3029
// Info returns metadata about the Operator.
@@ -325,7 +324,7 @@ var withOperatorInfoGetScheme = withFFI(ffiOpts{
325324
unsafe.Pointer(&bytePtr),
326325
unsafe.Pointer(&info),
327326
)
328-
return unix.BytePtrToString(bytePtr)
327+
return BytePtrToString(bytePtr)
329328
}
330329
})
331330

@@ -344,7 +343,7 @@ var withOperatorInfoGetRoot = withFFI(ffiOpts{
344343
unsafe.Pointer(&bytePtr),
345344
unsafe.Pointer(&info),
346345
)
347-
return unix.BytePtrToString(bytePtr)
346+
return BytePtrToString(bytePtr)
348347
}
349348
})
350349

@@ -363,6 +362,6 @@ var withOperatorInfoGetName = withFFI(ffiOpts{
363362
unsafe.Pointer(&bytePtr),
364363
unsafe.Pointer(&info),
365364
)
366-
return unix.BytePtrToString(bytePtr)
365+
return BytePtrToString(bytePtr)
367366
}
368367
})

bindings/go/reader.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import (
2525
"unsafe"
2626

2727
"github.com/jupiterrider/ffi"
28-
"golang.org/x/sys/unix"
2928
)
3029

3130
// Read reads the entire contents of the file at the specified path into a byte slice.
@@ -210,7 +209,7 @@ var withOperatorRead = withFFI(ffiOpts{
210209
aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer},
211210
}, func(ctx context.Context, ffiCall func(rValue unsafe.Pointer, aValues ...unsafe.Pointer)) operatorRead {
212211
return func(op *opendalOperator, path string) (opendalBytes, error) {
213-
bytePath, err := unix.BytePtrFromString(path)
212+
bytePath, err := BytePtrFromString(path)
214213
if err != nil {
215214
return opendalBytes{}, err
216215
}
@@ -234,7 +233,7 @@ var withOperatorReader = withFFI(ffiOpts{
234233
aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer},
235234
}, func(ctx context.Context, ffiCall func(rValue unsafe.Pointer, aValues ...unsafe.Pointer)) operatorReader {
236235
return func(op *opendalOperator, path string) (*opendalReader, error) {
237-
bytePath, err := unix.BytePtrFromString(path)
236+
bytePath, err := BytePtrFromString(path)
238237
if err != nil {
239238
return nil, err
240239
}

bindings/go/stat.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import (
2424
"unsafe"
2525

2626
"github.com/jupiterrider/ffi"
27-
"golang.org/x/sys/unix"
2827
)
2928

3029
// Stat retrieves metadata for the specified path.
@@ -111,7 +110,7 @@ var withOperatorStat = withFFI(ffiOpts{
111110
aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer},
112111
}, func(ctx context.Context, ffiCall func(rValue unsafe.Pointer, aValues ...unsafe.Pointer)) operatorStat {
113112
return func(op *opendalOperator, path string) (*opendalMetadata, error) {
114-
bytePath, err := unix.BytePtrFromString(path)
113+
bytePath, err := BytePtrFromString(path)
115114
if err != nil {
116115
return nil, err
117116
}
@@ -138,7 +137,7 @@ var withOperatorIsExists = withFFI(ffiOpts{
138137
aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer},
139138
}, func(ctx context.Context, ffiCall func(rValue unsafe.Pointer, aValues ...unsafe.Pointer)) operatorIsExist {
140139
return func(op *opendalOperator, path string) (bool, error) {
141-
bytePath, err := unix.BytePtrFromString(path)
140+
bytePath, err := BytePtrFromString(path)
142141
if err != nil {
143142
return false, err
144143
}

0 commit comments

Comments
 (0)