Skip to content

Commit

Permalink
Add no_std support
Browse files Browse the repository at this point in the history
- Through `no_std` support, we now also support `wasm32v1-none`.
- Introduced a new `std` crate feature that is enabled by default. Without it `#[no_std]` is enabled, but only on Web! This allows Web target to build without std, which is now supported by `wasm-bindgen` as well.

  Additionally, various changes had to be done to support `no_std`:
  - `no_std` does not support `thread_local!`, we use `once_cell` to polyfill this gap. `once_cell` is not a new dependency, it is already a dependency of `wasm-bindgen`.
    - With `feature = "std"`, we use `thread_local!` as before.
    - Without std and without `target_feature = "atomics"`, we use a `static mut` with `once_cell::unsync::Lazy`.
    - Without std and with atomics, we use [`#[thread_local]`](https://doc.rust-lang.org/1.83.0/unstable-book/language-features/thread-local.html?highlight=thread_l#thread_local) with `once_cell::unsync::Lazy`.
  - Some `f64` instructions are not available on `no_std` and had to be polyfilled. For this code from [`libm`](https://crates.io/crates/libm) was copied. Which is used by std as well.
  - `SystemTimeError` now only implements `Error` with `feature = "std"`.
  - `no_std` testing requires to refrain from using the default test harness. The problem was that native tests still needed to use the default test harness. To solve this, integration tests were removed from root crate and two workspace members added, that manually define all integration tests as test targets. The `tests-web` crate has `harness = false` on all tests, while `tests-native` functions regularly. This allow us to use the default test harness for native tests while disabling it for Web.

    Additionally, every test target requires the `run` crate feature, which are enabled by default depending on the target by the root crate. This way regular testing can function correctly for each target as long as `--all-features` is not used. E.g. `cargo test --workspace` and `cargo test --workspace --target wasm32-unknown-unknown` will work correctly.

    The `tests-web` library is used to implement the `panic_handler`, `global_allocator` and `critical_section`. It is always imported to reduce code duplication in all tests.
  - Used [`serde-json-core`](https://crates.io/crates/serde-json-core) to cover tests with `no_std` as well.
  - All links to std documentation had to be supplemented with manual link when `std` is not present.
- Improvements to CI:
  - Refactored all matrices for simplification and covering `--no-default-features`.
  - Split regular and minimal versions building off tests.
  - Split doctests from regular tests.
  - Update Rust toolchain when testing `cargo publish`.
  - Test coverage improvements:
    - The new `wasm-bindgen` update allows us to refrain from having to pass `cfg` flags to the `wasm-bindgen` proc-macros.
    - Use Rust `llvm-tools` instead of the official LLVM package.
    - Remove invalid `script` tag from coverage report.
    - Retain coverage artifact instead of limiting it to one day.
    - Refactor large environment variables into global level ones.
- Small fixes that were stumbled upon:
  - Expose `web_time::web` with `cfg(docsrs)` on native as well.
  - `Serialize` and `Deserialize` implementation are now marked with `doc(cfg(feature = "serde"))`.
  - Std docs.rs link unnecessarily including `stable`.
  - Recommendation for `-Ctarget-feature=+nontrapping-fptoint` was moved from the top-level to the "Usage" section.
  - The minimal version of Serde is now fully specified as v1.0.0.
  - Fix some typos in internal documentation.
  • Loading branch information
daxpedda committed Nov 29, 2024
1 parent f1fa50c commit 227cd06
Show file tree
Hide file tree
Showing 40 changed files with 1,726 additions and 489 deletions.
2 changes: 1 addition & 1 deletion .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[target.wasm32-unknown-unknown]
[target.'cfg(target_family = "wasm")']
runner = "wasm-bindgen-test-runner"
4 changes: 3 additions & 1 deletion .config/topic.dic
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
23
25
1G
1M
1ns
allocator
APIs
Atomics
de
Expand All @@ -12,6 +13,7 @@ io
JS
MDN
MSRV
representable
Serde
Serde's
timestamps
Expand Down
148 changes: 148 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
name: Build

on:
push:
branches: ["main"]
pull_request:

concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true

env:
CARGO_TERM_COLOR: always

jobs:
build:
name:
Build ${{ matrix.target.description }} ${{ matrix.rust.description }} ${{
matrix.features.description }}

runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
target:
- { target: x86_64-unknown-linux-gnu, description: Native }
- { target: wasm32-unknown-unknown, description: Web }
- { target: wasm32v1-none, description: Wasm v1 }
rust:
- { version: "1.60", description: MSRV, atomics: false }
- { version: stable, atomics: false }
- { version: nightly, atomics: false }
- {
version: nightly,
description: with Atomics,
atomics: true,
component: --component rust-src,
flags: "-Ctarget-feature=+atomics,+bulk-memory",
build-std: true,
}
features:
- { features: "", no_std: false }
- { features: --features serde, no_std: false, description: (`serde`) }
- { features: --no-default-features, no_std: true, description: (`no_std`) }
- {
features: --no-default-features --features serde,
no_std: true,
description: "(`no_std`, `serde`)",
}
exclude:
- target: { target: x86_64-unknown-linux-gnu, description: Native }
rust: { version: nightly }
- target: { target: wasm32-unknown-unknown, description: Web }
rust: { version: nightly, atomics: false }
- target: { target: wasm32v1-none, description: Wasm v1 }
rust: { version: "1.60" }
- target: { target: wasm32v1-none, description: Wasm v1 }
rust: { version: stable }
- target: { target: wasm32v1-none, description: Wasm v1 }
features: { no_std: false }

steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Rust
run: |
rustup toolchain install ${{ matrix.rust.version }} --profile minimal ${{ matrix.rust.component }} --target ${{ matrix.target.target }}
rustup default ${{ matrix.rust.version }}
- name: Set `build-std` components
if: matrix.rust.build-std == true && matrix.features.no_std == false
run: echo "BUILD_STD_COMPONENTS=-Zbuild-std=panic_abort,std" >> $GITHUB_ENV
- name: Set `build-std` `no_std` components
if: matrix.rust.build-std == true && matrix.features.no_std == true
run: echo "BUILD_STD_COMPONENTS=-Zbuild-std=core,alloc" >> $GITHUB_ENV
- name: Fix MSRV dependencies
if: matrix.rust.version == '1.60'
run: |
cargo update -p bumpalo --precise 3.14.0
cargo update -p serde --precise 1.0.210
cargo update -p syn --precise 2.0.67
- name: Build
env:
RUSTFLAGS: ${{ matrix.rust.flags }}
run:
cargo build ${{ matrix.features.features }} --target ${{ matrix.target.target }}
$BUILD_STD_COMPONENTS

minimal-versions:
name:
Minimal Versions ${{ matrix.target.description }} ${{ matrix.rust.description }} ${{
matrix.features.description }}

runs-on: ubuntu-latest

defaults:
run:
working-directory: minimal-versions

strategy:
fail-fast: false
matrix:
target:
- { target: x86_64-unknown-linux-gnu, description: Native }
- { target: wasm32-unknown-unknown, description: Web }
- { target: wasm32v1-none, description: Wasm v1 }
rust:
- { version: "1.60", description: MSRV }
- { version: stable }
- { version: nightly }
features:
- { features: "", no_std: false }
- { features: --features serde, no_std: false, description: (`serde`) }
- { features: --no-default-features, no_std: true, description: (`no_std`) }
- {
features: --no-default-features --features serde,
no_std: true,
description: "(`no_std`, `serde`)",
}
exclude:
- target: { target: x86_64-unknown-linux-gnu, description: Native }
rust: { version: nightly }
- target: { target: wasm32-unknown-unknown, description: Web }
rust: { version: nightly }
- target: { target: wasm32v1-none, description: Wasm v1 }
rust: { version: "1.60" }
- target: { target: wasm32v1-none, description: Wasm v1 }
rust: { version: stable }
- target: { target: wasm32v1-none, description: Wasm v1 }
features: { no_std: false }

steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Rust
run: |
rustup toolchain install ${{ matrix.rust.version }} --profile minimal --target ${{ matrix.target.target }}
rustup default ${{ matrix.rust.version }}
- name: Downgrade to minimal versions
run: |
rustup toolchain install nightly --profile minimal
cargo +nightly update -Z minimal-versions
- name: Fix Rust nightly incompatible dependencies
if: matrix.rust.version == 'nightly'
run: |
cargo update -p proc-macro2 --precise 1.0.60
- name: Build
run: cargo build ${{ matrix.features.features }} --target ${{ matrix.target.target }}
64 changes: 34 additions & 30 deletions .github/workflows/coverage-documentation.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ env:

jobs:
coverage:
name: Test Coverage ${{ matrix.mt.description }}
name: Test Coverage ${{ matrix.mt.description }} ${{ matrix.features.description }}

runs-on: ubuntu-latest

Expand All @@ -24,15 +24,24 @@ jobs:
strategy:
matrix:
mt:
- { id: 0 }
- { id: "st" }
- {
id: 1,
id: "mt",
description: with Atomics,
component: --component rust-src,
cflags: -matomics -mbulk-memory,
flags: "-Ctarget-feature=+atomics,+bulk-memory",
args: "-Zbuild-std=panic_abort,std",
build-std: true,
}
features:
- { id: "", features: "", no_std: false }
- { id: -no_std, features: --no-default-features, no_std: true, description: (`no_std`) }

env:
CFLAGS_wasm32_unknown_unknown: ${{ matrix.mt.cflags }}
RUSTFLAGS:
-Cinstrument-coverage -Zcoverage-options=condition -Zno-profiler-runtime --emit=llvm-ir
--cfg=wasm_bindgen_unstable_test_coverage ${{ matrix.mt.flags }}

steps:
- name: Checkout
Expand All @@ -50,31 +59,25 @@ jobs:
run: |
rustup toolchain install nightly --profile minimal --target wasm32-unknown-unknown ${{ matrix.mt.component }}
rustup default nightly
- name: Set `build-std` components
if: matrix.mt.build-std == true && matrix.features.no_std == false
run: echo "BUILD_STD_COMPONENTS=-Zbuild-std=panic_abort,std" >> $GITHUB_ENV
- name: Set `build-std` `no_std` components
if: matrix.mt.build-std == true && matrix.features.no_std == true
run: echo "BUILD_STD_COMPONENTS=-Zbuild-std=core,alloc" >> $GITHUB_ENV
- name: Test
env:
CHROMEDRIVER: chromedriver
CFLAGS_wasm32_unknown_unknown: ${{ matrix.mt.cflags }}
CARGO_HOST_RUSTFLAGS: --cfg=wasm_bindgen_unstable_test_coverage
RUSTFLAGS:
-Cinstrument-coverage -Zcoverage-options=condition -Zno-profiler-runtime --emit=llvm-ir
--cfg=wasm_bindgen_unstable_test_coverage ${{ matrix.mt.flags }}
WASM_BINDGEN_UNSTABLE_TEST_PROFRAW_OUT: coverage-output
run: |
mkdir coverage-output
cargo test --all-features --target wasm32-unknown-unknown -Ztarget-applies-to-host -Zhost-config ${{ matrix.mt.args }} --tests
WASM_BINDGEN_UNSTABLE_TEST_PROFRAW_OUT=$(realpath coverage-output) cargo test --workspace --features serde --target wasm32-unknown-unknown $BUILD_STD_COMPONENTS ${{ matrix.features.features }} --tests
- name: Prepare Object Files
env:
CFLAGS_wasm32_unknown_unknown: ${{ matrix.mt.cflags }}
CARGO_HOST_RUSTFLAGS: --cfg=wasm_bindgen_unstable_test_coverage
RUSTFLAGS:
-Cinstrument-coverage -Zcoverage-options=condition -Zno-profiler-runtime --emit=llvm-ir
--cfg=wasm_bindgen_unstable_test_coverage ${{ matrix.mt.flags }}
run: |
mkdir coverage-input
crate_name=web_time
IFS=$'\n'
for file in $(
cargo test --all-features --target wasm32-unknown-unknown -Ztarget-applies-to-host -Zhost-config ${{ matrix.mt.args }} --tests --no-run --message-format=json | \
cargo test --workspace --features serde --target wasm32-unknown-unknown $BUILD_STD_COMPONENTS ${{ matrix.features.features }} --tests --no-run --message-format=json | \
jq -r "select(.reason == \"compiler-artifact\") | (select(.target.kind == [\"test\"]) // select(.target.name == \"$crate_name\")) | .filenames[0]"
)
do
Expand All @@ -90,7 +93,7 @@ jobs:
- name: Upload Test Coverage Artifact
uses: actions/upload-artifact@v4
with:
name: test-coverage-${{ matrix.mt.id }}
name: test-coverage-${{ matrix.mt.id }}${{ matrix.features.id }}
path: coverage-output
retention-days: 1
if-no-files-found: error
Expand All @@ -105,20 +108,22 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install LLVM v19
- name: Install Rust nightly
run: |
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo add-apt-repository "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-19 main"
sudo apt-get install llvm-19
rustup toolchain install nightly --profile minimal --component llvm-tools
rustup default nightly
- name: Install `cargo-binutils`
uses: taiki-e/install-action@v2
with:
tool: cargo-binutils
- name: Download Test Coverage
uses: actions/download-artifact@v4
with:
pattern: test-coverage-*
path: coverage-input
- name: Merge Profile Data
run:
llvm-profdata-19 merge -sparse coverage-input/*/*.profraw -o
coverage-input/coverage.profdata
rust-profdata merge -sparse coverage-input/*/*.profraw -o coverage-input/coverage.profdata
- name: Export Code Coverage Report
run: |
mkdir coverage-output
Expand All @@ -127,16 +132,15 @@ jobs:
do
objects+=(-object $file)
done
llvm-cov-19 show -show-instantiations=false -output-dir coverage-output -format=html -instr-profile=coverage-input/coverage.profdata ${objects[@]} -sources src
llvm-cov-19 export -format=text -summary-only -instr-profile=coverage-input/coverage.profdata ${objects[@]} -sources src | \
rust-cov show -show-instantiations=false -output-dir coverage-output -format=html -instr-profile=coverage-input/coverage.profdata ${objects[@]} -sources src
rust-cov export -format=text -summary-only -instr-profile=coverage-input/coverage.profdata ${objects[@]} -sources src | \
printf '{ "coverage": "%.2f%%" }' $(jq '.data[0].totals.functions.percent') > coverage-output/coverage.json
sed 's/<!doctype html>//' coverage-output/index.html | perl -p0e 's/<a[^>]*>((?!here).*?)<\/a>/$1/g' >> $GITHUB_STEP_SUMMARY
sed 's/<!doctype html>//' coverage-output/index.html | sed "s/<script src='control.js'><\/script>//" | perl -p0e 's/<a[^>]*>((?!here).*?)<\/a>/$1/g' >> $GITHUB_STEP_SUMMARY
- name: Upload Test Coverage Artifact
uses: actions/upload-artifact@v4
with:
name: test-coverage
path: coverage-output
retention-days: 1
if-no-files-found: error

document:
Expand All @@ -158,7 +162,7 @@ jobs:
RUSTDOCFLAGS: --crate-version main --cfg=docsrs
run:
cargo doc --no-deps -Z rustdoc-map -Z rustdoc-scrape-examples --target
wasm32-unknown-unknown --all-features
wasm32-unknown-unknown --features serde
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Fix permissions
Expand Down
Loading

0 comments on commit 227cd06

Please sign in to comment.