mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 20:27:58 +00:00
Wasm-builder 3.0 (#7532)
* Build every wasm crate in its own project with wasm-builder Building all wasm crates in one workspace was a nice idea, however it just introduced problems: 1. We needed to prune old members, but this didn't worked for old git deps. 2. We locked the whole wasm workspace while building one crate. This could lead to infinitely locking the workspace on a crash. Now we just build every crate in its own project, this means we will build the dependencies multiple times. While building the dependencies multiple times, we still decrease the build time by around 30 seconds for Polkadot and Substrate because of the new parallelism ;) * Remove the requirement on wasm-builder-runner This removes the requirement on wasm-builder-runner by using the new `build_dep` feature of cargo. We use nightly anyway and that enables us to use this feature. This solves the problem of not mixing build/proc-macro deps with normal deps. By doing this we get rid off this complicated project structure and can depend directly on `wasm-builder`. This also removes all the code from wasm-builder-runner and mentions that it is deprecated. * Copy the `Cargo.lock` to the correct folder * Remove wasm-builder-runner * Update docs * Fix deterministic check Modified-by: Bastian Köcher <git@kchr.de> * Try to make the ui test happy * Switch to `SKIP_WASM_BUILD` * Rename `SKIP_WASM_BINARY` to the correct name... * Update utils/wasm-builder/src/builder.rs Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> * Update utils/wasm-builder/src/builder.rs Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,245 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2019-2020 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::{env, path::{PathBuf, Path}, process};
|
||||
|
||||
/// Returns the manifest dir from the `CARGO_MANIFEST_DIR` env.
|
||||
fn get_manifest_dir() -> PathBuf {
|
||||
env::var("CARGO_MANIFEST_DIR")
|
||||
.expect("`CARGO_MANIFEST_DIR` is always set for `build.rs` files; qed")
|
||||
.into()
|
||||
}
|
||||
|
||||
/// First step of the [`WasmBuilder`] to select the project to build.
|
||||
pub struct WasmBuilderSelectProject {
|
||||
/// This parameter just exists to make it impossible to construct
|
||||
/// this type outside of this crate.
|
||||
_ignore: (),
|
||||
}
|
||||
|
||||
impl WasmBuilderSelectProject {
|
||||
/// Use the current project as project for building the WASM binary.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the `CARGO_MANIFEST_DIR` variable is not set. This variable
|
||||
/// is always set by `Cargo` in `build.rs` files.
|
||||
pub fn with_current_project(self) -> WasmBuilder {
|
||||
WasmBuilder {
|
||||
rust_flags: Vec::new(),
|
||||
file_name: None,
|
||||
project_cargo_toml: get_manifest_dir().join("Cargo.toml"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Use the given `path` as project for building the WASM binary.
|
||||
///
|
||||
/// Returns an error if the given `path` does not points to a `Cargo.toml`.
|
||||
pub fn with_project(
|
||||
self,
|
||||
path: impl Into<PathBuf>,
|
||||
) -> Result<WasmBuilder, &'static str> {
|
||||
let path = path.into();
|
||||
|
||||
if path.ends_with("Cargo.toml") && path.exists() {
|
||||
Ok(WasmBuilder {
|
||||
rust_flags: Vec::new(),
|
||||
file_name: None,
|
||||
project_cargo_toml: path,
|
||||
})
|
||||
} else {
|
||||
Err("Project path must point to the `Cargo.toml` of the project")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The builder for building a wasm binary.
|
||||
///
|
||||
/// The builder itself is separated into multiple structs to make the setup type safe.
|
||||
///
|
||||
/// Building a wasm binary:
|
||||
///
|
||||
/// 1. Call [`WasmBuilder::new`] to create a new builder.
|
||||
/// 2. Select the project to build using the methods of [`WasmBuilderSelectProject`].
|
||||
/// 3. Set additional `RUST_FLAGS` or a different name for the file containing the WASM code
|
||||
/// using methods of [`WasmBuilder`].
|
||||
/// 4. Build the WASM binary using [`Self::build`].
|
||||
pub struct WasmBuilder {
|
||||
/// Flags that should be appended to `RUST_FLAGS` env variable.
|
||||
rust_flags: Vec<String>,
|
||||
/// The name of the file that is being generated in `OUT_DIR`.
|
||||
///
|
||||
/// Defaults to `wasm_binary.rs`.
|
||||
file_name: Option<String>,
|
||||
/// The path to the `Cargo.toml` of the project that should be built
|
||||
/// for wasm.
|
||||
project_cargo_toml: PathBuf,
|
||||
}
|
||||
|
||||
impl WasmBuilder {
|
||||
/// Create a new instance of the builder.
|
||||
pub fn new() -> WasmBuilderSelectProject {
|
||||
WasmBuilderSelectProject {
|
||||
_ignore: (),
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable exporting `__heap_base` as global variable in the WASM binary.
|
||||
///
|
||||
/// This adds `-Clink-arg=--export=__heap_base` to `RUST_FLAGS`.
|
||||
pub fn export_heap_base(mut self) -> Self {
|
||||
self.rust_flags.push("-Clink-arg=--export=__heap_base".into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the name of the file that will be generated in `OUT_DIR`.
|
||||
///
|
||||
/// This file needs to be included to get access to the build WASM binary.
|
||||
///
|
||||
/// If this function is not called, `file_name` defaults to `wasm_binary.rs`
|
||||
pub fn set_file_name(mut self, file_name: impl Into<String>) -> Self {
|
||||
self.file_name = Some(file_name.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Instruct the linker to import the memory into the WASM binary.
|
||||
///
|
||||
/// This adds `-C link-arg=--import-memory` to `RUST_FLAGS`.
|
||||
pub fn import_memory(mut self) -> Self {
|
||||
self.rust_flags.push("-C link-arg=--import-memory".into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Append the given `flag` to `RUST_FLAGS`.
|
||||
///
|
||||
/// `flag` is appended as is, so it needs to be a valid flag.
|
||||
pub fn append_to_rust_flags(mut self, flag: impl Into<String>) -> Self {
|
||||
self.rust_flags.push(flag.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Build the WASM binary.
|
||||
pub fn build(self) {
|
||||
let out_dir = PathBuf::from(env::var("OUT_DIR").expect("`OUT_DIR` is set by cargo!"));
|
||||
let file_path = out_dir.join(self.file_name.unwrap_or_else(|| "wasm_binary.rs".into()));
|
||||
|
||||
if check_skip_build() {
|
||||
// If we skip the build, we still want to make sure to be called when an env variable
|
||||
// changes
|
||||
generate_rerun_if_changed_instructions();
|
||||
|
||||
provide_dummy_wasm_binary_if_not_exist(&file_path);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
build_project(
|
||||
file_path,
|
||||
self.project_cargo_toml,
|
||||
self.rust_flags.into_iter().map(|f| format!("{} ", f)).collect(),
|
||||
);
|
||||
|
||||
// As last step we need to generate our `rerun-if-changed` stuff. If a build fails, we don't
|
||||
// want to spam the output!
|
||||
generate_rerun_if_changed_instructions();
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate the name of the skip build environment variable for the current crate.
|
||||
fn generate_crate_skip_build_env_name() -> String {
|
||||
format!(
|
||||
"SKIP_{}_WASM_BUILD",
|
||||
env::var("CARGO_PKG_NAME").expect("Package name is set").to_uppercase().replace('-', "_"),
|
||||
)
|
||||
}
|
||||
|
||||
/// Checks if the build of the WASM binary should be skipped.
|
||||
fn check_skip_build() -> bool {
|
||||
env::var(crate::SKIP_BUILD_ENV).is_ok() || env::var(generate_crate_skip_build_env_name()).is_ok()
|
||||
}
|
||||
|
||||
/// Provide a dummy WASM binary if there doesn't exist one.
|
||||
fn provide_dummy_wasm_binary_if_not_exist(file_path: &Path) {
|
||||
if !file_path.exists() {
|
||||
crate::write_file_if_changed(
|
||||
file_path,
|
||||
"pub const WASM_BINARY: Option<&[u8]> = None;\
|
||||
pub const WASM_BINARY_BLOATY: Option<&[u8]> = None;",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate the `rerun-if-changed` instructions for cargo to make sure that the WASM binary is
|
||||
/// rebuilt when needed.
|
||||
fn generate_rerun_if_changed_instructions() {
|
||||
// Make sure that the `build.rs` is called again if one of the following env variables changes.
|
||||
println!("cargo:rerun-if-env-changed={}", crate::SKIP_BUILD_ENV);
|
||||
println!("cargo:rerun-if-env-changed={}", crate::FORCE_WASM_BUILD_ENV);
|
||||
println!("cargo:rerun-if-env-changed={}", generate_crate_skip_build_env_name());
|
||||
}
|
||||
|
||||
/// Build the currently built project as wasm binary.
|
||||
///
|
||||
/// The current project is determined by using the `CARGO_MANIFEST_DIR` environment variable.
|
||||
///
|
||||
/// `file_name` - The name + path of the file being generated. The file contains the
|
||||
/// constant `WASM_BINARY`, which contains the built WASM binary.
|
||||
/// `project_cargo_toml` - The path to the `Cargo.toml` of the project that should be built.
|
||||
/// `default_rustflags` - Default `RUSTFLAGS` that will always be set for the build.
|
||||
fn build_project(
|
||||
file_name: PathBuf,
|
||||
project_cargo_toml: PathBuf,
|
||||
default_rustflags: String,
|
||||
) {
|
||||
let cargo_cmd = match crate::prerequisites::check() {
|
||||
Ok(cmd) => cmd,
|
||||
Err(err_msg) => {
|
||||
eprintln!("{}", err_msg);
|
||||
process::exit(1);
|
||||
},
|
||||
};
|
||||
|
||||
let (wasm_binary, bloaty) = crate::wasm_project::create_and_compile(
|
||||
&project_cargo_toml,
|
||||
&default_rustflags,
|
||||
cargo_cmd,
|
||||
);
|
||||
|
||||
let (wasm_binary, wasm_binary_bloaty) = if let Some(wasm_binary) = wasm_binary {
|
||||
(
|
||||
wasm_binary.wasm_binary_path_escaped(),
|
||||
bloaty.wasm_binary_bloaty_path_escaped(),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
bloaty.wasm_binary_bloaty_path_escaped(),
|
||||
bloaty.wasm_binary_bloaty_path_escaped(),
|
||||
)
|
||||
};
|
||||
|
||||
crate::write_file_if_changed(
|
||||
file_name,
|
||||
format!(
|
||||
r#"
|
||||
pub const WASM_BINARY: Option<&[u8]> = Some(include_bytes!("{wasm_binary}"));
|
||||
pub const WASM_BINARY_BLOATY: Option<&[u8]> = Some(include_bytes!("{wasm_binary_bloaty}"));
|
||||
"#,
|
||||
wasm_binary = wasm_binary,
|
||||
wasm_binary_bloaty = wasm_binary_bloaty,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -25,20 +25,23 @@
|
||||
//! A project that should be compiled as a Wasm binary needs to:
|
||||
//!
|
||||
//! 1. Add a `build.rs` file.
|
||||
//! 2. Add `substrate-wasm-builder` as dependency into `build-dependencies`.
|
||||
//! 2. Add `wasm-builder` as dependency into `build-dependencies`.
|
||||
//!
|
||||
//! The `build.rs` file needs to contain the following code:
|
||||
//!
|
||||
//! ```ignore
|
||||
//! use wasm_builder_runner::{build_current_project, WasmBuilderSource};
|
||||
//! ```no_run
|
||||
//! use substrate_wasm_builder::WasmBuilder;
|
||||
//!
|
||||
//! fn main() {
|
||||
//! build_current_project(
|
||||
//! // The name of the file being generated in out-dir.
|
||||
//! "wasm_binary.rs",
|
||||
//! // How to include wasm-builder, in this case from crates.io.
|
||||
//! WasmBuilderSource::Crates("1.0.0"),
|
||||
//! );
|
||||
//! WasmBuilder::new()
|
||||
//! // Tell the builder to build the project (crate) this `build.rs` is part of.
|
||||
//! .with_current_project()
|
||||
//! // Make sure to export the `heap_base` global, this is required by Substrate
|
||||
//! .export_heap_base()
|
||||
//! // Build the Wasm file so that it imports the memory (need to be provided by at instantiation)
|
||||
//! .import_memory()
|
||||
//! // Build it.
|
||||
//! .build()
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
@@ -49,7 +52,8 @@
|
||||
//! ```
|
||||
//!
|
||||
//! This will include the generated Wasm binary as two constants `WASM_BINARY` and `WASM_BINARY_BLOATY`.
|
||||
//! The former is a compact Wasm binary and the latter is not compacted.
|
||||
//! The former is a compact Wasm binary and the latter is the Wasm binary as being generated by the compiler.
|
||||
//! Both variables have `Option<&'static [u8]>` as type.
|
||||
//!
|
||||
//! ### Feature
|
||||
//!
|
||||
@@ -63,19 +67,19 @@
|
||||
//!
|
||||
//! By using environment variables, you can configure which Wasm binaries are built and how:
|
||||
//!
|
||||
//! - `SKIP_WASM_BUILD` - Skips building any wasm binary. This is useful when only native should be recompiled.
|
||||
//! - `BUILD_DUMMY_WASM_BINARY` - Builds dummy wasm binaries. These dummy binaries are empty and useful
|
||||
//! for `cargo check` runs.
|
||||
//! - `WASM_BUILD_TYPE` - Sets the build type for building wasm binaries. Supported values are `release` or `debug`.
|
||||
//! - `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`.
|
||||
//! - `WASM_BUILD_TYPE` - Sets the build type for building Wasm binaries. Supported values are `release` or `debug`.
|
||||
//! By default the build type is equal to the build type used by the main build.
|
||||
//! - `FORCE_WASM_BUILD` - Can be set to force a wasm build. On subsequent calls the value of the variable
|
||||
//! needs to change. As wasm builder instructs `cargo` to watch for file changes
|
||||
//! - `FORCE_WASM_BUILD` - Can be set to force a Wasm build. On subsequent calls the value of the variable
|
||||
//! needs to change. As wasm-builder instructs `cargo` to watch for file changes
|
||||
//! this environment variable should only be required in certain circumstances.
|
||||
//! - `WASM_BUILD_RUSTFLAGS` - Extend `RUSTFLAGS` given to `cargo build` while building the wasm binary.
|
||||
//! - `WASM_BUILD_NO_COLOR` - Disable color output of the wasm build.
|
||||
//! - `WASM_TARGET_DIRECTORY` - Will copy any build wasm binary to the given directory. The path needs
|
||||
//! - `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
|
||||
//! - `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`.
|
||||
//!
|
||||
//! Each project can be skipped individually by using the environment variable `SKIP_PROJECT_NAME_WASM_BUILD`.
|
||||
@@ -92,11 +96,14 @@
|
||||
//! as well. For example if installing the rust nightly 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`.
|
||||
|
||||
use std::{env, fs, path::{PathBuf, Path}, process::{Command, self}, io::BufRead};
|
||||
use std::{env, fs, path::{PathBuf, Path}, process::Command, io::BufRead};
|
||||
|
||||
mod builder;
|
||||
mod prerequisites;
|
||||
mod wasm_project;
|
||||
|
||||
pub use builder::{WasmBuilder, WasmBuilderSelectProject};
|
||||
|
||||
/// Environment variable that tells us to skip building the wasm binary.
|
||||
const SKIP_BUILD_ENV: &str = "SKIP_WASM_BUILD";
|
||||
|
||||
@@ -120,87 +127,8 @@ const WASM_BUILD_NO_COLOR: &str = "WASM_BUILD_NO_COLOR";
|
||||
/// Environment variable to set the toolchain used to compile the wasm binary.
|
||||
const WASM_BUILD_TOOLCHAIN: &str = "WASM_BUILD_TOOLCHAIN";
|
||||
|
||||
/// Build the currently built project as wasm binary.
|
||||
///
|
||||
/// The current project is determined by using the `CARGO_MANIFEST_DIR` environment variable.
|
||||
///
|
||||
/// `file_name` - The name + path of the file being generated. The file contains the
|
||||
/// constant `WASM_BINARY`, which contains the built WASM binary.
|
||||
/// `cargo_manifest` - The path to the `Cargo.toml` of the project that should be built.
|
||||
pub fn build_project(file_name: &str, cargo_manifest: &str) {
|
||||
build_project_with_default_rustflags(file_name, cargo_manifest, "");
|
||||
}
|
||||
|
||||
/// Build the currently built project as wasm binary.
|
||||
///
|
||||
/// The current project is determined by using the `CARGO_MANIFEST_DIR` environment variable.
|
||||
///
|
||||
/// `file_name` - The name + path of the file being generated. The file contains the
|
||||
/// constant `WASM_BINARY`, which contains the built WASM binary.
|
||||
/// `cargo_manifest` - The path to the `Cargo.toml` of the project that should be built.
|
||||
/// `default_rustflags` - Default `RUSTFLAGS` that will always be set for the build.
|
||||
pub fn build_project_with_default_rustflags(
|
||||
file_name: &str,
|
||||
cargo_manifest: &str,
|
||||
default_rustflags: &str,
|
||||
) {
|
||||
if check_skip_build() {
|
||||
return;
|
||||
}
|
||||
|
||||
let cargo_manifest = PathBuf::from(cargo_manifest);
|
||||
|
||||
if !cargo_manifest.exists() {
|
||||
panic!("'{}' does not exist!", cargo_manifest.display());
|
||||
}
|
||||
|
||||
if !cargo_manifest.ends_with("Cargo.toml") {
|
||||
panic!("'{}' no valid path to a `Cargo.toml`!", cargo_manifest.display());
|
||||
}
|
||||
|
||||
let cargo_cmd = match prerequisites::check() {
|
||||
Ok(cmd) => cmd,
|
||||
Err(err_msg) => {
|
||||
eprintln!("{}", err_msg);
|
||||
process::exit(1);
|
||||
},
|
||||
};
|
||||
|
||||
let (wasm_binary, bloaty) = wasm_project::create_and_compile(
|
||||
&cargo_manifest,
|
||||
default_rustflags,
|
||||
cargo_cmd,
|
||||
);
|
||||
|
||||
let (wasm_binary, wasm_binary_bloaty) = if let Some(wasm_binary) = wasm_binary {
|
||||
(
|
||||
wasm_binary.wasm_binary_path_escaped(),
|
||||
bloaty.wasm_binary_bloaty_path_escaped(),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
bloaty.wasm_binary_bloaty_path_escaped(),
|
||||
bloaty.wasm_binary_bloaty_path_escaped(),
|
||||
)
|
||||
};
|
||||
|
||||
write_file_if_changed(
|
||||
file_name,
|
||||
format!(
|
||||
r#"
|
||||
pub const WASM_BINARY: Option<&[u8]> = Some(include_bytes!("{wasm_binary}"));
|
||||
pub const WASM_BINARY_BLOATY: Option<&[u8]> = Some(include_bytes!("{wasm_binary_bloaty}"));
|
||||
"#,
|
||||
wasm_binary = wasm_binary,
|
||||
wasm_binary_bloaty = wasm_binary_bloaty,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Checks if the build of the WASM binary should be skipped.
|
||||
fn check_skip_build() -> bool {
|
||||
env::var(SKIP_BUILD_ENV).is_ok()
|
||||
}
|
||||
/// Environment variable that makes sure the WASM build is triggered.
|
||||
const FORCE_WASM_BUILD_ENV: &str = "FORCE_WASM_BUILD";
|
||||
|
||||
/// Write to the given `file` if the `content` is different.
|
||||
fn write_file_if_changed(file: impl AsRef<Path>, content: impl AsRef<str>) {
|
||||
@@ -217,7 +145,9 @@ fn copy_file_if_changed(src: PathBuf, dst: PathBuf) {
|
||||
|
||||
if src_file != dst_file {
|
||||
fs::copy(&src, &dst)
|
||||
.unwrap_or_else(|_| panic!("Copying `{}` to `{}` can not fail; qed", src.display(), dst.display()));
|
||||
.unwrap_or_else(
|
||||
|_| panic!("Copying `{}` to `{}` can not fail; qed", src.display(), dst.display())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,10 +30,6 @@ use cargo_metadata::{MetadataCommand, Metadata};
|
||||
|
||||
use walkdir::WalkDir;
|
||||
|
||||
use fs2::FileExt;
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
/// Colorize an info message.
|
||||
///
|
||||
/// Returns the colorized message.
|
||||
@@ -70,31 +66,6 @@ impl WasmBinary {
|
||||
}
|
||||
}
|
||||
|
||||
/// A lock for the WASM workspace.
|
||||
struct WorkspaceLock(fs::File);
|
||||
|
||||
impl WorkspaceLock {
|
||||
/// Create a new lock
|
||||
fn new(wasm_workspace_root: &Path) -> Self {
|
||||
let lock = fs::OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(wasm_workspace_root.join("wasm_workspace.lock"))
|
||||
.expect("Opening the lock file does not fail");
|
||||
|
||||
lock.lock_exclusive().expect("Locking `wasm_workspace.lock` failed");
|
||||
|
||||
WorkspaceLock(lock)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for WorkspaceLock {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.0.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
fn crate_metadata(cargo_manifest: &Path) -> Metadata {
|
||||
let mut cargo_lock = cargo_manifest.to_path_buf();
|
||||
cargo_lock.set_file_name("Cargo.lock");
|
||||
@@ -120,35 +91,36 @@ fn crate_metadata(cargo_manifest: &Path) -> Metadata {
|
||||
/// Creates the WASM project, compiles the WASM binary and compacts the WASM binary.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// The path to the compact WASM binary and the bloaty WASM binary.
|
||||
pub(crate) fn create_and_compile(
|
||||
cargo_manifest: &Path,
|
||||
project_cargo_toml: &Path,
|
||||
default_rustflags: &str,
|
||||
cargo_cmd: CargoCommandVersioned,
|
||||
) -> (Option<WasmBinary>, WasmBinaryBloaty) {
|
||||
let wasm_workspace_root = get_wasm_workspace_root();
|
||||
let wasm_workspace = wasm_workspace_root.join("wbuild");
|
||||
|
||||
// Lock the workspace exclusively for us
|
||||
let _lock = WorkspaceLock::new(&wasm_workspace_root);
|
||||
let crate_metadata = crate_metadata(project_cargo_toml);
|
||||
|
||||
let crate_metadata = crate_metadata(cargo_manifest);
|
||||
|
||||
let project = create_project(cargo_manifest, &wasm_workspace, &crate_metadata);
|
||||
create_wasm_workspace_project(&wasm_workspace, &crate_metadata.workspace_root);
|
||||
let project = create_project(
|
||||
project_cargo_toml,
|
||||
&wasm_workspace,
|
||||
&crate_metadata,
|
||||
&crate_metadata.workspace_root,
|
||||
);
|
||||
|
||||
build_project(&project, default_rustflags, cargo_cmd);
|
||||
let (wasm_binary, bloaty) = compact_wasm_file(
|
||||
&project,
|
||||
cargo_manifest,
|
||||
&wasm_workspace,
|
||||
project_cargo_toml,
|
||||
);
|
||||
|
||||
wasm_binary.as_ref().map(|wasm_binary|
|
||||
copy_wasm_to_target_directory(cargo_manifest, wasm_binary)
|
||||
copy_wasm_to_target_directory(project_cargo_toml, wasm_binary)
|
||||
);
|
||||
|
||||
generate_rerun_if_changed_instructions(cargo_manifest, &project, &wasm_workspace);
|
||||
generate_rerun_if_changed_instructions(project_cargo_toml, &project, &wasm_workspace);
|
||||
|
||||
(wasm_binary, bloaty)
|
||||
}
|
||||
@@ -221,69 +193,14 @@ fn get_wasm_workspace_root() -> PathBuf {
|
||||
panic!("Could not find target dir in: {}", build_helper::out_dir().display())
|
||||
}
|
||||
|
||||
/// Find all workspace members.
|
||||
///
|
||||
/// Each folder in `wasm_workspace` is seen as a member of the workspace. Exceptions are
|
||||
/// folders starting with "." and the "target" folder.
|
||||
///
|
||||
/// Every workspace member that is not valid anymore is deleted (the folder of it). A
|
||||
/// member is not valid anymore when the `wasm-project` dependency points to an non-existing
|
||||
/// folder or the package name is not valid.
|
||||
fn find_and_clear_workspace_members(wasm_workspace: &Path) -> Vec<String> {
|
||||
let mut members = WalkDir::new(wasm_workspace)
|
||||
.min_depth(1)
|
||||
.max_depth(1)
|
||||
.into_iter()
|
||||
.filter_map(|p| p.ok())
|
||||
.map(|d| d.into_path())
|
||||
.filter(|p| p.is_dir())
|
||||
.filter_map(|p| p.file_name().map(|f| f.to_owned()).and_then(|s| s.into_string().ok()))
|
||||
.filter(|f| !f.starts_with('.') && f != "target")
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut i = 0;
|
||||
while i != members.len() {
|
||||
let path = wasm_workspace.join(&members[i]).join("Cargo.toml");
|
||||
|
||||
// Extract the `wasm-project` dependency.
|
||||
// If the path can be extracted and is valid and the package name matches,
|
||||
// the member is valid.
|
||||
if let Some(mut wasm_project) = fs::read_to_string(path)
|
||||
.ok()
|
||||
.and_then(|s| toml::from_str::<Table>(&s).ok())
|
||||
.and_then(|mut t| t.remove("dependencies"))
|
||||
.and_then(|p| p.try_into::<Table>().ok())
|
||||
.and_then(|mut t| t.remove("wasm_project"))
|
||||
.and_then(|p| p.try_into::<Table>().ok())
|
||||
{
|
||||
if let Some(path) = wasm_project.remove("path")
|
||||
.and_then(|p| p.try_into::<String>().ok())
|
||||
{
|
||||
if let Some(name) = wasm_project.remove("package")
|
||||
.and_then(|p| p.try_into::<String>().ok())
|
||||
{
|
||||
let path = PathBuf::from(path);
|
||||
if path.exists() {
|
||||
if name == get_crate_name(&path.join("Cargo.toml")) {
|
||||
i += 1;
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fs::remove_dir_all(wasm_workspace.join(&members[i]))
|
||||
.expect("Removing invalid workspace member can not fail; qed");
|
||||
members.remove(i);
|
||||
}
|
||||
|
||||
members
|
||||
}
|
||||
|
||||
fn create_wasm_workspace_project(wasm_workspace: &Path, workspace_root_path: &Path) {
|
||||
let members = find_and_clear_workspace_members(wasm_workspace);
|
||||
|
||||
fn create_project_cargo_toml(
|
||||
wasm_workspace: &Path,
|
||||
workspace_root_path: &Path,
|
||||
crate_name: &str,
|
||||
crate_path: &Path,
|
||||
wasm_binary: &str,
|
||||
enabled_features: &[String],
|
||||
) {
|
||||
let mut workspace_toml: Table = toml::from_str(
|
||||
&fs::read_to_string(
|
||||
workspace_root_path.join("Cargo.toml"),
|
||||
@@ -306,12 +223,6 @@ fn create_wasm_workspace_project(wasm_workspace: &Path, workspace_root_path: &Pa
|
||||
|
||||
wasm_workspace_toml.insert("profile".into(), profile.into());
|
||||
|
||||
// Add `workspace` with members
|
||||
let mut workspace = Table::new();
|
||||
workspace.insert("members".into(), members.into());
|
||||
|
||||
wasm_workspace_toml.insert("workspace".into(), workspace.into());
|
||||
|
||||
// Add patch section from the project root `Cargo.toml`
|
||||
if let Some(mut patch) = workspace_toml.remove("patch").and_then(|p| p.try_into::<Table>().ok()) {
|
||||
// Iterate over all patches and make the patch path absolute from the workspace root path.
|
||||
@@ -335,6 +246,33 @@ fn create_wasm_workspace_project(wasm_workspace: &Path, workspace_root_path: &Pa
|
||||
wasm_workspace_toml.insert("patch".into(), patch.into());
|
||||
}
|
||||
|
||||
let mut package = Table::new();
|
||||
package.insert("name".into(), format!("{}-wasm", crate_name).into());
|
||||
package.insert("version".into(), "1.0.0".into());
|
||||
package.insert("edition".into(), "2018".into());
|
||||
|
||||
wasm_workspace_toml.insert("package".into(), package.into());
|
||||
|
||||
let mut lib = Table::new();
|
||||
lib.insert("name".into(), wasm_binary.into());
|
||||
lib.insert("crate-type".into(), vec!["cdylib".to_string()].into());
|
||||
|
||||
wasm_workspace_toml.insert("lib".into(), lib.into());
|
||||
|
||||
let mut dependencies = Table::new();
|
||||
|
||||
let mut wasm_project = Table::new();
|
||||
wasm_project.insert("package".into(), crate_name.into());
|
||||
wasm_project.insert("path".into(), crate_path.display().to_string().into());
|
||||
wasm_project.insert("default-features".into(), false.into());
|
||||
wasm_project.insert("features".into(), enabled_features.to_vec().into());
|
||||
|
||||
dependencies.insert("wasm-project".into(), wasm_project.into());
|
||||
|
||||
wasm_workspace_toml.insert("dependencies".into(), dependencies.into());
|
||||
|
||||
wasm_workspace_toml.insert("workspace".into(), Table::new().into());
|
||||
|
||||
write_file_if_changed(
|
||||
wasm_workspace.join("Cargo.toml"),
|
||||
toml::to_string_pretty(&wasm_workspace_toml).expect("Wasm workspace toml is valid; qed"),
|
||||
@@ -394,56 +332,48 @@ fn has_runtime_wasm_feature_declared(
|
||||
/// Create the project used to build the wasm binary.
|
||||
///
|
||||
/// # Returns
|
||||
/// The path to the created project.
|
||||
fn create_project(cargo_manifest: &Path, wasm_workspace: &Path, crate_metadata: &Metadata) -> PathBuf {
|
||||
let crate_name = get_crate_name(cargo_manifest);
|
||||
let crate_path = cargo_manifest.parent().expect("Parent path exists; qed");
|
||||
let wasm_binary = get_wasm_binary_name(cargo_manifest);
|
||||
let project_folder = wasm_workspace.join(&crate_name);
|
||||
///
|
||||
/// The path to the created wasm project.
|
||||
fn create_project(
|
||||
project_cargo_toml: &Path,
|
||||
wasm_workspace: &Path,
|
||||
crate_metadata: &Metadata,
|
||||
workspace_root_path: &Path,
|
||||
) -> PathBuf {
|
||||
let crate_name = get_crate_name(project_cargo_toml);
|
||||
let crate_path = project_cargo_toml.parent().expect("Parent path exists; qed");
|
||||
let wasm_binary = get_wasm_binary_name(project_cargo_toml);
|
||||
let wasm_project_folder = wasm_workspace.join(&crate_name);
|
||||
|
||||
fs::create_dir_all(project_folder.join("src"))
|
||||
fs::create_dir_all(wasm_project_folder.join("src"))
|
||||
.expect("Wasm project dir create can not fail; qed");
|
||||
|
||||
let mut enabled_features = project_enabled_features(&cargo_manifest, &crate_metadata);
|
||||
let mut enabled_features = project_enabled_features(&project_cargo_toml, &crate_metadata);
|
||||
|
||||
if has_runtime_wasm_feature_declared(cargo_manifest, crate_metadata) {
|
||||
if has_runtime_wasm_feature_declared(project_cargo_toml, crate_metadata) {
|
||||
enabled_features.push("runtime-wasm".into());
|
||||
}
|
||||
|
||||
write_file_if_changed(
|
||||
project_folder.join("Cargo.toml"),
|
||||
format!(
|
||||
r#"
|
||||
[package]
|
||||
name = "{crate_name}-wasm"
|
||||
version = "1.0.0"
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
name = "{wasm_binary}"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
wasm_project = {{ package = "{crate_name}", path = "{crate_path}", default-features = false, features = [ {features} ] }}
|
||||
"#,
|
||||
crate_name = crate_name,
|
||||
crate_path = crate_path.display(),
|
||||
wasm_binary = wasm_binary,
|
||||
features = enabled_features.into_iter().map(|f| format!("\"{}\"", f)).join(","),
|
||||
)
|
||||
create_project_cargo_toml(
|
||||
&wasm_project_folder,
|
||||
workspace_root_path,
|
||||
&crate_name,
|
||||
&crate_path,
|
||||
&wasm_binary,
|
||||
&enabled_features,
|
||||
);
|
||||
|
||||
write_file_if_changed(
|
||||
project_folder.join("src/lib.rs"),
|
||||
wasm_project_folder.join("src/lib.rs"),
|
||||
"#![no_std] pub use wasm_project::*;",
|
||||
);
|
||||
|
||||
if let Some(crate_lock_file) = find_cargo_lock(cargo_manifest) {
|
||||
if let Some(crate_lock_file) = find_cargo_lock(project_cargo_toml) {
|
||||
// Use the `Cargo.lock` of the main project.
|
||||
crate::copy_file_if_changed(crate_lock_file, wasm_workspace.join("Cargo.lock"));
|
||||
crate::copy_file_if_changed(crate_lock_file, wasm_project_folder.join("Cargo.lock"));
|
||||
}
|
||||
|
||||
project_folder
|
||||
wasm_project_folder
|
||||
}
|
||||
|
||||
/// Returns if the project should be built as a release.
|
||||
@@ -474,9 +404,13 @@ fn build_project(project: &Path, default_rustflags: &str, cargo_cmd: CargoComman
|
||||
env::var(crate::WASM_BUILD_RUSTFLAGS_ENV).unwrap_or_default(),
|
||||
);
|
||||
|
||||
build_cmd.args(&["rustc", "--target=wasm32-unknown-unknown"])
|
||||
build_cmd.args(&["-Zfeatures=build_dep", "rustc", "--target=wasm32-unknown-unknown"])
|
||||
.arg(format!("--manifest-path={}", manifest_path.display()))
|
||||
.env("RUSTFLAGS", rustflags)
|
||||
// Unset the `CARGO_TARGET_DIR` to prevent a cargo deadlock (cargo locks a target dir exclusive).
|
||||
// The runner project is created in `CARGO_TARGET_DIR` and executing it will create a sub target
|
||||
// directory inside of `CARGO_TARGET_DIR`.
|
||||
.env_remove("CARGO_TARGET_DIR")
|
||||
// We don't want to call ourselves recursively
|
||||
.env(crate::SKIP_BUILD_ENV, "");
|
||||
|
||||
@@ -503,14 +437,14 @@ fn build_project(project: &Path, default_rustflags: &str, cargo_cmd: CargoComman
|
||||
fn compact_wasm_file(
|
||||
project: &Path,
|
||||
cargo_manifest: &Path,
|
||||
wasm_workspace: &Path,
|
||||
) -> (Option<WasmBinary>, WasmBinaryBloaty) {
|
||||
let is_release_build = is_release_build();
|
||||
let target = if is_release_build { "release" } else { "debug" };
|
||||
let wasm_binary = get_wasm_binary_name(cargo_manifest);
|
||||
let wasm_file = wasm_workspace.join("target/wasm32-unknown-unknown")
|
||||
let wasm_file = project.join("target/wasm32-unknown-unknown")
|
||||
.join(target)
|
||||
.join(format!("{}.wasm", wasm_binary));
|
||||
|
||||
let wasm_compact_file = if is_release_build {
|
||||
let wasm_compact_file = project.join(format!("{}.compact.wasm", wasm_binary));
|
||||
wasm_gc::garbage_collect_file(&wasm_file, &wasm_compact_file)
|
||||
|
||||
Reference in New Issue
Block a user