Skip to content
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .devcontainer/post_create.sh
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,10 @@ sudo make
sudo cp lib/*.a /usr/lib
sudo ln -s /usr/lib/libgtest.a /usr/local/lib/libgtest.a
sudo ln -s /usr/lib/libgtest_main.a /usr/local/lib/libgtest_main.a

# Setup for Zig binding
sudo apt install -y wget
wget -q https://github.com/marler8997/zigup/releases/download/v2022_08_25/zigup.ubuntu-latest-x86_64.zip
unzip zigup.ubuntu-latest-x86_64.zip -d /usr/bin
chmod +x /usr/bin/zigup
zigup master #TODO: replace to 0.11.0 (stable)
63 changes: 63 additions & 0 deletions .github/workflows/bindings_zig.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.

name: Bindings Zig CI

on:
push:
branches:
- main
tags:
- '*'
pull_request:
branches:
- main
paths:
- "bindings/zig/**"
- ".github/workflows/bindings_zig.yml"
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
cancel-in-progress: true

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: goto-bus-stop/setup-zig@v2
with:
version: master # 0.11.0 (nightly/master)
#version: 0.11.0 # TODO: replace to stable version - wait new zig release

- name: Setup Rust toolchain
uses: ./.github/actions/setup

- name: Build Zig binding
working-directory: "bindings/zig"
run: zig build opendal_c

- name: Check diff
run: git diff --exit-code

- name: Build and Run BDD tests
working-directory: "bindings/zig"
run: zig build test -fsummary
2 changes: 2 additions & 0 deletions bindings/zig/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
zig-cache/
zig-out/
53 changes: 53 additions & 0 deletions bindings/zig/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Contributing
- [Contributing](#contributing)
- [Setup](#setup)
- [Using a dev container environment](#using-a-dev-container-environment)
- [Bring your own toolbox](#bring-your-own-toolbox)
- [Build](#build)
- [Test](#test)
- [Docs](#docs)
- [Misc](#misc)

## Setup

### Using a dev container environment
OpenDAL provides a pre-configured [dev container](https://containers.dev/) that could be used in [Github Codespaces](https://github.com/features/codespaces), [VSCode](https://code.visualstudio.com/), [JetBrains](https://www.jetbrains.com/remote-development/gateway/), [JuptyerLab](https://jupyterlab.readthedocs.io/en/stable/). Please pick up your favourite runtime environment.
Comment thread
Xuanwo marked this conversation as resolved.

The fastest way is:

[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/apache/incubator-opendal?quickstart=1&machine=standardLinux32gb)

### Bring your own toolbox
To build OpenDAL Zig binding, the following is all you need:
- **Zig toolchain** is based on **LLVM-toolchain**(clang/++, libcxx, lld).

## Need
- opendal C binding

## Build
To build the library and header file.
```shell
zig build opendal_c
```

- zig build need add header file `opendal.h` is under `../c/include`
- The library is under `../../target/debug` or `../../target/release` after building.

To clean the build results.
```shell
zig build uninstall
```

## Test
To build and run the tests.
```shell
zig build test -fsummary
```

```text
Build Summary: 3/3 steps succeeded; 1/1 tests passed
test success
└─ run test 1 passed 960us MaxRSS:3M
└─ zig test Debug native success 982ms MaxRSS:164M
```

17 changes: 17 additions & 0 deletions bindings/zig/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# OpenDAL Zig Binding (WIP)

# Build Zig bindings

To compile OpenDAL from source code, you'll need:
- [Zig 0.11.0 or higher](https://ziglang.org/download), totally portable.

```bash
# build opendal_c library (call make -C ../c)
zig build opendal_c
# Build and run test
zig build test -fsummary
```

## License

[Apache v2.0](https://www.apache.org/licenses/LICENSE-2.0)
48 changes: 48 additions & 0 deletions bindings/zig/build.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
const std = @import("std");

pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});

// This function creates a module and adds it to the package's module set, making
// it available to other packages which depend on this one.
_ = b.addModule("opendal", .{
.source_file = .{
.path = "src/opendal.zig",
},
.dependencies = &.{},
});
// Creates a step for unit testing. This only builds the test executable
// but does not run it.
const unit_tests = b.addTest(.{
.root_source_file = .{
.path = "src/opendal.zig",
},
.target = target,
.optimize = optimize,
});
unit_tests.addIncludePath("../c/include");
if (optimize == .Debug)
unit_tests.addLibraryPath("../../target/debug")
else
unit_tests.addLibraryPath("../../target/release");
unit_tests.linkSystemLibrary("opendal_c");
unit_tests.linkLibC();

const opendal_c = buildOpendalC(b);
const make_opendal_c = b.step("opendal_c", "Build opendal_c library");
make_opendal_c.dependOn(&opendal_c.step);
const run_unit_tests = b.addRunArtifact(unit_tests);
const test_step = b.step("test", "Run opendal tests");
test_step.dependOn(&run_unit_tests.step);
}
fn buildOpendalC(b: *std.Build) *std.Build.Step.Run {
const rootdir = (comptime std.fs.path.dirname(@src().file) orelse null) ++ "/";
const opendalCdir = rootdir ++ "../c";
return b.addSystemCommand(&[_][]const u8{
"make",
"-C",
opendalCdir,
"build",
});
}
74 changes: 74 additions & 0 deletions bindings/zig/src/opendal.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
pub const opendal = @cImport(@cInclude("opendal.h"));
Comment thread
Xuanwo marked this conversation as resolved.

const std = @import("std");
const testing = std.testing;

test "Opendal BDD test" {
const c_str = [*:0]const u8; // const char*
// const zig_str = []const u8;

const OpendalBddTest = struct {
p: opendal.opendal_operator_ptr,
scheme: c_str,
path: c_str,
content: c_str,

pub fn init() Self {
var self: Self = undefined;
self.scheme = "memory";
self.path = "test";
self.content = "Hello, World!";

var options: opendal.opendal_operator_options = opendal.opendal_operator_options_new();
opendal.opendal_operator_options_set(&options, "root", "/myroot");

// Given A new OpenDAL Blocking Operator
self.p = opendal.opendal_operator_new(self.scheme, options);
defer opendal.opendal_operator_options_free(&options);
// try testing.expect(self.p != null);
return self;
}
pub fn deinit(self: *Self) void {
opendal.opendal_operator_free(&self.p);
}

const Self = @This();
};
var bddTest = OpendalBddTest.init();
defer bddTest.deinit();

// When Blocking write path "test" with content "Hello, World!"
const data: opendal.opendal_bytes = .{
.data = bddTest.content,
// c_str hasn't len field (.* is ptr)
.len = std.mem.len(bddTest.content),
};
const code = opendal.opendal_operator_blocking_write(bddTest.p, bddTest.path, data);
try testing.expectEqual(code, opendal.OPENDAL_OK);

// The blocking file "test" should exist
var e: opendal.opendal_result_is_exist = opendal.opendal_operator_is_exist(bddTest.p, bddTest.path);
try testing.expectEqual(e.code, opendal.OPENDAL_OK);
try testing.expect(e.is_exist);

// The blocking file "test" entry mode must be file
var s: opendal.opendal_result_stat = opendal.opendal_operator_stat(bddTest.p, bddTest.path);
try testing.expectEqual(s.code, opendal.OPENDAL_OK);
var meta: opendal.opendal_metadata = s.meta;
try testing.expect(opendal.opendal_metadata_is_file(&meta));

// The blocking file "test" content length must be 13
try testing.expectEqual(opendal.opendal_metadata_content_length(&meta), 13);
defer opendal.opendal_metadata_free(&meta);

// The blocking file "test" must have content "Hello, World!"
var r: opendal.opendal_result_read = opendal.opendal_operator_blocking_read(bddTest.p, bddTest.path);
defer opendal.opendal_bytes_free(r.data);
try testing.expect(r.code == opendal.OPENDAL_OK);
try testing.expectEqual(std.mem.len(r.data.*.data), std.mem.len(bddTest.content));

var count: usize = 0;
while (count < std.mem.len(r.data.*.data)) : (count += 1) {
try testing.expectEqual(bddTest.content[count], r.data.*.data[count]);
}
}