Skip to content
Open
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 .github/actions/wasi/base/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: "Ubuntu base dependencies"

runs:
using: "composite"
steps:
- name: Install system dependencies
shell: bash
run: |
sudo apt-get update
sudo apt-get install -y ninja-build

- name: Install wasmtime CLI
shell: bash
run: |
curl https://wasmtime.dev/install.sh -sSf | bash
sudo cp ~/.wasmtime/bin/wasmtime /usr/bin/wasmtime
15 changes: 15 additions & 0 deletions .github/actions/wasi/clang/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: "Ubuntu latest clang"

runs:
using: "composite"
steps:
- name: Install clang/llvm 17
shell: bash
run: |
sudo apt-add-repository 'deb https://apt.llvm.org/jammy llvm-toolchain-jammy-17 main'
sudo wget -qO /etc/apt/trusted.gpg.d/llvm.asc https://apt.llvm.org/llvm-snapshot.gpg.key
sudo apt-get update
sudo apt-get install -y -t llvm-toolchain-jammy-17 \
clang-17 llvm-17 lld-17 lldb-17 libc++-17-dev \
libc++abi-17-dev libclang-rt-17-dev
for f in /usr/lib/llvm-*/bin/*; do sudo ln -sf "$f" /usr/bin; done
9 changes: 9 additions & 0 deletions .github/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ jobs:
{ os: macos-latest, preset: x64-darwin-clang-dynamic },
{ os: windows-latest, preset: x64-windows-msvc-dynamic },
{ os: windows-latest, preset: x64-windows-clang-dynamic },
{ os: ubuntu-latest, preset: wasm32-wasi-clang-static },
]

steps:
Expand All @@ -31,3 +32,11 @@ jobs:
shell: bash
run: |
cmake --workflow --preset ${{matrix.target.preset}}

- name: Upload vcpkg error logs
if: failure()
uses: actions/upload-artifact@v4
with:
name: logs-${{matrix.target.preset}}
path: |
/usr/local/share/vcpkg/**/*.log
10 changes: 9 additions & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ jobs:
{ os: ubuntu-latest, preset: x64-linux-gcc-dynamic },
{ os: ubuntu-latest, preset: x86-linux-gcc-dynamic },
{ os: ubuntu-latest, preset: x64-linux-clang-dynamic },
{ os: ubuntu-latest, preset: x86-linux-clang-dynamic },
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was removed from .github/ci.yaml in 5efee87, so I removed it here in .github/workflows/ci.yaml as well. It fails due to missing libc++abi on x86

{ os: macos-latest, preset: x64-darwin-gcc-dynamic },
{ os: macos-latest, preset: x64-darwin-clang-dynamic },
{ os: windows-latest, preset: x64-windows-msvc-dynamic },
{ os: windows-latest, preset: x64-windows-clang-dynamic },
{ os: ubuntu-latest, preset: wasm32-wasi-clang-static },
]

steps:
Expand All @@ -37,3 +37,11 @@ jobs:
shell: bash
run: |
cmake --workflow --preset ${{matrix.target.preset}}

- name: Upload vcpkg error logs
if: failure()
uses: actions/upload-artifact@v4
with:
name: logs-${{matrix.target.preset}}
path: |
/usr/local/share/vcpkg/**/*.log
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessary for this PR, but adds a nice way to debug issues in CI.
Can be removed and put up as a separate PR.

1 change: 1 addition & 0 deletions CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"include": [
"cmake/presets/arm64-darwin-gcc.json",
"cmake/presets/arm64-darwin-clang.json",
"cmake/presets/wasm32-wasi-clang.json",
"cmake/presets/x64-darwin-gcc.json",
"cmake/presets/x64-darwin-clang.json",
"cmake/presets/x64-linux-gcc.json",
Expand Down
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,18 @@ find_package(<PROJECT_NAME> CONFIG REQUIRED)
target_link_libraries(<TARGET> PUBLIC <PROJECT_NAME>::<MODULE_NAME>)
```

This will require that your library is published and installed via vcpkg or found locally by setting the `CMAKE_PREFIX_PATH` environment variable in your other project during configure.
This will require that your library is published and installed via vcpkg or found locally by setting the `CMAKE_PREFIX_PATH` environment variable in your other project during configure.

If you wish to expose parts of your library as a WebAssembly module, you can add the `extern "C" __attribute__((export_name("<my_function_name>")))` annotation to any function you wish to expose in `src/wasm/interface.cpp`, and compile using the `wasm32-wasi-clang` preset. This will generate a minimal WebAssembly binary exposing your exported functions at `build/<PRESET>/src/wasm/<Debug|Release|RelWithDebInfo>/lib<PROJECT_NAME>.wasm`. This binary conforms to the [WASI WebAssembly standard](https://wasi.dev/), so it can be utilized in any WASI-supporting runtime like [`wasmer.io`](https://wasmer.io/) or [wasmtime](https://wasmtime.dev/).

A WebAssembly version of your CLI will also be available:

```sh
wasmtime run build/<PRESET>/src/cli/<Debug|Release|RelWithDebInfo>/<PROJECT_NAME>_cli

# Prints:
# <PROJECT_NAME> version: 0.0.1
```

> [!NOTE]
> Exposed WebAssembly functions currently only accept parameters and return values of numerical type. This is how core WebAssembly files work, and without additional glue-code or binding generators like [wit-bindgen](https://github.com/bytecodealliance/wit-bindgen), complex data-types cannot be sent over the runtime's ABI boundary. Complex data must be transferred via pointer and serialized bytes in exported/shared memory.
13 changes: 13 additions & 0 deletions cmake/presets/toolchains/wasi-sdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"version": 6,
"configurePresets": [
{
"name": "wasi-sdk",
"hidden": true,
"cacheVariables": {
"VCPKG_CHAINLOAD_TOOLCHAIN_FILE": "${sourceDir}/cmake/toolchains/wasi-sdk.toolchain.cmake",
"CMAKE_CROSSCOMPILING_EMULATOR": "wasmtime"
}
}
]
}
52 changes: 52 additions & 0 deletions cmake/presets/wasm32-wasi-clang.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"version": 6,
"include": [
"base.json",
"compilers/clang.json",
"toolchains/wasi-sdk.json"
],
"configurePresets": [
{
"name": "wasm32-wasi-clang-static",
"inherits": [
"base",
"wasi-sdk",
"clang"
],
"displayName": "WASM32 WASI clang static libs"
}
],
"buildPresets": [
{
"name": "wasm32-wasi-clang-static",
"inherits": "base",
"configurePreset": "wasm32-wasi-clang-static"
}
],
"testPresets": [
{
"name": "wasm32-wasi-clang-static",
"inherits": "base",
"configurePreset": "wasm32-wasi-clang-static"
}
],
"workflowPresets": [
{
"name": "wasm32-wasi-clang-static",
"steps": [
{
"type": "configure",
"name": "wasm32-wasi-clang-static"
},
{
"type": "build",
"name": "wasm32-wasi-clang-static"
},
{
"type": "test",
"name": "wasm32-wasi-clang-static"
}
]
}
]
}
14 changes: 14 additions & 0 deletions cmake/toolchains/wasi-sdk.toolchain.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
include(FetchContent)

set(FETCHCONTENT_FULLY_DISCONNECTED_OLD ${FETCHCONTENT_FULLY_DISCONNECTED})
set(FETCHCONTENT_FULLY_DISCONNECTED OFF)
FetchContent_Declare(
wasi_sdk_toolchain
SOURCE_DIR "${CMAKE_BINARY_DIR}/_deps/wasi-sdk"
GIT_REPOSITORY https://github.com/rioam2/wasi-sdk-toolchain.git
GIT_TAG wasi-sdk-23
)
FetchContent_MakeAvailable(wasi_sdk_toolchain)
set(FETCHCONTENT_FULLY_DISCONNECTED ${FETCHCONTENT_FULLY_DISCONNECTED_OLD})

include("${wasi_sdk_toolchain_SOURCE_DIR}/wasi-sdk.toolchain.cmake")
9 changes: 9 additions & 0 deletions cmake/vcpkg/triplets/wasm32-wasi.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
set(VCPKG_TARGET_ARCHITECTURE wasm32)
set(VCPKG_LIBRARY_LINKAGE static)
set(VCPKG_CRT_LINKAGE static)

set(VCPKG_CMAKE_SYSTEM_NAME WASI)
set(VCPKG_CMAKE_SYSTEM_VERSION 1)
set(VCPKG_CMAKE_SYSTEM_PROCESSOR wasm32)

set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE "${CMAKE_CURRENT_LIST_DIR}/../../toolchains/wasi-sdk.toolchain.cmake")
4 changes: 2 additions & 2 deletions cmake/vcpkg/vcpkg.toolchain.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ include(${CMAKE_CURRENT_LIST_DIR}/bootstrap/vcpkg-config.cmake)

vcpkg_configure(
CACHE_DIR_NAME @cpp_pt_name@
REPO https://github.com/microsoft/vcpkg.git
REF 9edb1b8e590cc086563301d735cae4b6e732d2d2 # release 2023.08.09
REPO https://github.com/rioam2/vcpkg-wasm32-wasi
REF 983e077b814c4c0e56336e1d32407c2b2c13a90b
)

include($CACHE{_VCPKG_TOOLCHAIN_FILE})
2 changes: 2 additions & 0 deletions init.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ set(templates
src/${cpp_pt_module}/src/header-1.cpp
src/cli/CMakeLists.txt
src/cli/src/main.cpp
src/wasm/CMakeLists.txt
src/wasm/src/interface.cpp
src/cmake/add_cpp_pt_executable.cmake
src/cmake/add_cpp_pt_module.cmake
src/cmake/cpp-pt-config.cmake
Expand Down
4 changes: 4 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,7 @@ include(add_@cpp_pt_cmake@_executable)

add_subdirectory(@cpp_pt_module@)
add_subdirectory(cli)

if (WASI)
add_subdirectory(wasm)
endif()
16 changes: 16 additions & 0 deletions src/wasm/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# SPDX-FileCopyrightText: Copyright 2023 Mikhail Svetkin
# SPDX-License-Identifier: MIT

add_@cpp_pt_cmake@_executable(wasm)

target_sources(${@cpp_pt_cmake@_executable_target} PRIVATE
src/interface.cpp
)

target_link_libraries(${@cpp_pt_cmake@_executable_target}
PRIVATE
@cpp_pt_name@::@cpp_pt_module@
)

set_target_properties(${@cpp_pt_cmake@_executable_target} PROPERTIES OUTPUT_NAME "lib@cpp_pt_module@.wasm")
target_link_options(${@cpp_pt_cmake@_executable_target} PRIVATE -nostartfiles -Wl,--no-entry)
35 changes: 35 additions & 0 deletions src/wasm/src/interface.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include "@cpp_pt_name@/@cpp_pt_module@/@cpp_pt_module_header@.hpp"

#include <fmt/core.h>

#include <cstdlib>
#include <cstring>
#include <string>

// Memory management exports
extern "C" __attribute__((export_name("wasm_malloc"))) void* wasm_malloc(size_t nbytes) {
return std::malloc(nbytes);
}

extern "C" __attribute__((export_name("wasm_free"))) void wasm_free(
void* ptr) {
return std::free(ptr);
}

// Exported WASM functions
extern "C" __attribute__((export_name("version"))) uint8_t* version() {
std::string version = @cpp_pt_name@::@cpp_pt_module@::version();
uint8_t* versionPointer = (uint8_t*)wasm_malloc(version.length());
std::memcpy(versionPointer, version.c_str(), version.length());
return versionPointer;
}

// This is the WASM module's initialization entrypoint. It should be called
// before running any other exported function because it initializes implicit
// global/static resources. This is only required in library/reactor mode of
// WebAssembly. Read more here:
// https://wasmcloud.com/blog/webassembly-patterns-command-reactor-library#the-reactor-pattern
extern "C" void __wasm_call_ctors();
extern "C" __attribute__((export_name("_start"))) void _start() {
__wasm_call_ctors();
}
4 changes: 4 additions & 0 deletions tests/cmake/add_cpp_pt_test.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,9 @@ function(add_@cpp_pt_cmake@_test test_name)
${test_target} PRIVATE @cpp_pt_name@::${module_name} Catch2::Catch2WithMain
)

if (WASI)
target_link_options(${test_target} PRIVATE -nostartfiles -Wl,--no-entry)
endif()

catch_discover_tests(${test_target} TEST_PREFIX "${test_target}:" ${ARGN})
endfunction()