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
38 changes: 38 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,44 @@ jobs:
- name: All C++ suites (core roundtrip + unit, api mxtest + examples + roundtrip)
run: make test-all

# mx::api cross-compiled to WebAssembly via the pinned mx-sdk emsdk install
# (#386) -- denigma (rpatters1/denigma) depends on this path staying green.
emscripten:
name: emscripten
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v5

- uses: docker/setup-buildx-action@v3
- uses: crazy-max/ghaction-github-runtime@v3

- name: Create bind-backed build volume
run: |
mkdir -p build/docker
docker volume create --driver local \
--opt type=none --opt o=bind \
--opt device="$GITHUB_WORKSPACE/build/docker" mx-build

# ccache for mx's own object files; EM_CACHE (below) is Emscripten's
# separate cache for its compiled system libraries.
- name: Cache ccache
uses: actions/cache@v4
with:
path: build/docker/.ccache
key: ccache-${{ runner.os }}-wasm-${{ github.sha }}
restore-keys: ccache-${{ runner.os }}-wasm-

- name: Cache EM_CACHE
uses: actions/cache@v4
with:
path: build/docker/.emcache
key: emcache-${{ runner.os }}-wasm-${{ github.sha }}
restore-keys: emcache-${{ runner.os }}-wasm-

- name: mx::api under Node (wasm)
run: make wasm-test

# The api product surface, built natively with AppleClang. macOS is a
# portability check on the product tier only (the core suites run on Linux in
# test-linux); this is the only C++ job off the pinned toolchain, by design,
Expand Down
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ separately. `make test-all` runs every C++ suite at once (core roundtrip + unit
api-roundtrip) — the deep gate CI runs on Linux (see `make help` / CI).
Adding/removing a `data/` file: bump the pinned count in `CoreRoundtripTest.cpp`, run `make audit` (regenerates `corpus.xml` + `*.features.xml`), confirm round-trip via `make core-roundtrip-test`.
`ApiLoadSmokeTest` proves a file imports without crashing, not that the data is correct; the read→write→read gate (`make api-roundtrip` / `roundtrip-baseline.txt`) is the correctness check — pin a fixture there to defend a feature.
`make wasm-test` builds `mx::api` for Emscripten and runs the examples under Node — CI's check that mx
still compiles and works client-side in a browser (a consumer, denigma, depends on this; issue #386).

Look at what will run in CI `.github/workflows/ci.yaml` and anticipate issues there when coding
locally. Code coverage is not part of the normal CI run; trigger it on demand with a `/coverage`
Expand Down
7 changes: 7 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR})
set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR})

# Emscripten disables exception catching by default; mx's internal MX_THROW
# (Throw.h) needs it, so DocumentManager can actually catch and convert it.
if(EMSCRIPTEN)
add_compile_options(-fexceptions)
add_link_options(-fexceptions)
endif()

option(MX_CORE_DEV "Build the core roundtrip (corert) test binary against the regenerated mx/core." OFF)

option(MX_API "Build the mx::api/mx::impl product library and its tests." ON)
Expand Down
18 changes: 18 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
libxml2-dev \
libxml2-utils \
pkg-config \
git \
ca-certificates \
xz-utils \
&& rm -rf /var/lib/apt/lists/*

# Python quality tooling for `make gen-quality` / `make gen-lint`. Isolated in a
Expand All @@ -35,6 +38,21 @@ RUN python3 -m venv /opt/quality-venv \
# Unversioned name so the Makefile invokes the formatter without the suffix.
RUN ln -sf /usr/bin/clang-format-18 /usr/local/bin/clang-format

# emsdk: pinned Emscripten toolchain for building mx to WebAssembly (#386).
ARG EMSDK_VERSION=6.0.6
RUN git clone --depth 1 --branch ${EMSDK_VERSION} https://github.com/emscripten-core/emsdk.git /opt/emsdk \
&& /opt/emsdk/emsdk install ${EMSDK_VERSION} \
&& /opt/emsdk/emsdk activate ${EMSDK_VERSION} \
&& ln -s "$(find /opt/emsdk/node -maxdepth 2 -type d -name bin)" /opt/emsdk/node/current-bin \
&& rm -rf /opt/emsdk/downloads

# EM_CACHE: like CCACHE_DIR, avoids writing under root-owned /opt/emsdk at runtime.
# PATH is appended, not prepended -- emsdk's own dirs shadow real tools by name (node, cmake).
ENV EMSDK=/opt/emsdk \
EM_CONFIG=/opt/emsdk/.emscripten \
EM_CACHE=/workspace/build/.emcache \
PATH="${PATH}:/opt/emsdk/node/current-bin:/opt/emsdk/upstream/emscripten"

# MX_RUNNING_IN_DOCKER flips the Makefile to its in-container branch. Build with
# the pinned GCC; ccache state lives under the mounted build volume.
ENV MX_RUNNING_IN_DOCKER=1 \
Expand Down
34 changes: 34 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ FIND_CPP := find src \
core-build core-roundtrip-test core-unit core-coverage \
api-lib api-build api-test api-examples api-roundtrip \
api-roundtrip-discover api-roundtrip-dump api-roundtrip-classify api-coverage \
wasm-lib wasm-build wasm-test \
gen-test audit-test gen-quality gen-lint \
gen gen-cpp gen-go gen-c gen-schema \
audit audit-force \
Expand All @@ -93,6 +94,11 @@ help:
@echo ' make api-roundtrip-classify Classify dumped failures by root cause (Python).'
@echo ' make api-coverage Instrumented api/impl/utility build + gcovr report.'
@echo ''
@echo ' WebAssembly (mx::api via Emscripten, issue #386):'
@echo ' make wasm-lib Build the mx static library for wasm (MX_API=ON, emcmake).'
@echo ' make wasm-build wasm-lib plus the mxread/mxwrite/mxhide examples.'
@echo ' make wasm-test Run mxread/mxwrite/mxhide under Node against the wasm build.'
@echo ''
@echo ' Core substrate (mx::core):'
@echo ' make core-build Build mx_core and the corert + unit test binaries.'
@echo " make core-roundtrip-test Run the core roundtrip suite. Filter: ARGS='[core-roundtrip] lysuite/*'"
Expand Down Expand Up @@ -235,6 +241,25 @@ api-coverage:
$(BUILD_ROOT)/cov-api | tee $(COV_DIR)/api/summary.txt
@echo "=== api-coverage written to $(COV_DIR)/api/ ==="

# wasm: mx::api built for Emscripten. ccache wraps emcc via the ambient
# CMAKE_*_COMPILER_LAUNCHER env (Dockerfile), same as every other job.
wasm-lib:
emcmake $(CMAKE) -S . -B $(BUILD_ROOT)/wasm \
-DCMAKE_BUILD_TYPE=$(BUILD_TYPE) \
-DMX_API=on
emmake $(CMAKE) --build $(BUILD_ROOT)/wasm --target mx --parallel $(JOBS)

wasm-build: wasm-lib
emmake $(CMAKE) --build $(BUILD_ROOT)/wasm --target mxread mxwrite mxhide --parallel $(JOBS)

# Runs mxread/mxwrite/mxhide under Node, exercising mx::api, not just a compile.
# mxwrite's output path is bare -- Emscripten's MEMFS starts empty, no build/wasm/ dir.
wasm-test: wasm-build
node $(BUILD_ROOT)/wasm/mxread.js
node $(BUILD_ROOT)/wasm/mxwrite.js example.musicxml
node $(BUILD_ROOT)/wasm/mxhide.js
@echo 'wasm-test: mxread/mxwrite/mxhide ran successfully under Node (wasm).'

core-build:
$(CMAKE) -S . -B $(BUILD_ROOT)/core-dev -DCMAKE_BUILD_TYPE=$(BUILD_TYPE) -DMX_CORE_DEV=on
$(CMAKE) --build $(BUILD_ROOT)/core-dev --parallel $(JOBS)
Expand Down Expand Up @@ -400,6 +425,15 @@ api-coverage: $(DOCKER_STAMP) docker-volume
$(DOCKER_RUN) make api-coverage BUILD_TYPE=$(BUILD_TYPE) ARGS='$(ARGS)'
@echo "API coverage written to $(COV_DIR)/api/ (open $(COV_DIR)/api/index.html)"

wasm-lib: $(DOCKER_STAMP) docker-volume
$(DOCKER_RUN) make wasm-lib BUILD_TYPE=$(BUILD_TYPE)

wasm-build: $(DOCKER_STAMP) docker-volume
$(DOCKER_RUN) make wasm-build BUILD_TYPE=$(BUILD_TYPE)

wasm-test: $(DOCKER_STAMP) docker-volume
$(DOCKER_RUN) make wasm-test BUILD_TYPE=$(BUILD_TYPE)

core-build: $(DOCKER_STAMP) docker-volume
$(DOCKER_RUN) make core-build BUILD_TYPE=$(BUILD_TYPE)

Expand Down
Loading