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
16 changes: 16 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Keep the build context small. CI builds from a fresh checkout, but a local
# working copy carries a multi-gigabyte target/ dir that would otherwise be
# uploaded to the daemon on every `docker buildx build`.
target/
**/target/

.git/
.github/
Docker/

# Matches the .gitignore entries that never belong in a build.
my-*
.claude/
.vscode/
**/.DS_Store
**/*.rs.bk
5 changes: 4 additions & 1 deletion .github/workflows/libvmexeccapi-build-linux-arm64.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ jobs:
context: .
load: true
platforms: linux/arm64
file: ./Docker/arm64.dockerfile
file: ./Docker/linux.dockerfile
build-args: |
MAKE_TARGET=capi-linux-arm
ARTIFACT_NAME=libvmexeccapi_arm.so
tags: mx-builder

- name: Copy library from Docker image
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/libvmexeccapi-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
- name: Install rust
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: "1.92"
toolchain: "1.98"

- name: Checkout
uses: actions/checkout@v4
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/rust-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ jobs:
- uses: actions-rs/toolchain@v1
with:
default: true
toolchain: "1.92"
toolchain: "1.98"
- name: Run rust tests
run: cargo +1.92 test --release
run: cargo +1.98 test --release
clippy_check:
permissions: write-all
name: Clippy linter check
Expand All @@ -26,7 +26,7 @@ jobs:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
toolchain: "1.98"
components: clippy
default: true
- uses: actions-rs/clippy-check@v1
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,8 @@ Cargo.lock

# Personal scripts
my-*

# Other
.vscode
**/.DS_Store
.claude
14 changes: 0 additions & 14 deletions Docker/arm64.dockerfile

This file was deleted.

21 changes: 21 additions & 0 deletions Docker/linux.dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Builds libvmexeccapi for Linux, amd64 or arm64. The caller picks the arch via
# `docker buildx build --platform`; the official `rust` image is multi-arch, so
# the same tag resolves to the right base image on either platform.
FROM rust:1.98

# ARTIFACT_NAME must match what MAKE_TARGET produces in target/release (see Makefile).
ARG MAKE_TARGET=capi-linux-amd64
ARG ARTIFACT_NAME=libvmexeccapi.so

# wget and git already ship in the base image; only these two are new.
RUN apt-get update && apt-get install -y --no-install-recommends \
patchelf \
build-essential \
&& rm -rf /var/lib/apt/lists/*

ENV CARGO_NET_GIT_FETCH_WITH_CLI=true

COPY . /repository
WORKDIR /repository
RUN make ${MAKE_TARGET}
RUN mkdir /data && cp /repository/target/release/${ARTIFACT_NAME} /data/${ARTIFACT_NAME}
89 changes: 81 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
.PHONY: clean
.PHONY: capi capi-linux-amd64 capi-linux-arm capi-osx-amd64 capi-osx-arm \
capi-osx-amd64-cross capi-linux-amd64-docker capi-linux-arm64-docker \
ci-local clean check-macos

# cargo and docker already parallelise internally, and a parallel make would
# have two cargo invocations contend for the same target dir lock.
.NOTPARALLEL:

# Where `ci-local` gathers the artifacts, so the cross and container builds
# stay out of the host's target/release.
CI_LOCAL_DIR ?= target/ci-local

UNAME_S := $(shell uname -s)

capi:
cargo build -p multiversx-chain-vm-executor-c-api --release
Expand All @@ -11,18 +23,79 @@ capi-linux-arm: capi
mv target/release/libmultiversx_chain_vm_executor_c_api.so target/release/libvmexeccapi_arm.so
patchelf --set-soname libvmexeccapi_arm.so target/release/libvmexeccapi_arm.so

capi-osx-amd64: capi
# A prerequisite, not an inline check in the recipe: under .NOTPARALLEL, make
# runs prerequisites in order and stops at the first failure, so listing this
# before `capi` below rejects a non-macOS host before the (potentially
# lengthy) cargo build starts, rather than after it.
check-macos:
@test "$(UNAME_S)" = "Darwin" || { echo "This target requires a macOS host (produces a Mach-O .dylib)." >&2; exit 1; }

capi-osx-amd64: check-macos capi
mv target/release/libmultiversx_chain_vm_executor_c_api.dylib target/release/libvmexeccapi.dylib
install_name_tool -id @rpath/libvmexeccapi.dylib target/release/libvmexeccapi.dylib

capi-osx-arm: capi
capi-osx-arm: check-macos capi
mv target/release/libmultiversx_chain_vm_executor_c_api.dylib target/release/libvmexeccapi_arm.dylib
install_name_tool -id @rpath/libvmexeccapi_arm.dylib target/release/libvmexeccapi_arm.dylib

# --- Local reproduction of the CI build matrix -------------------------------
# The four targets above are what CI runs natively, one per runner. The targets
# below drive the same builds from a single host: the two Linux libraries build
# in containers (any Docker host, cross-arch via buildx/QEMU), the way the
# libvmexeccapi-build-linux-arm64 workflow does. The two macOS libraries have no
# such option — Apple's terms don't allow macOS in a container, which is why CI
# itself uses real macos-15-intel/macos-latest runners rather than Docker for
# them — so they only run on a macOS host, and fail fast with an explanation
# everywhere else.

capi-osx-amd64-cross:
@test "$(UNAME_S)" = "Darwin" || { \
echo "capi-osx-amd64-cross requires a macOS host: cross-compiling to Darwin" >&2; \
echo "needs Apple's linker/SDK, which Docker and a plain Linux toolchain don't" >&2; \
echo "have. Run this on a Mac, or rely on the CI runner." >&2; \
exit 1; \
}
rustup target add x86_64-apple-darwin
cargo build -p multiversx-chain-vm-executor-c-api --release --target x86_64-apple-darwin
cp target/x86_64-apple-darwin/release/libmultiversx_chain_vm_executor_c_api.dylib target/x86_64-apple-darwin/release/libvmexeccapi.dylib
install_name_tool -id @rpath/libvmexeccapi.dylib target/x86_64-apple-darwin/release/libvmexeccapi.dylib

capi-linux-amd64-docker:
docker buildx build --platform linux/amd64 --file Docker/linux.dockerfile \
--build-arg MAKE_TARGET=capi-linux-amd64 --build-arg ARTIFACT_NAME=libvmexeccapi.so \
--tag mx-builder-amd64 --load .
mkdir -p $(CI_LOCAL_DIR)
docker run --platform linux/amd64 --rm mx-builder-amd64 cat /data/libvmexeccapi.so > $(CI_LOCAL_DIR)/libvmexeccapi.so

capi-linux-arm64-docker:
docker buildx build --platform linux/arm64 --file Docker/linux.dockerfile \
--build-arg MAKE_TARGET=capi-linux-arm --build-arg ARTIFACT_NAME=libvmexeccapi_arm.so \
--tag mx-builder-arm64 --load .
mkdir -p $(CI_LOCAL_DIR)
docker run --platform linux/arm64 --rm mx-builder-arm64 cat /data/libvmexeccapi_arm.so > $(CI_LOCAL_DIR)/libvmexeccapi_arm.so

ci-local: capi-linux-amd64-docker capi-linux-arm64-docker
ifeq ($(UNAME_S),Darwin)
ci-local: capi-osx-arm capi-osx-amd64-cross
endif
mkdir -p $(CI_LOCAL_DIR)
cp c-api/libvmexeccapi.h $(CI_LOCAL_DIR)/libvmexeccapi.h
@if [ "$(UNAME_S)" = "Darwin" ]; then \
cp target/release/libvmexeccapi_arm.dylib $(CI_LOCAL_DIR)/libvmexeccapi_arm.dylib; \
cp target/x86_64-apple-darwin/release/libvmexeccapi.dylib $(CI_LOCAL_DIR)/libvmexeccapi.dylib; \
else \
echo "--- Not on macOS: skipping libvmexeccapi.dylib / libvmexeccapi_arm.dylib" \
"(no container or cross-compile path exists for macOS; see README) ---"; \
fi
@echo "--- CI artifacts reproduced in $(CI_LOCAL_DIR) ---"
@ls -1 $(CI_LOCAL_DIR)

clean:
cargo clean
rm target/release/libvmexeccapi.so
rm target/release/libvmexeccapi_arm.so
rm target/release/libvmexeccapi.dylib
rm target/release/libvmexeccapi_arm.dylib
rm c-api/libvmexeccapi.h
rm -f target/release/libvmexeccapi.so
rm -f target/release/libvmexeccapi_arm.so
rm -f target/release/libvmexeccapi.dylib
rm -f target/release/libvmexeccapi_arm.dylib
rm -f c-api/libvmexeccapi.h
rm -f target/x86_64-apple-darwin/release/libvmexeccapi.dylib
rm -rf $(CI_LOCAL_DIR)
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,41 @@
VM wasmer 2.2 executor + specialized C API to be used from Go.

Call `make capi` in the root to get the binary and the C header.

## Reproducing the CI artifacts locally

CI builds one library per platform, each on its own runner. `make ci-local`
reproduces as many of the four as your host can produce, and collects them,
together with the generated header, in `target/ci-local`:

| Artifact | Built by | Needs |
| --- | --- | --- |
| `libvmexeccapi.so` | `linux/amd64` container (`capi-linux-amd64-docker`) | Docker |
| `libvmexeccapi_arm.so` | `linux/arm64` container (`capi-linux-arm64-docker`) | Docker |
| `libvmexeccapi_arm.dylib` | native (`capi-osx-arm`) | a macOS host |
| `libvmexeccapi.dylib` | cross-compiled to `x86_64-apple-darwin` (`capi-osx-amd64-cross`) | a macOS host |

The two container targets build under `Docker/linux.dockerfile`, the same file
the `libvmexeccapi-build-linux-arm64` workflow uses, parameterized by
`MAKE_TARGET`/`ARTIFACT_NAME`; the `rust` base image is multi-arch, so
`--platform` alone selects amd64 or arm64. They need a running Docker daemon
with buildx, and the non-native arch needs QEMU registered (Docker Desktop does
this automatically; on plain Docker Engine, run
`docker run --privileged --rm tonistiigi/binfmt --install all` once).

**The two macOS libraries can only be built on an actual macOS host.** There is
no Docker equivalent for them: Apple's terms don't allow macOS to run in a
container, which is exactly why CI itself uses real `macos-15-intel` /
`macos-latest` runners rather than Docker for those two matrix entries, and why
plain cross-compilation to `x86_64-apple-darwin` only works when the host
already has Apple's linker/SDK (i.e. is itself a Mac).

Running `make ci-local` on Linux (or any non-macOS host) therefore builds only
`libvmexeccapi.so` and `libvmexeccapi_arm.so`, skips the two `.dylib`s with an
explanatory message, and still succeeds. Invoking a macOS-only target directly
on such a host (`capi-osx-arm`, `capi-osx-amd64`, `capi-osx-amd64-cross`) fails
fast with an error saying so, rather than a cryptic `install_name_tool: command
not found`.

The Rust version is pinned in `rust-toolchain.toml`, the workflow files and
`Docker/linux.dockerfile`; all must be bumped together.
7 changes: 6 additions & 1 deletion c-api/build.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
extern crate cbindgen;

use cbindgen::{Builder, Language};
use cbindgen::{Builder, Language, Style};
use std::{env, fs, path::PathBuf};

const HEADER_FILE_NAME: &str = "libvmexeccapi";
Expand All @@ -19,6 +19,11 @@ fn main() {
Builder::new()
.with_crate(crate_dir)
.with_language(Language::C)
// Keep the historical formatting: no line wrapping, `typedef struct { ... } name;`
// without a tag. Both differ from the cbindgen defaults and would otherwise rewrite
// the whole header on every re-generation.
.with_line_length(usize::MAX)
.with_style(Style::Both)
.generate()
.expect("Unable to generate C bindings")
.write_to_file(out_wasmer_header_file.as_path());
Expand Down
Loading
Loading