diff options
author | Andrew Clayton <a.clayton@nginx.com> | 2023-08-02 17:03:48 +0100 |
---|---|---|
committer | Andrew Clayton <a.clayton@nginx.com> | 2023-08-21 23:24:12 +0100 |
commit | d6ed6a219b31a58526721f96195c80061d41ce54 (patch) | |
tree | 17a1fd6ecf72a327916ff0f8bc7aaf85b981ceff /src | |
download | unit-wasm-d6ed6a219b31a58526721f96195c80061d41ce54.tar.gz unit-wasm-d6ed6a219b31a58526721f96195c80061d41ce54.tar.bz2 |
Initial commitv0.1.0
libunit-wasm and example C and Rust WebAssembly modules for NGINX Unit.
Co-developed-by: Timo Stark <t.stark@nginx.com>
Co-developed-by: Liam Crilly <liam@nginx.com>
Signed-off-by: Andrew Clayton <a.clayton@nginx.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/c/.gitignore | 1 | ||||
-rw-r--r-- | src/c/Makefile | 21 | ||||
-rw-r--r-- | src/c/include/unit/unit-wasm.h | 211 | ||||
-rw-r--r-- | src/c/libunit-wasm.c | 381 | ||||
-rw-r--r-- | src/rust/.gitignore | 4 | ||||
-rw-r--r-- | src/rust/Cargo.toml | 15 | ||||
-rw-r--r-- | src/rust/Makefile | 11 | ||||
-rw-r--r-- | src/rust/README.md | 18 | ||||
-rw-r--r-- | src/rust/src/ffi/mod.rs | 3 | ||||
-rw-r--r-- | src/rust/src/lib.rs | 8 | ||||
-rw-r--r-- | src/rust/unit-wasm-sys/Cargo.toml | 18 | ||||
-rw-r--r-- | src/rust/unit-wasm-sys/README.md | 4 | ||||
-rw-r--r-- | src/rust/unit-wasm-sys/build.rs | 52 | ||||
-rw-r--r-- | src/rust/unit-wasm-sys/lib.rs | 19 | ||||
l--------- | src/rust/unit-wasm-sys/libunit-wasm | 1 | ||||
-rw-r--r-- | src/rust/unit-wasm-sys/macros.rs | 15 |
16 files changed, 782 insertions, 0 deletions
diff --git a/src/c/.gitignore b/src/c/.gitignore new file mode 100644 index 0000000..a676009 --- /dev/null +++ b/src/c/.gitignore @@ -0,0 +1 @@ +libunit-wasm.a diff --git a/src/c/Makefile b/src/c/Makefile new file mode 100644 index 0000000..83262f6 --- /dev/null +++ b/src/c/Makefile @@ -0,0 +1,21 @@ +include ../../shared.mk + +CFLAGS += -Iinclude + +SDIR = src/c + +TARGETS = libunit-wasm.o libunit-wasm.a + +.PHONY: libunit-wasm +libunit-wasm: $(TARGETS) + +libunit-wasm.o: libunit-wasm.c include/unit/unit-wasm.h + $(PP_CC) $(SDIR)/$@ + $(v)$(CC) $(CFLAGS) -fvisibility=hidden -c $< + +libunit-wasm.a: libunit-wasm.o + $(PP_AR) $(SDIR)/$@ + $(v)llvm-ar rcs $@ $< + +clean: + rm -f *.o *.a *.gch diff --git a/src/c/include/unit/unit-wasm.h b/src/c/include/unit/unit-wasm.h new file mode 100644 index 0000000..df3852a --- /dev/null +++ b/src/c/include/unit/unit-wasm.h @@ -0,0 +1,211 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +/* + * Copyright (C) Andrew Clayton + * Copyright (C) F5, Inc. + */ + +#ifndef _UNIT_WASM_H_ +#define _UNIT_WASM_H_ + +#include <stddef.h> +#include <stdint.h> +#include <stdbool.h> +#include <strings.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define LUW_VERSION_MAJOR 0 +#define LUW_VERSION_MINOR 1 +#define LUW_VERSION_PATCH 0 + +/* Version number in hex 0xMMmmpp00 */ +#define LUW_VERSION_NUMBER \ + ( (LUW_VERSION_MAJOR << 24) | \ + (LUW_VERSION_MINOR << 16) | \ + (LUW_VERSION_PATCH << 8) ) + +#define __luw_export_name(name) __attribute__((export_name(name))) + +#define __luw_unused __attribute__((unused)) +#define __luw_maybe_unused __luw_unused + +typedef uint64_t u64; +typedef int64_t s64; +typedef uint32_t u32; +typedef int32_t s32; +typedef uint16_t u16; +typedef int16_t s16; +typedef uint8_t u8; +typedef int8_t s8; + +struct luw_hdr_field { + u32 name_off; + u32 name_len; + u32 value_off; + u32 value_len; +}; + +struct luw_req { + u32 method_off; + u32 method_len; + u32 version_off; + u32 version_len; + u32 path_off; + u32 path_len; + u32 query_off; + u32 query_len; + u32 remote_off; + u32 remote_len; + u32 local_addr_off; + u32 local_addr_len; + u32 local_port_off; + u32 local_port_len; + u32 server_name_off; + u32 server_name_len; + + u32 content_off; + u32 content_len; + u32 content_sent; + u32 total_content_sent; + + u32 request_size; + + u32 nr_fields; + + u32 tls; + + struct luw_hdr_field fields[]; +}; + +struct luw_resp { + u32 size; + + u8 data[]; +}; + +struct luw_resp_hdr { + u32 nr_fields; + + struct luw_hdr_field fields[]; +}; + +typedef struct { + /* pointer to the shared memory */ + u8 *addr; + + /* points to the end of ctx->resp->data */ + u8 *mem; + + /* struct luw_req representation of the shared memory */ + struct luw_req *req; + + /* struct luw_resp representation of the shared memory */ + struct luw_resp *resp; + + /* struct luw_resp_hdr represnetation of the shared memory */ + struct luw_resp_hdr *resp_hdr; + + /* offset to where the struct resp starts in the shared memory */ + size_t resp_offset; + + /* points to the external buffer used for a copy of the request */ + u8 *req_buf; + + /* points to the end of the fields array in struct luw_resp_hdr */ + u8 *hdrp; + + /* points to the end of ctx->req_buf */ + u8 *reqp; +} luw_ctx_t; + +typedef enum { + LUW_SRB_NONE = 0x00, + LUW_SRB_APPEND = 0x01, + LUW_SRB_ALLOC = 0x02, + LUW_SRB_FULL_SIZE = 0x04, +} luw_srb_flags_t; +#define LUW_SRB_FLAGS_ALL \ + (LUW_SRB_NONE|LUW_SRB_APPEND|LUW_SRB_ALLOC|LUW_SRB_FULL_SIZE) + +typedef struct luw_hdr_field luw_http_hdr_iter_t; + +#define luw_foreach_http_hdr(ctx, iter, name, value) \ + for (iter = ctx.req->fields, \ + name = (const char *)ctx.req + iter->name_off; \ + (iter < (ctx.req->fields + ctx.req->nr_fields)) && \ + (value = (const char *)ctx.req + iter->value_off); \ + iter++, name = (const char *)ctx.req + iter->name_off) + +/* Imported functions from the host/runtime */ +__attribute__((import_module("env"), import_name("nxt_wasm_get_init_mem_size"))) +u32 nxt_wasm_get_init_mem_size(void); +__attribute__((import_module("env"), import_name("nxt_wasm_response_end"))) +void nxt_wasm_response_end(void); +__attribute__((import_module("env"), import_name("nxt_wasm_send_headers"))) +void nxt_wasm_send_headers(u32 offset); +__attribute__((import_module("env"), import_name("nxt_wasm_send_response"))) +void nxt_wasm_send_response(u32 offset); + +extern void luw_module_init_handler(void); +extern void luw_module_end_handler(void); +extern void luw_request_init_handler(void); +extern void luw_request_end_handler(void); +extern void luw_response_end_handler(void); +extern int luw_request_handler(u8 *addr); +extern void luw_free_handler(u32 addr); +extern u32 luw_malloc_handler(size_t size); + +#pragma GCC visibility push(default) + +extern void luw_init_ctx(luw_ctx_t *ctx, u8 *addr, size_t offset); +extern void luw_destroy_ctx(const luw_ctx_t *ctx); +extern int luw_set_req_buf(luw_ctx_t *ctx, u8 **buf, unsigned long flags); +extern const char *luw_get_http_path(const luw_ctx_t *ctx); +extern const char *luw_get_http_method(const luw_ctx_t *ctx); +extern const char *luw_get_http_version(const luw_ctx_t *ctx); +extern const char *luw_get_http_query(const luw_ctx_t *ctx); +extern const char *luw_get_http_remote(const luw_ctx_t *ctx); +extern const char *luw_get_http_local_addr(const luw_ctx_t *ctx); +extern const char *luw_get_http_local_port(const luw_ctx_t *ctx); +extern const char *luw_get_http_server_name(const luw_ctx_t *ctx); +extern const u8 *luw_get_http_content(const luw_ctx_t *ctx); +extern size_t luw_get_http_content_len(const luw_ctx_t *ctx); +extern size_t luw_get_http_content_sent(const luw_ctx_t *ctx); +extern bool luw_http_is_tls(const luw_ctx_t *ctx); +extern void luw_http_hdr_iter(luw_ctx_t *ctx, + bool (*luw_http_hdr_iter_func)(luw_ctx_t *ctx, + const char *name, + const char *value, + void *data), + void *user_data); +extern const char *luw_http_hdr_get_value(luw_ctx_t *ctx, const char *hdr); +extern size_t luw_get_response_data_size(const luw_ctx_t *ctx); +extern int luw_mem_writep(luw_ctx_t *ctx, const char *fmt, ...); +extern size_t luw_mem_writep_data(luw_ctx_t *ctx, const u8 *src, size_t size); +extern void luw_req_buf_append(luw_ctx_t *ctx, const u8 *src); +extern size_t luw_mem_fill_buf_from_req(luw_ctx_t *ctx, size_t from); +extern void luw_mem_reset(luw_ctx_t *ctx); +extern void luw_http_send_response(const luw_ctx_t *ctx); +extern void luw_http_init_headers(luw_ctx_t *ctx, size_t nr, size_t offset); +extern void luw_http_add_header(luw_ctx_t *ctx, u16 idx, const char *name, + const char *value); +extern void luw_http_send_headers(const luw_ctx_t *ctx); +extern void luw_http_response_end(void); +extern u32 luw_mem_get_init_size(void); + +/* + * Convenience wrappers for the Rust bindings, not for general consumption. + */ +extern void *luw_malloc(size_t size); +extern void luw_free(void *ptr); + +#pragma GCC visibility pop + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _UNIT_WASM_H_ */ diff --git a/src/c/libunit-wasm.c b/src/c/libunit-wasm.c new file mode 100644 index 0000000..1860fe3 --- /dev/null +++ b/src/c/libunit-wasm.c @@ -0,0 +1,381 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +/* + * Copyright (C) Andrew Clayton + * Copyright (C) F5, Inc. + */ + +#define _GNU_SOURCE + +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <stdarg.h> +#include <string.h> +#include <errno.h> + +#include "unit/unit-wasm.h" + +/* + * Some handlers are required some are optional. + * + * They are defined as _weak_ symbols so they can be overridden + * in the module. + */ + +/* Optional Handlers */ +__attribute__((export_name("luw_module_init_handler"), __weak__)) +void luw_module_init_handler(void) +{ +} + +__attribute__((export_name("luw_module_end_handler"), __weak__)) +void luw_module_end_handler(void) +{ +} + +__attribute__((export_name("luw_request_init_handler"), __weak__)) +void luw_request_init_handler(void) +{ +} + +__attribute__((export_name("luw_request_end_handler"), __weak__)) +void luw_request_end_handler(void) +{ +} + +__attribute__((export_name("luw_response_end_handler"), __weak__)) +void luw_response_end_handler(void) +{ +} + +/* Required Handlers */ +__attribute__((export_name("luw_request_handler"), __weak__)) +int luw_request_handler(u8 *addr) +{ + (void)addr; + + return 0; +} + +__attribute__((export_name("luw_free_handler"), __weak__)) +void luw_free_handler(u32 addr) +{ + free((void *)addr); +} + +__attribute__((export_name("luw_malloc_handler"), __weak__)) +u32 luw_malloc_handler(size_t size) +{ + return (u32)malloc(size); +} + +void luw_init_ctx(luw_ctx_t *ctx, u8 *addr, size_t offset) +{ + *ctx = (luw_ctx_t){ }; + + ctx->addr = addr; + ctx->req = (struct luw_req *)addr; + ctx->resp = (struct luw_resp *)(addr + offset); + ctx->mem = ctx->resp->data; + ctx->resp_offset = offset; + ctx->resp->size = 0; + ctx->resp_hdr->nr_fields = 0; +} + +/* + * Allows to set an external buffer to be used as a copy for + * the request. + * + * The flags dictate a few behaviours + * + * LUW_SRB_NONE - No specific action to be performed. It will + * simply copy the request data into the specified + * buffer. + * + * LUW_SRB_APPEND - Sets up append mode whereby multiple successive + * requests will be appended to the specified buffer. + * + * The first request will have all its metadata + * copied. Subsequent requests will _only_ have the + * actual body data appended. + * + * LUW_SRB_ALLOC - Allocate memory for the specified buffer. + * + * LUW_SRB_FULL_SIZE - Used in conjunction with LUW_SRB_ALLOC. By + * default only ctx->req->request_size is + * allocated. If this flag is present it says to + * allocate memory for the _entire_ request that + * will eventually be sent. + */ +int luw_set_req_buf(luw_ctx_t *ctx, u8 **buf, unsigned long flags) +{ + size_t alloc_size; + size_t copy_bytes; + + /* Check for unknown flags */ + if (flags & ~LUW_SRB_FLAGS_ALL) { + errno = EINVAL; + return -1; + } + + /* Check for invalid combinations of flags */ + if (flags & LUW_SRB_FULL_SIZE && !(flags & LUW_SRB_ALLOC)) { + errno = EINVAL; + return -1; + } + + alloc_size = copy_bytes = ctx->req->request_size; + + if (flags & LUW_SRB_FULL_SIZE) + alloc_size = ctx->req->content_off + ctx->req->content_len; + + if (flags & LUW_SRB_ALLOC) { + *buf = malloc(alloc_size); + if (!*buf) + return -1; + } + + memcpy(*buf, ctx->addr, copy_bytes); + ctx->req_buf = *buf; + ctx->req = (struct luw_req *)ctx->req_buf; + ctx->reqp = ctx->req_buf; + + if (flags & LUW_SRB_APPEND) + ctx->reqp = ctx->req_buf + copy_bytes; + + return 0; +} + +const char *luw_get_http_path(const luw_ctx_t *ctx) +{ + return (const char *)ctx->req + ctx->req->path_off; +} + +const char *luw_get_http_method(const luw_ctx_t *ctx) +{ + return (const char *)ctx->req + ctx->req->method_off; +} + +const char *luw_get_http_version(const luw_ctx_t *ctx) +{ + return (const char *)ctx->req + ctx->req->version_off; +} + +const char *luw_get_http_query(const luw_ctx_t *ctx) +{ + return (const char *)ctx->req + ctx->req->query_off; +} + +const char *luw_get_http_remote(const luw_ctx_t *ctx) +{ + return (const char *)ctx->req + ctx->req->remote_off; +} + +const char *luw_get_http_local_addr(const luw_ctx_t *ctx) +{ + return (const char *)ctx->req + ctx->req->local_addr_off; +} + +const char *luw_get_http_local_port(const luw_ctx_t *ctx) +{ + return (const char *)ctx->req + ctx->req->local_port_off; +} + +const char *luw_get_http_server_name(const luw_ctx_t *ctx) +{ + return (const char *)ctx->req + ctx->req->server_name_off; +} + +const u8 *luw_get_http_content(const luw_ctx_t *ctx) +{ + return (u8 *)ctx->req + ctx->req->content_off; +} + +/* Returns the size of the overall content length */ +size_t luw_get_http_content_len(const luw_ctx_t *ctx) +{ + return ctx->req->content_len; +} + +/* Returns the size of the content sent in _this_ request */ +size_t luw_get_http_content_sent(const luw_ctx_t *ctx) +{ + return ctx->req->content_sent; +} + +bool luw_http_is_tls(const luw_ctx_t *ctx) +{ + return ctx->req->tls; +} + +void luw_http_hdr_iter(luw_ctx_t *ctx, + bool (*luw_http_hdr_iter_func)(luw_ctx_t *ctx, + const char *name, + const char *value, + void *data), + void *user_data) +{ + struct luw_hdr_field *hf; + struct luw_hdr_field *hf_end; + + hf_end = ctx->req->fields + ctx->req->nr_fields; + for (hf = ctx->req->fields; hf < hf_end; hf++) { + const char *name = (const char *)ctx->req + hf->name_off; + const char *value = (const char *)ctx->req + hf->value_off; + bool again; + + again = luw_http_hdr_iter_func(ctx, name, value, user_data); + if (!again) + break; + } +} + +const char *luw_http_hdr_get_value(luw_ctx_t *ctx, const char *hdr) +{ + luw_http_hdr_iter_t *iter; + const char *name; + const char *value; + + luw_foreach_http_hdr((*ctx), iter, name, value) { + if (strcasecmp(name, hdr) == 0) + return value; + } + + return NULL; +} + +/* Returns the size of ctx->resp->data[] */ +size_t luw_get_response_data_size(const luw_ctx_t *ctx) +{ + return ctx->mem - ctx->resp->data; +} + +/* Appends (non-nul terminmates) formatted data to the response buffer */ +__attribute__((__format__(printf, 2, 3))) +int luw_mem_writep(luw_ctx_t *ctx, const char *fmt, ...) +{ + int len; + char *logbuf; + va_list ap; + + va_start(ap, fmt); + len = vasprintf(&logbuf, fmt, ap); + if (len == -1) { + va_end(ap); + return -1; + } + va_end(ap); + + ctx->mem = mempcpy(ctx->mem, logbuf, len); + ctx->resp->size += len; + + free(logbuf); + + return len; +} + +/* Appends data of length size to the response buffer */ +size_t luw_mem_writep_data(luw_ctx_t *ctx, const u8 *src, size_t size) +{ + ctx->mem = mempcpy(ctx->mem, src, size); + ctx->resp->size += size; + + return ctx->resp->size; +} + +/* Append data from the request to the previously setup request_buffer. */ +void luw_req_buf_append(luw_ctx_t *ctx, const u8 *src) +{ + struct luw_req *req = (struct luw_req *)src; + + ctx->reqp = mempcpy(ctx->reqp, src + req->content_off, + req->request_size); + ctx->req->content_sent = req->content_sent; + ctx->req->total_content_sent = req->total_content_sent; +} + +/* + * Convenience function to fill the response buffer with data from + * the request buffer. + * + * The runtime allocates NXT_WASM_MEM_SIZE + NXT_WASM_PAGE_SIZE + * bytes so we don't need to worry about the size of the actual + * response structures. + */ +size_t luw_mem_fill_buf_from_req(luw_ctx_t *ctx, size_t from) +{ + size_t write_bytes; + size_t mem_size = nxt_wasm_get_init_mem_size(); + + write_bytes = ctx->req->content_sent; + if (write_bytes > mem_size) + write_bytes = mem_size; + + memcpy(ctx->resp->data, ctx->req_buf + ctx->req->content_off + from, + write_bytes); + ctx->resp->size = write_bytes; + + return write_bytes; +} + +void luw_mem_reset(luw_ctx_t *ctx) +{ + ctx->mem = ctx->resp->data; + ctx->resp->size = 0; + ctx->resp_hdr->nr_fields = 0; +} + +void luw_http_send_response(const luw_ctx_t *ctx) +{ + nxt_wasm_send_response(ctx->resp_offset); +} + +void luw_http_init_headers(luw_ctx_t *ctx, size_t nr, size_t offset) +{ + ctx->resp_hdr = (struct luw_resp_hdr *)(ctx->addr + offset); + ctx->hdrp = (u8 *)ctx->resp_hdr + sizeof(struct luw_resp_hdr) + + (nr * sizeof(struct luw_hdr_field)); + + ctx->resp_hdr->nr_fields = nr; +} + +void luw_http_add_header(luw_ctx_t *ctx, u16 idx, const char *name, + const char *value) +{ + ctx->resp_hdr->fields[idx].name_off = ctx->hdrp - ctx->addr; + ctx->resp_hdr->fields[idx].name_len = strlen(name); + ctx->hdrp = mempcpy(ctx->hdrp, name, strlen(name)); + + ctx->resp_hdr->fields[idx].value_off = ctx->hdrp - ctx->addr; + ctx->resp_hdr->fields[idx].value_len = strlen(value); + ctx->hdrp = mempcpy(ctx->hdrp, value, strlen(value)); +} + +void luw_http_send_headers(const luw_ctx_t *ctx) +{ + nxt_wasm_send_headers((u8 *)ctx->resp_hdr - ctx->addr); +} + +void luw_http_response_end(void) +{ + nxt_wasm_response_end(); +} + +u32 luw_mem_get_init_size(void) +{ + return nxt_wasm_get_init_mem_size(); +} + +/* These are really just convenience wrappers for the rust bindings... */ + +void *luw_malloc(size_t size) +{ + return malloc(size); +} + +void luw_free(void *ptr) +{ + free(ptr); +} diff --git a/src/rust/.gitignore b/src/rust/.gitignore new file mode 100644 index 0000000..6c481e2 --- /dev/null +++ b/src/rust/.gitignore @@ -0,0 +1,4 @@ +bindings.rs +Cargo.lock + +target/ diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml new file mode 100644 index 0000000..2e0bba2 --- /dev/null +++ b/src/rust/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "unit-wasm" +version = "0.1.0" +authors = ["Timo Stark <t.stark@f5.com>", "Andrew Clayton <a.clayton@f5.com>"] +description = "WASM SDK for NGINX Unit" +license = "Apache-2.0" +keywords = ["nginx", "unit", "wasm", "wasi"] + +[lib] +crate-type = ["rlib"] +path = "src/lib.rs" + + +[dependencies] +unit-wasm-sys = { path = "unit-wasm-sys", version = "0.1.1" } diff --git a/src/rust/Makefile b/src/rust/Makefile new file mode 100644 index 0000000..d2e705a --- /dev/null +++ b/src/rust/Makefile @@ -0,0 +1,11 @@ +include ../../shared.mk + +SDIR = src/rust + +rustlib: + $(PP_GEN) $(SDIR)/target/wasm32-wasi + $(v)cargo build --target=wasm32-wasi + +clean: + rm -f Cargo.lock unit-wasm-sys/Cargo.lock + rm -rf target/ unit-wasm-sys/target/ diff --git a/src/rust/README.md b/src/rust/README.md new file mode 100644 index 0000000..a48e2cd --- /dev/null +++ b/src/rust/README.md @@ -0,0 +1,18 @@ +# Quickstart in Developing Rust WebAssembly Modules for Unit + +The current version is published to crates.io. To get started with the SDK +include `unit-wasm` as dependency. + +``` +cargo add unit-wasm +``` + +## Prerequisites + +- target add wasm32-wasi. `rustup target add wasm32-wasi` + +## From Source + +The Rust implementation is in an early stage. If you would like to build the +crate by yourself, we have to generate the `libunit-wasm` first. This step is +NOT included in the build process. diff --git a/src/rust/src/ffi/mod.rs b/src/rust/src/ffi/mod.rs new file mode 100644 index 0000000..087148a --- /dev/null +++ b/src/rust/src/ffi/mod.rs @@ -0,0 +1,3 @@ +// @todo: Check if this is valid?! +extern crate unit_wasm_sys; +pub use self::unit_wasm_sys::*; diff --git a/src/rust/src/lib.rs b/src/rust/src/lib.rs new file mode 100644 index 0000000..dface32 --- /dev/null +++ b/src/rust/src/lib.rs @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +/* + * Copyright (C) Timo Stark + * Copyright (C) F5, Inc. + */ + +pub mod ffi; diff --git a/src/rust/unit-wasm-sys/Cargo.toml b/src/rust/unit-wasm-sys/Cargo.toml new file mode 100644 index 0000000..d8f64a3 --- /dev/null +++ b/src/rust/unit-wasm-sys/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "unit-wasm-sys" +version = "0.1.1" +edition = "2021" +authors = ["Timo Stark <t.stark@nginx.com>", "Andrew Clayton <a.clayton@nginx.com>"] +links = "unit-wasm" +description = "Native bindings for the libunit-wasm C API" +license = "Apache-2.0" +keywords = ["nginx", "unit", "ffi", "sys"] + +[lib] +name = "unit_wasm_sys" +path = "lib.rs" +crate-type = ["staticlib", "rlib"] + + +[build-dependencies] +bindgen = "0.66.1" diff --git a/src/rust/unit-wasm-sys/README.md b/src/rust/unit-wasm-sys/README.md new file mode 100644 index 0000000..fbc53f0 --- /dev/null +++ b/src/rust/unit-wasm-sys/README.md @@ -0,0 +1,4 @@ +# unit-wasm sys crate + +The symbolic link is beeing used to share the c code with cargo during +buildtime. diff --git a/src/rust/unit-wasm-sys/build.rs b/src/rust/unit-wasm-sys/build.rs new file mode 100644 index 0000000..bd2ebd5 --- /dev/null +++ b/src/rust/unit-wasm-sys/build.rs @@ -0,0 +1,52 @@ +// buildscript for the unit-wasm-sys crate. + +use std::env; +use std::path::{Path, PathBuf}; + +fn main() { + // Tell rustc where to find the libunit-wasm library. + let libunit_wasm_dir = "libunit-wasm"; + + let dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + + // Some generics + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=wrapper.h"); + + // The rustc-link-search tells Cargo to pass the `-L` flag to the + // compiler to add a directory to the library search plugin. The + // `native` keyword means "only looking for `native libraries` in + // this directory". + println!( + "cargo:rustc-link-search=native={}", + Path::new(&dir).join(libunit_wasm_dir).display() + ); + + // The rustc-link-lib tells Cargo to link the given library using + // the compiler's `-l` flag. This is needed to start building our + // FFIs. + println!("cargo:rustc-link-lib=static=unit-wasm"); + + generate_bindings(); +} + +fn generate_bindings() { + let wasi_sysroot = "--sysroot=".to_owned() + &env::var("WASI_SYSROOT").unwrap(); + let bindings = bindgen::Builder::default() + // The input header file. + .header("libunit-wasm/include/unit/unit-wasm.h") + .allowlist_function("^luw_.*") + .allowlist_var("^luw_.*") + .allowlist_type("^luw_.*") + .clang_args(vec![wasi_sysroot]) // Needed for strings.h + .generate() + .expect("Unable to generate bindings"); + + let out_dir_env = + env::var("OUT_DIR").expect("The required environment variable OUT_DIR was not set"); + let out_path = PathBuf::from(out_dir_env); + + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} diff --git a/src/rust/unit-wasm-sys/lib.rs b/src/rust/unit-wasm-sys/lib.rs new file mode 100644 index 0000000..9ef60f6 --- /dev/null +++ b/src/rust/unit-wasm-sys/lib.rs @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +/* + * Copyright (C) Timo Stark + * Copyright (C) F5, Inc. + */ + +#[doc(hidden)] +mod bindings { + #![allow(non_upper_case_globals)] + #![allow(non_camel_case_types)] + #![allow(dead_code)] + + include!(concat!(env!("OUT_DIR"), "/bindings.rs")); + include!("macros.rs"); +} + +#[doc(no_inline)] +pub use bindings::*; diff --git a/src/rust/unit-wasm-sys/libunit-wasm b/src/rust/unit-wasm-sys/libunit-wasm new file mode 120000 index 0000000..2fcc511 --- /dev/null +++ b/src/rust/unit-wasm-sys/libunit-wasm @@ -0,0 +1 @@ +../../c
\ No newline at end of file diff --git a/src/rust/unit-wasm-sys/macros.rs b/src/rust/unit-wasm-sys/macros.rs new file mode 100644 index 0000000..d7fde22 --- /dev/null +++ b/src/rust/unit-wasm-sys/macros.rs @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +/* + * Copyright (C) Andrew Clayton + * Copyright (C) F5, Inc. + */ + +pub const LUW_VERSION_MAJOR: i32 = 0; +pub const LUW_VERSION_MINOR: i32 = 1; +pub const LUW_VERSION_PATCH: i32 = 0; + +pub const LUW_VERSION_NUMBER: i32 = + (LUW_VERSION_MAJOR << 24) | + (LUW_VERSION_MINOR << 16) | + (LUW_VERSION_PATCH << 8); |