Make wasm-builder print the rustc version (#7351)

* Make `wasm-builder` print the rustc version

This makes `wasm-builder` print the rustc version that is being used to
compile the project. This is rather useful, because people can check
faster if the used rustc version is maybe known for being broken with
Substrate.

* Apply suggestions from code review

* Add some comments
This commit is contained in:
Bastian Köcher
2020-10-19 14:45:09 +02:00
committed by GitHub
parent 7ca9baf9b6
commit 23382db1b4
3 changed files with 142 additions and 43 deletions
+43 -10
View File
@@ -92,7 +92,7 @@
//! 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, process::{Command, self}, io::BufRead};
use std::{env, fs, path::{PathBuf, Path}, process::{Command, self}, io::BufRead};
mod prerequisites;
mod wasm_project;
@@ -158,14 +158,18 @@ pub fn build_project_with_default_rustflags(
panic!("'{}' no valid path to a `Cargo.toml`!", cargo_manifest.display());
}
if let Some(err_msg) = prerequisites::check() {
eprintln!("{}", err_msg);
process::exit(1);
}
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 {
@@ -181,7 +185,7 @@ pub fn build_project_with_default_rustflags(
};
write_file_if_changed(
file_name.into(),
file_name,
format!(
r#"
pub const WASM_BINARY: Option<&[u8]> = Some(include_bytes!("{wasm_binary}"));
@@ -199,9 +203,10 @@ fn check_skip_build() -> bool {
}
/// Write to the given `file` if the `content` is different.
fn write_file_if_changed(file: PathBuf, content: String) {
if fs::read_to_string(&file).ok().as_ref() != Some(&content) {
fs::write(&file, content).unwrap_or_else(|_| panic!("Writing `{}` can not fail!", file.display()));
fn write_file_if_changed(file: impl AsRef<Path>, content: impl AsRef<str>) {
if fs::read_to_string(file.as_ref()).ok().as_deref() != Some(content.as_ref()) {
fs::write(file.as_ref(), content.as_ref())
.unwrap_or_else(|_| panic!("Writing `{}` can not fail!", file.as_ref().display()));
}
}
@@ -268,7 +273,7 @@ fn get_rustup_nightly(selected: Option<String>) -> Option<CargoCommand> {
Some(CargoCommand::new_with_args("rustup", &["run", &version, "cargo"]))
}
/// Builder for cargo commands
/// Wraps a specific command which represents a cargo invocation.
#[derive(Debug)]
struct CargoCommand {
program: String,
@@ -310,6 +315,34 @@ impl CargoCommand {
}
}
/// Wraps a [`CargoCommand`] and the version of `rustc` the cargo command uses.
struct CargoCommandVersioned {
command: CargoCommand,
version: String,
}
impl CargoCommandVersioned {
fn new(command: CargoCommand, version: String) -> Self {
Self {
command,
version,
}
}
/// Returns the `rustc` version.
fn rustc_version(&self) -> &str {
&self.version
}
}
impl std::ops::Deref for CargoCommandVersioned {
type Target = CargoCommand;
fn deref(&self) -> &CargoCommand {
&self.command
}
}
/// Returns `true` when color output is enabled.
fn color_output_enabled() -> bool {
env::var(crate::WASM_BUILD_NO_COLOR).is_err()
@@ -15,7 +15,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use std::fs;
use crate::{CargoCommandVersioned, CargoCommand, write_file_if_changed};
use std::{fs, path::Path};
use tempfile::tempdir;
use ansi_term::Color;
@@ -31,33 +33,33 @@ fn print_error_message(message: &str) -> String {
/// Checks that all prerequisites are installed.
///
/// # Returns
/// Returns `None` if everything was found and `Some(ERR_MSG)` if something could not be found.
pub fn check() -> Option<String> {
if !check_nightly_installed(){
return Some(print_error_message("Rust nightly not installed, please install it!"))
/// Returns the versioned cargo command on success.
pub(crate) fn check() -> Result<CargoCommandVersioned, String> {
let cargo_command = crate::get_nightly_cargo();
if !cargo_command.is_nightly() {
return Err(print_error_message("Rust nightly not installed, please install it!"))
}
check_wasm_toolchain_installed()
check_wasm_toolchain_installed(cargo_command)
}
fn check_nightly_installed() -> bool {
crate::get_nightly_cargo().is_nightly()
}
/// Create the project that will be used to check that the wasm toolchain is installed and to
/// extract the rustc version.
fn create_check_toolchain_project(project_dir: &Path) {
let lib_rs_file = project_dir.join("src/lib.rs");
let main_rs_file = project_dir.join("src/main.rs");
let build_rs_file = project_dir.join("build.rs");
let manifest_path = project_dir.join("Cargo.toml");
fn check_wasm_toolchain_installed() -> Option<String> {
let temp = tempdir().expect("Creating temp dir does not fail; qed");
fs::create_dir_all(temp.path().join("src")).expect("Creating src dir does not fail; qed");
let test_file = temp.path().join("src/lib.rs");
let manifest_path = temp.path().join("Cargo.toml");
fs::write(&manifest_path,
write_file_if_changed(
&manifest_path,
r#"
[package]
name = "wasm-test"
version = "1.0.0"
edition = "2018"
build = "build.rs"
[lib]
name = "wasm_test"
@@ -65,27 +67,78 @@ fn check_wasm_toolchain_installed() -> Option<String> {
[workspace]
"#,
).expect("Writing wasm-test manifest does not fail; qed");
fs::write(&test_file, "pub fn test() {}")
.expect("Writing to the test file does not fail; qed");
);
write_file_if_changed(lib_rs_file, "pub fn test() {}");
// We want to know the rustc version of the rustc that is being used by our cargo command.
// The cargo command is determined by some *very* complex algorithm to find the cargo command
// that supports nightly.
// The best solution would be if there is a `cargo rustc --version` command, which sadly
// doesn't exists. So, the only available way of getting the rustc version is to build a project
// and capture the rustc version in this build process. This `build.rs` is exactly doing this.
// It gets the rustc version by calling `rustc --version` and exposing it in the `RUSTC_VERSION`
// environment variable.
write_file_if_changed(
build_rs_file,
r#"
fn main() {
let rustc_cmd = std::env::var("RUSTC").ok().unwrap_or_else(|| "rustc".into());
let rustc_version = std::process::Command::new(rustc_cmd)
.arg("--version")
.output()
.ok()
.and_then(|o| String::from_utf8(o.stdout).ok());
println!(
"cargo:rustc-env=RUSTC_VERSION={}",
rustc_version.unwrap_or_else(|| "unknown rustc version".into()),
);
}
"#
);
// Just prints the `RURSTC_VERSION` environment variable that is being created by the
// `build.rs` script.
write_file_if_changed(
main_rs_file,
r#"
fn main() {
println!("{}", env!("RUSTC_VERSION"));
}
"#
);
}
fn check_wasm_toolchain_installed(
cargo_command: CargoCommand,
) -> Result<CargoCommandVersioned, String> {
let temp = tempdir().expect("Creating temp dir does not fail; qed");
fs::create_dir_all(temp.path().join("src")).expect("Creating src dir does not fail; qed");
create_check_toolchain_project(temp.path());
let err_msg = print_error_message("Rust WASM toolchain not installed, please install it!");
let manifest_path = manifest_path.display().to_string();
let mut build_cmd = crate::get_nightly_cargo().command();
let manifest_path = temp.path().join("Cargo.toml").display().to_string();
let mut build_cmd = cargo_command.command();
build_cmd.args(&["build", "--target=wasm32-unknown-unknown", "--manifest-path", &manifest_path]);
if super::color_output_enabled() {
build_cmd.arg("--color=always");
}
let mut run_cmd = cargo_command.command();
run_cmd.args(&["run", "--manifest-path", &manifest_path]);
build_cmd
.output()
.map_err(|_| err_msg.clone())
.and_then(|s|
if s.status.success() {
Ok(())
let version = run_cmd.output().ok().and_then(|o| String::from_utf8(o.stdout).ok());
Ok(CargoCommandVersioned::new(
cargo_command,
version.unwrap_or_else(|| "unknown rustc version".into()),
))
} else {
match String::from_utf8(s.stderr) {
Ok(ref err) if err.contains("linker `rust-lld` not found") => {
@@ -105,5 +158,4 @@ fn check_wasm_toolchain_installed() -> Option<String> {
}
}
)
.err()
}
@@ -15,7 +15,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::write_file_if_changed;
use crate::{write_file_if_changed, CargoCommandVersioned};
use std::{
fs, path::{Path, PathBuf}, borrow::ToOwned, process, env, collections::HashSet,
@@ -34,6 +34,17 @@ use fs2::FileExt;
use itertools::Itertools;
/// Colorize an info message.
///
/// Returns the colorized message.
fn colorize_info_message(message: &str) -> String {
if super::color_output_enabled() {
ansi_term::Color::Yellow.bold().paint(message).to_string()
} else {
message.into()
}
}
/// Holds the path to the bloaty WASM binary.
pub struct WasmBinaryBloaty(PathBuf);
@@ -110,9 +121,10 @@ fn crate_metadata(cargo_manifest: &Path) -> Metadata {
///
/// # Returns
/// The path to the compact WASM binary and the bloaty WASM binary.
pub fn create_and_compile(
pub(crate) fn create_and_compile(
cargo_manifest: &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");
@@ -125,7 +137,7 @@ pub fn create_and_compile(
let project = create_project(cargo_manifest, &wasm_workspace, &crate_metadata);
create_wasm_workspace_project(&wasm_workspace, &crate_metadata.workspace_root);
build_project(&project, default_rustflags);
build_project(&project, default_rustflags, cargo_cmd);
let (wasm_binary, bloaty) = compact_wasm_file(
&project,
cargo_manifest,
@@ -423,7 +435,7 @@ fn create_project(cargo_manifest: &Path, wasm_workspace: &Path, crate_metadata:
write_file_if_changed(
project_folder.join("src/lib.rs"),
"#![no_std] pub use wasm_project::*;".into(),
"#![no_std] pub use wasm_project::*;",
);
if let Some(crate_lock_file) = find_cargo_lock(cargo_manifest) {
@@ -452,9 +464,9 @@ fn is_release_build() -> bool {
}
/// Build the project to create the WASM binary.
fn build_project(project: &Path, default_rustflags: &str) {
fn build_project(project: &Path, default_rustflags: &str, cargo_cmd: CargoCommandVersioned) {
let manifest_path = project.join("Cargo.toml");
let mut build_cmd = crate::get_nightly_cargo().command();
let mut build_cmd = cargo_cmd.command();
let rustflags = format!(
"-C link-arg=--export-table {} {}",
@@ -476,7 +488,9 @@ fn build_project(project: &Path, default_rustflags: &str) {
build_cmd.arg("--release");
};
println!("Executing build command: {:?}", build_cmd);
println!("{}", colorize_info_message("Information that should be included in a bug report."));
println!("{} {:?}", colorize_info_message("Executing build command:"), build_cmd);
println!("{} {}", colorize_info_message("Using rustc version:"), cargo_cmd.rustc_version());
match build_cmd.status().map(|s| s.success()) {
Ok(true) => {},