Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(wasm-builder): add support for new wasm32v1-none target #7008

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
78c5f4c
feat(wasm-builder): add support for new `wasm32v1-none` target
StackOverflowExcept1on Dec 27, 2024
dcfbeb7
fix typo
StackOverflowExcept1on Dec 27, 2024
03714a0
trying to fix CI
StackOverflowExcept1on Dec 27, 2024
2410530
Merge branch 'master' into wasm32v1-none
StackOverflowExcept1on Dec 27, 2024
575d805
Update prdoc/pr_7008.prdoc
bkchr Dec 30, 2024
8fffe18
Merge branch 'master' into wasm32v1-none
bkchr Jan 2, 2025
ce0fb1d
Merge branch 'master' into wasm32v1-none
bkchr Jan 6, 2025
3d26ce9
Merge branch 'master' into wasm32v1-none
bkchr Jan 8, 2025
4c3b0ea
git apply patch from CI
StackOverflowExcept1on Jan 8, 2025
486be73
Revert "git apply patch from CI"
StackOverflowExcept1on Jan 8, 2025
21245bc
just changle location of substrate-wasm-builder
StackOverflowExcept1on Jan 9, 2025
d044efc
Merge branch 'master' into wasm32v1-none
bkchr Jan 9, 2025
1f34087
Fix umbrella script
bkchr Jan 9, 2025
8ae5e7b
Update prdoc/pr_7008.prdoc
bkchr Jan 10, 2025
f154742
add sp-consensus-beefy bump patch to prdoc
StackOverflowExcept1on Jan 10, 2025
6bfea51
use wasm32v1-none target only if installed
StackOverflowExcept1on Jan 13, 2025
5838f1e
Merge remote-tracking branch 'origin/master' into wasm32v1-none
StackOverflowExcept1on Jan 13, 2025
b21ffdf
Merge branch 'master' into wasm32v1-none
StackOverflowExcept1on Jan 16, 2025
3628105
resolve conversations
StackOverflowExcept1on Jan 18, 2025
a804037
Merge remote-tracking branch 'origin/master' into wasm32v1-none
StackOverflowExcept1on Jan 18, 2025
d8b1363
Merge branch 'master' into wasm32v1-none
StackOverflowExcept1on Jan 23, 2025
929e755
Merge branch 'master' into wasm32v1-none
StackOverflowExcept1on Jan 25, 2025
94e1fd1
Merge remote-tracking branch 'origin/master' into wasm32v1-none
StackOverflowExcept1on Jan 30, 2025
f5fa789
move `is_target_installed` method to `DummyCrate`
StackOverflowExcept1on Jan 30, 2025
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
22 changes: 22 additions & 0 deletions prdoc/pr_7008.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json

title: 'feat(wasm-builder): add support for new `wasm32v1-none` target'
doc:
- audience: Runtime Dev
description: |
Resolves [#5777](https://github.com/paritytech/polkadot-sdk/issues/5777)

Previously `wasm-builder` used hacks such as `-Zbuild-std` (required `rust-src` component) and `RUSTC_BOOTSTRAP=1` to build WASM runtime without WASM features: `sign-ext`, `multivalue` and `reference-types`, but since Rust 1.84 (will be stable on 9 January, 2025) the situation has improved as there is new [`wasm32v1-none`](https://doc.rust-lang.org/beta/rustc/platform-support/wasm32v1-none.html) target that disables all "post-MVP" WASM features except `mutable-globals`.

Wasm builder requires the following prerequisites for building the WASM binary:
- Rust >= 1.68 and Rust < 1.84:
- `wasm32-unknown-unknown` target
- `rust-src` component
- Rust >= 1.84:
- `wasm32v1-none` target
- no more `-Zbuild-std` and `RUSTC_BOOTSTRAP=1` hacks and `rust-src` component requirements!

crates:
- name: substrate-wasm-builder
bump: minor
bkchr marked this conversation as resolved.
Show resolved Hide resolved
14 changes: 10 additions & 4 deletions scripts/generate-umbrella.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,17 @@ def main(path, version):

# No search for a no_std attribute:
with open(lib_path, "r") as f:
content = f.read()
if "#![no_std]" in content or '#![cfg_attr(not(feature = "std"), no_std)]' in content:
nostd_crate = False
for line in f:
line = line.strip()
if line == "#![no_std]" or line == '#![cfg_attr(not(feature = "std"), no_std)]':
nostd_crate = True
break
elif "no_std" in line:
print(line)

if nostd_crate:
nostd_crates.append((crate, path))
elif 'no_std' in content:
raise Exception(f"Found 'no_std' in {lib_path} without knowing how to handle it")
else:
std_crates.append((crate, path))

Expand Down
2 changes: 1 addition & 1 deletion substrate/primitives/consensus/beefy/src/payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ impl Payload {
pub fn get_all_raw<'a>(
&'a self,
id: &'a BeefyPayloadId,
) -> impl Iterator<Item = &Vec<u8>> + 'a {
) -> impl Iterator<Item = &'a Vec<u8>> + 'a {
self.0
.iter()
.filter_map(move |probe| if &probe.0 != id { return None } else { Some(&probe.1) })
Expand Down
8 changes: 4 additions & 4 deletions substrate/utils/wasm-builder/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ impl WasmBuilder {

/// Enable exporting `__heap_base` as global variable in the WASM binary.
///
/// This adds `-Clink-arg=--export=__heap_base` to `RUST_FLAGS`.
/// This adds `-C link-arg=--export=__heap_base` to `RUST_FLAGS`.
pub fn export_heap_base(mut self) -> Self {
self.export_heap_base = true;
self
Expand Down Expand Up @@ -239,7 +239,7 @@ impl WasmBuilder {

if target == RuntimeTarget::Wasm {
if self.export_heap_base {
self.rust_flags.push("-Clink-arg=--export=__heap_base".into());
self.rust_flags.push("-C link-arg=--export=__heap_base".into());
}

if self.import_memory {
Expand All @@ -265,7 +265,7 @@ impl WasmBuilder {
target,
file_path,
self.project_cargo_toml,
self.rust_flags.into_iter().map(|f| format!("{} ", f)).collect(),
self.rust_flags.join(" "),
self.features_to_enable,
self.file_name,
!self.disable_runtime_version_section_check,
Expand Down Expand Up @@ -353,7 +353,7 @@ fn build_project(
let cargo_cmd = match crate::prerequisites::check(target) {
Ok(cmd) => cmd,
Err(err_msg) => {
eprintln!("{}", err_msg);
eprintln!("{err_msg}");
process::exit(1);
},
};
Expand Down
80 changes: 54 additions & 26 deletions substrate/utils/wasm-builder/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@
//!
//! By using environment variables, you can configure which Wasm binaries are built and how:
//!
//! - `SUBSTRATE_RUNTIME_TARGET` - Sets the target for building runtime. Supported values are `wasm`
//! or `riscv` (experimental, do not use it in production!). By default the target is equal to
//! `wasm`.
//! - `SKIP_WASM_BUILD` - Skips building any Wasm binary. This is useful when only native should be
//! recompiled. If this is the first run and there doesn't exist a Wasm binary, this will set both
//! variables to `None`.
Expand All @@ -78,14 +81,15 @@
//! - `WASM_TARGET_DIRECTORY` - Will copy any build Wasm binary to the given directory. The path
//! needs to be absolute.
//! - `WASM_BUILD_TOOLCHAIN` - The toolchain that should be used to build the Wasm binaries. The
//! format needs to be the same as used by cargo, e.g. `nightly-2020-02-20`.
//! format needs to be the same as used by cargo, e.g. `nightly-2024-12-26`.
//! - `WASM_BUILD_WORKSPACE_HINT` - Hint the workspace that is being built. This is normally not
//! required as we walk up from the target directory until we find a `Cargo.toml`. If the target
//! directory is changed for the build, this environment variable can be used to point to the
//! actual workspace.
//! - `WASM_BUILD_STD` - Sets whether the Rust's standard library crates will also be built. This is
//! necessary to make sure the standard library crates only use the exact WASM feature set that
//! our executor supports. Enabled by default.
//! - `WASM_BUILD_STD` - Sets whether the Rust's standard library crates (`core` and `alloc`) will
//! also be built. This is necessary to make sure the standard library crates only use the exact
//! WASM feature set that our executor supports. Enabled by default for RISC-V target and WASM
//! target (but only if Rust < 1.84). Disabled by default for WASM target and Rust >= 1.84.
//! - `WASM_BUILD_CARGO_ARGS` - This can take a string as space separated list of `cargo` arguments.
//! It was added specifically for the use case of enabling JSON diagnostic messages during the
//! build phase, to be used by IDEs that parse them, but it might be useful for other cases too.
Expand All @@ -99,17 +103,19 @@
//! ## Prerequisites:
//!
//! Wasm builder requires the following prerequisites for building the Wasm binary:
//! - Rust >= 1.68 and Rust < 1.84:
//! - `wasm32-unknown-unknown` target
//! - `rust-src` component
//! - Rust >= 1.84:
//! - `wasm32v1-none` target
//!
//! - rust nightly + `wasm32-unknown-unknown` toolchain
//!
//! or
//!
//! - rust stable and version at least 1.68.0 + `wasm32-unknown-unknown` toolchain
//!
//! If a specific rust is installed with `rustup`, it is important that the wasm target is
//! installed as well. For example if installing the rust from 20.02.2020 using `rustup
//! install nightly-2020-02-20`, the wasm target needs to be installed as well `rustup target add
//! wasm32-unknown-unknown --toolchain nightly-2020-02-20`.
//! If a specific Rust is installed with `rustup`, it is important that the WASM
//! target is installed as well. For example if installing the Rust from
//! 26.12.2024 using `rustup install nightly-2024-12-26`, the WASM target
//! (`wasm32-unknown-unknown` or `wasm32v1-none`) needs to be installed as well
//! `rustup target add wasm32-unknown-unknown --toolchain nightly-2024-12-26`.
//! To install the `rust-src` component, use `rustup component add rust-src
//! --toolchain nightly-2024-12-26`.

use std::{
env, fs,
Expand Down Expand Up @@ -162,7 +168,7 @@ const FORCE_WASM_BUILD_ENV: &str = "FORCE_WASM_BUILD";
/// Environment variable that hints the workspace we are building.
const WASM_BUILD_WORKSPACE_HINT: &str = "WASM_BUILD_WORKSPACE_HINT";

/// Environment variable to set whether we'll build `core`/`std`.
/// Environment variable to set whether we'll build `core`/`alloc`.
const WASM_BUILD_STD: &str = "WASM_BUILD_STD";

/// Environment variable to set additional cargo arguments that might be useful
Expand Down Expand Up @@ -327,6 +333,14 @@ impl CargoCommand {
// Check if major and minor are greater or equal than 1.68 or this is a nightly.
version.major > 1 || (version.major == 1 && version.minor >= 68) || version.is_nightly
}

/// Returns whether this version of the toolchain supports the `wasm32v1-none` target.
fn supports_wasm32v1_none_target(&self) -> bool {
self.version.map_or(false, |version| {
// Check if major and minor are greater or equal than 1.84.
version.major > 1 || (version.major == 1 && version.minor >= 84)
StackOverflowExcept1on marked this conversation as resolved.
Show resolved Hide resolved
})
}
}

/// Wraps a [`CargoCommand`] and the version of `rustc` the cargo command uses.
Expand Down Expand Up @@ -404,9 +418,14 @@ impl RuntimeTarget {
}

/// Figures out the target parameter value for rustc.
fn rustc_target(self) -> String {
fn rustc_target(self, cargo_command: &CargoCommand) -> String {
match self {
RuntimeTarget::Wasm => "wasm32-unknown-unknown".to_string(),
RuntimeTarget::Wasm =>
if cargo_command.supports_wasm32v1_none_target() {
"wasm32v1-none".into()
} else {
"wasm32-unknown-unknown".into()
},
RuntimeTarget::Riscv => {
let path = polkavm_linker::target_json_32_path().expect("riscv not found");
path.into_os_string().into_string().unwrap()
Expand All @@ -415,25 +434,34 @@ impl RuntimeTarget {
}

/// Figures out the target directory name used by cargo.
fn rustc_target_dir(self) -> &'static str {
fn rustc_target_dir(self, cargo_command: &CargoCommand) -> &'static str {
match self {
RuntimeTarget::Wasm => "wasm32-unknown-unknown",
RuntimeTarget::Wasm =>
if cargo_command.supports_wasm32v1_none_target() {
"wasm32v1-none".into()
} else {
"wasm32-unknown-unknown".into()
},
RuntimeTarget::Riscv => "riscv32emac-unknown-none-polkavm",
}
}

/// Figures out the build-std argument.
fn rustc_target_build_std(self) -> Option<&'static str> {
if !crate::get_bool_environment_variable(crate::WASM_BUILD_STD).unwrap_or(true) {
fn rustc_target_build_std(self, cargo_command: &CargoCommand) -> Option<&'static str> {
if !crate::get_bool_environment_variable(crate::WASM_BUILD_STD).unwrap_or_else(
|| match self {
RuntimeTarget::Wasm => !cargo_command.supports_wasm32v1_none_target(),
RuntimeTarget::Riscv => true,
},
) {
return None;
}

// This is a nightly-only flag.
let arg = match self {
RuntimeTarget::Wasm => "build-std",
RuntimeTarget::Riscv => "build-std=core,alloc",
};

Some(arg)
// We only build `core` and `alloc` crates since wasm-builder disables `std` featue for
// runtime. Thus the runtime is `#![no_std]` crate.

Some("build-std=core,alloc")
}
}
78 changes: 57 additions & 21 deletions substrate/utils/wasm-builder/src/prerequisites.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,22 +90,55 @@ impl<'a> DummyCrate<'a> {
fs::create_dir_all(project_dir.join("src")).expect("Creating src dir does not fail; qed");

let manifest_path = project_dir.join("Cargo.toml");
write_file_if_changed(
&manifest_path,
r#"
[package]
name = "dummy-crate"
version = "1.0.0"
edition = "2021"

[workspace]
"#,
);
match target {
Copy link
Member

Choose a reason for hiding this comment

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

Why do we need this?

Choose a reason for hiding this comment

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

I can't compile fn main() {} for wasm32v1-none because it doesn't have stdlib

Copy link
Author

Choose a reason for hiding this comment

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

so i just decided to compile no_std cdylib with empty panic handler (runtime does the same thing, see wasm_project.rs file)

RuntimeTarget::Wasm => {
write_file_if_changed(
&manifest_path,
r#"
[package]
name = "dummy-crate"
version = "1.0.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[workspace]
"#,
);

write_file_if_changed(
project_dir.join("src/lib.rs"),
r#"
#![no_std]

#[panic_handler]
fn panic(_: &core::panic::PanicInfo<'_>) -> ! {
loop {}
}
"#,
);
},
RuntimeTarget::Riscv => {
write_file_if_changed(
&manifest_path,
r#"
[package]
name = "dummy-crate"
version = "1.0.0"
edition = "2021"

[workspace]
"#,
);

write_file_if_changed(
project_dir.join("src/main.rs"),
"#![allow(missing_docs)] fn main() {}",
);
},
}

write_file_if_changed(
project_dir.join("src/main.rs"),
"#![allow(missing_docs)] fn main() {}",
);
DummyCrate { cargo_command, temp, manifest_path, target }
}

Expand All @@ -115,7 +148,7 @@ impl<'a> DummyCrate<'a> {
// by accident - it can happen in some CI environments.
cmd.current_dir(&self.temp);
cmd.arg(subcommand)
.arg(format!("--target={}", self.target.rustc_target()))
.arg(format!("--target={}", self.target.rustc_target(self.cargo_command)))
.args(&["--manifest-path", &self.manifest_path.display().to_string()]);

if super::color_output_enabled() {
Expand Down Expand Up @@ -172,7 +205,10 @@ impl<'a> DummyCrate<'a> {
fn check_wasm_toolchain_installed(
cargo_command: CargoCommand,
) -> Result<CargoCommandVersioned, String> {
let dummy_crate = DummyCrate::new(&cargo_command, RuntimeTarget::Wasm);
let target = RuntimeTarget::Wasm;
let rustc_target = target.rustc_target(&cargo_command);

let dummy_crate = DummyCrate::new(&cargo_command, target);

if let Err(error) = dummy_crate.try_build() {
let toolchain = dummy_crate.get_toolchain().unwrap_or("<unknown>".to_string());
Expand All @@ -181,9 +217,9 @@ fn check_wasm_toolchain_installed(
);
return match error {
None => Err(basic_error_message),
Some(error) if error.contains("the `wasm32-unknown-unknown` target may not be installed") => {
Err(colorize_error_message(&format!("Cannot compile the WASM runtime: the `wasm32-unknown-unknown` target is not installed!\n\
You can install it with `rustup target add wasm32-unknown-unknown --toolchain {toolchain}` if you're using `rustup`.")))
Some(error) if error.contains(&format!("the `{rustc_target}` target may not be installed")) => {
Err(colorize_error_message(&format!("Cannot compile the WASM runtime: the `{rustc_target}` target is not installed!\n\
You can install it with `rustup target add {rustc_target} --toolchain {toolchain}` if you're using `rustup`.")))
},
// Apparently this can happen when we're running on a non Tier 1 platform.
Some(ref error) if error.contains("linker `rust-lld` not found") =>
Expand All @@ -203,7 +239,7 @@ fn check_wasm_toolchain_installed(

let target = RuntimeTarget::new();
assert!(target == RuntimeTarget::Wasm);
if target.rustc_target_build_std().is_some() {
if target.rustc_target_build_std(&cargo_command).is_some() {
if let Some(sysroot) = dummy_crate.get_sysroot() {
let src_path =
Path::new(sysroot.trim()).join("lib").join("rustlib").join("src").join("rust");
Expand Down
Loading
Loading