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
12 changes: 11 additions & 1 deletion 7.x/cmake/WasmPlugins.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
set(USE_COMPILER_OPTIMIZATIONS OFF CACHE BOOL "" FORCE)
set(REQUIRE_PTHREADS OFF CACHE BOOL "" FORCE)
set(USE_DOUBLE ON CACHE BOOL "" FORCE)
if(NOT WASM_MAX_MEMORY)
message(FATAL_ERROR "WASM_MAX_MEMORY must match the shared-memory Csound engine")
endif()
execute_process(COMMAND ${CMAKE_C_COMPILER} --print-libgcc-file-name
OUTPUT_VARIABLE WASM_BUILTINS OUTPUT_STRIP_TRAILING_WHITESPACE
COMMAND_ERROR_IS_FATAL ANY)
Expand All @@ -13,12 +16,14 @@ function(configure_wasm_plugin target)
target_compile_definitions(${target} PRIVATE
USE_DOUBLE=1 _WASI_EMULATED_SIGNAL _WASI_EMULATED_MMAN)
target_compile_options(${target} PRIVATE
-fPIC "SHELL:-mllvm -wasm-enable-sjlj"
-fPIC -pthread "SHELL:-mllvm -wasm-enable-sjlj"
"SHELL:-mllvm -wasm-use-legacy-eh=false"
"$<$<COMPILE_LANGUAGE:C>:-std=gnu11>"
"$<$<COMPILE_LANGUAGE:CXX>:-nostdinc++>"
"$<$<COMPILE_LANGUAGE:CXX>:-isystem${WASM_CXX_INCLUDE_DIR}>")
if(WASM_CXX_EXCEPTIONS)
target_compile_options(${target} PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:-fwasm-exceptions>")
target_link_options(${target} PRIVATE "$<$<LINK_LANGUAGE:CXX>:-fwasm-exceptions>")
else()
target_compile_options(${target} PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:-fno-exceptions>")
endif()
Expand All @@ -29,7 +34,12 @@ function(configure_wasm_plugin target)
"LINKER:--no-entry"
"LINKER:--import-table"
"LINKER:--import-memory"
"LINKER:--shared-memory"
"LINKER:--max-memory=${WASM_MAX_MEMORY}"
"LINKER:--allow-undefined"
"LINKER:--export=__wasm_init_tls"
"LINKER:--export=__tls_size"
"LINKER:--export=__tls_align"
Comment thread
hlolli marked this conversation as resolved.
"LINKER:--export=__wasm_call_ctors"
"LINKER:--export-if-defined=__wasm_apply_data_relocs"
"LINKER:--export-if-defined=csound_opcode_init"
Expand Down
20 changes: 10 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 3 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"name": "@csound/plugins",
"version": "7.0.0-beta.0",
"private": true,
"version": "7.0.0-beta.1",
"description": "Csound 7 opcode plugins for WebAssembly",
"type": "module",
"license": "LGPL-2.1-or-later",
Expand Down Expand Up @@ -41,8 +40,8 @@
"prepack": "npm test"
},
"devDependencies": {
"@csound/browser": "7.0.0-beta39",
"@csound/wasm-bin": "7.0.0-beta29"
"@csound/browser": "7.0.0-beta41",
"@csound/wasm-bin": "7.0.0-beta31"
},
"keywords": [
"csound",
Expand Down
23 changes: 13 additions & 10 deletions wasm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ Csound 6 is not supported.

## Runtime dependency

Builds use the plugin SDK from `@csound/wasm-bin`, and tests use the published
`@csound/browser` runtime. [package.json](../package.json) and
Builds use the shared-memory plugin SDK and toolchain from `@csound/wasm-bin`.
Tests use the published `@csound/browser` runtime. [package.json](../package.json) and
[package-lock.json](../package-lock.json) record the versions used together.
Rebuild after updating these dependencies. Plugins built for the old,
non-shared engine cannot load in the shared-memory runtime.

## Use

Expand Down Expand Up @@ -62,8 +64,10 @@ npm pack
```

Nix pins the toolchain and builds the existing `7.x` CMake targets with Ninja.
Dependencies use static PIC builds. The C runtime imports the engine's allocator;
it does not link its own heap. STK uses a separate libc++ build with C++ exceptions
Dependencies use threaded static PIC builds. Each plugin exports its TLS size,
alignment, and initializer so the engine can set up thread-local storage.
The C runtime imports the engine's allocator; it does not link its own heap.
STK uses a separate libc++ build with C++ exceptions
and a static WASM unwinder. Other C++ plugins keep exceptions disabled.
CMake's `STK_EMBED_RAWWAVES` option embeds the samples and defaults to on for WASM.
Native builds keep using the installed raw-wave directory by default.
Expand Down Expand Up @@ -93,10 +97,9 @@ uses the `csound` Cachix cache and uploads builds when `CACHIX_AUTH_TOKEN` is
available. Fork PRs read from the cache without the secret. Native builds use
GitHub Actions caches.

## First npm release
## npm releases

Run the build, test, and pack commands against the published browser package
with `CSOUND_BROWSER_URL` unset. When ready to publish, remove `private` from
`package.json`, set the package version with `npm version`, and run
`npm publish --tag beta --access public` from a clean build. No workflow publishes
this package yet.
Set the next package version with `npm version`, then run the build, test, and
pack commands against the published browser package with `CSOUND_BROWSER_URL`
unset. Publish with `npm publish --tag beta --access public` from that build.
No workflow publishes this package yet.
39 changes: 29 additions & 10 deletions wasm/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,34 @@
, csoundSdkArchive ? null
}:
let
wasi = pkgs.pkgsCross.wasi32.clangStdenv;
lock = builtins.fromJSON (builtins.readFile ../package-lock.json);
wasmBin = lock.packages."node_modules/@csound/wasm-bin";
wasmPackage = pkgs.runCommand "csound-wasm-bin-${wasmBin.version}" {} ''
mkdir -p $out
tar -xzf ${pkgs.fetchurl { url = wasmBin.resolved; hash = wasmBin.integrity; }} --strip-components=1 -C $out
'';
# Use the same threaded WASI toolchain and memory limit as the engine.
toolchain = import "${wasmPackage}/src/wasi-realtime-toolchain.nix" { inherit pkgs; };
maxMemory = builtins.fromJSON (builtins.readFile "${wasmPackage}/src/shared-memory-max.txt");
wasi = toolchain.stdenv;
# Nix's WASI wrapper otherwise forces -fno-exceptions after CMake's flags.
# Let each target select its exception mode instead.
wasiCxx = pkgs.overrideCC wasi (wasi.cc.override (old: {
nixSupport = old.nixSupport // {
cc-cflags = builtins.filter (flag: flag != "-fno-exceptions") old.nixSupport.cc-cflags;
};
}));
libc = pkgs.pkgsCross.wasi32.wasilibc.overrideAttrs (old: {
NIX_CFLAGS_COMPILE = (old.NIX_CFLAGS_COMPILE or []) ++ [ "-fPIC" ];
libc = toolchain.libc.overrideAttrs (old: {
NIX_CFLAGS_COMPILE = (old.NIX_CFLAGS_COMPILE or []) ++ [
"-fPIC" "-mllvm" "-wasm-enable-sjlj" "-mllvm" "-wasm-use-legacy-eh=false"
];
# Use the engine's allocator. wasi-libc also skips its fixed symbol list
# for this mode; PIC adds imports absent from that non-PIC list.
makeFlags = (old.makeFlags or []) ++ [ "MALLOC_IMPL=none" ];
preFixup = "";
dontStrip = true;
});
libcxx = pkgs.pkgsCross.wasi32.llvmPackages.libcxx.overrideAttrs (old: {
libcxx = toolchain.libcxx.overrideAttrs (old: {
cmakeFlags = old.cmakeFlags ++ [
"-DCMAKE_POSITION_INDEPENDENT_CODE=ON"
"-DLIBCXX_ENABLE_FILESYSTEM=ON"
Expand All @@ -43,20 +54,17 @@ let
"-DLIBCXXABI_ENABLE_EXCEPTIONS=ON"
];
preConfigure = (old.preConfigure or "") + ''
cmakeFlagsArray+=("-DCMAKE_CXX_FLAGS=-fexceptions -fwasm-exceptions -I${libunwindSource}/libunwind/include")
cmakeFlagsArray+=("-DCMAKE_CXX_FLAGS=-fexceptions -fwasm-exceptions -mllvm -wasm-use-legacy-eh=false -I${libunwindSource}/libunwind/include")
'';
});
lock = builtins.fromJSON (builtins.readFile ../package-lock.json);
wasmBin = lock.packages."node_modules/@csound/wasm-bin";
sdkArchive = if csoundSdkArchive == null then null else builtins.path {
path = csoundSdkArchive;
name = "csound-plugin-sdk.tar.gz";
};
sdk = pkgs.runCommand "csound-wasm-plugin-sdk-${wasmBin.version}" {} ''
mkdir -p $out
${if csoundSdkArchive == null then ''
tar -xzf ${pkgs.fetchurl { url = wasmBin.resolved; hash = wasmBin.integrity; }}
tar -xzf package/lib/csound-plugin-sdk.tar.gz --strip-components=1 -C $out
tar -xzf ${wasmPackage}/lib/csound-plugin-sdk.tar.gz --strip-components=1 -C $out
'' else ''
tar -xzf ${sdkArchive} --strip-components=1 -C $out
''}
Expand All @@ -66,6 +74,16 @@ let
inherit (pkgs.zlib) version src;
nativeBuildInputs = [ pkgs.cmake pkgs.ninja ];
cmakeFlags = [ "-DCMAKE_POSITION_INDEPENDENT_CODE=ON" "-DCMAKE_INSTALL_LIBDIR=lib" "-DZLIB_BUILD_EXAMPLES=OFF" ];
# zlib 1.3.1 has no CMake switch to omit its shared library. Build only
# the static target so it does not link the toolchain's non-PIC libc.
ninjaFlags = [ "zlibstatic" ];
installPhase = ''
runHook preInstall
install -Dm644 libzlibstatic.a $out/lib/libzlibstatic.a
install -Dm644 ../zlib.h $out/include/zlib.h
install -Dm644 zconf.h $out/include/zconf.h
runHook postInstall
'';
dontStrip = true;
};
libpng = wasi.mkDerivation {
Expand All @@ -80,7 +98,7 @@ let
"-DPNG_SHARED=OFF" "-DPNG_STATIC=ON" "-DPNG_TESTS=OFF" "-DPNG_TOOLS=OFF"
"-DZLIB_LIBRARY=${zlib}/lib/libzlibstatic.a"
];
NIX_CFLAGS_COMPILE = [ "-mllvm" "-wasm-enable-sjlj" ];
NIX_CFLAGS_COMPILE = [ "-mllvm" "-wasm-enable-sjlj" "-mllvm" "-wasm-use-legacy-eh=false" ];
dontStrip = true;
};
in wasiCxx.mkDerivation {
Expand All @@ -94,6 +112,7 @@ in wasiCxx.mkDerivation {
"-DCMAKE_POLICY_VERSION_MINIMUM=3.5"
"-DCMAKE_BUILD_TYPE=Release"
"-DCSOUND_INCLUDE_DIR=${sdk}/include/csound"
"-DWASM_MAX_MEMORY=${toString maxMemory}"
"-DWASM_LIBC=${libc}/lib/libc.a"
"-DWASM_SETJMP=${libc}/lib/libsetjmp.a"
"-DWASM_LIBCXX=${libcxx}/lib/libc++.a"
Expand Down
2 changes: 1 addition & 1 deletion wasm/libunwind/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ project(CsoundWasmUnwind C)
add_library(unwind STATIC "${LIBUNWIND_SOURCE_DIR}/src/Unwind-wasm.c")
set_target_properties(unwind PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_compile_features(unwind PRIVATE c_std_11)
target_compile_options(unwind PRIVATE -fwasm-exceptions)
target_compile_options(unwind PRIVATE -fwasm-exceptions "SHELL:-mllvm -wasm-use-legacy-eh=false")
target_compile_definitions(unwind PRIVATE _LIBUNWIND_HIDE_SYMBOLS)
target_include_directories(unwind PRIVATE "${LIBUNWIND_SOURCE_DIR}/include")
install(TARGETS unwind ARCHIVE DESTINATION lib)
12 changes: 11 additions & 1 deletion wasm/tests/runtime.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ async function run(body, check, withPlugins = binaries, prepare = () => {}) {
}
}

test("all plugins are WASI side modules", async () => {
test("all plugins are WASI side modules with the shared-memory TLS exports", async () => {
for (const bytes of binaries) {
const module = await WebAssembly.compile(bytes);
const imports = WebAssembly.Module.imports(module);
Expand All @@ -79,6 +79,16 @@ test("all plugins are WASI side modules", async () => {
assert.ok(imports.some(({ kind, name }) => kind === "memory" && name === "memory"));
assert.ok(imports.some(({ kind }) => kind === "table"));
assert.ok(exports.some(({ name }) => name === "__wasm_call_ctors"));
for (const [name, kind] of [
["__wasm_init_tls", "function"],
["__tls_size", "global"],
["__tls_align", "global"],
]) {
assert.ok(
exports.some((entry) => entry.name === name && entry.kind === kind),
`${name} must be exported as a ${kind}`,
);
}
assert.ok(exports.some(({ name }) => ["csoundModuleInit", "csound_opcode_init"].includes(name)));
assert.ok(!exports.some(({ name }) => name === "_start"));
}
Expand Down
Loading