mirror of
https://github.com/enso-org/enso.git
synced 2024-12-18 16:01:37 +03:00
164 lines
5.6 KiB
Rust
164 lines
5.6 KiB
Rust
//! This build script is responsible for ensuring that if parser targets wasm,
|
|
//! the JS Parser package is available at the expected location for
|
|
//! `wasm_bindgen` tool.
|
|
|
|
// === Features ===
|
|
#![feature(option_result_contains)]
|
|
|
|
use std::io::prelude::*;
|
|
|
|
use enso_build_utilities::absolute_path;
|
|
use enso_build_utilities::targeting_wasm;
|
|
use enso_build_utilities::PathRef;
|
|
use std::fs;
|
|
use std::fs::create_dir_all;
|
|
use std::fs::File;
|
|
use std::path::PathBuf;
|
|
|
|
|
|
|
|
// =========================
|
|
// == Hardcoded constants ==
|
|
// =========================
|
|
|
|
/// Where the crate expects to find file with compiled parser.
|
|
/// Path relative to the crate directory.
|
|
const PARSER_PATH: &str = "./pkg/scala-parser.js";
|
|
|
|
/// Commit from `enso` repository that will be used to obtain parser from.
|
|
const PARSER_COMMIT: &str = "649fe33ccf148d47deb6ba6a06f3babc48078e3e";
|
|
|
|
/// Magic code that needs to be prepended to ScalaJS generated parser due to:
|
|
/// https://github.com/scala-js/scala-js/issues/3677/
|
|
const PARSER_PREAMBLE: &str = "var __ScalaJSEnv = { global: window };";
|
|
|
|
/// Obtains a URL where this parser version can be downloaded.
|
|
pub fn parser_url(version: &ParserVersion) -> reqwest::Url {
|
|
let url_string = format!(
|
|
"https://packages.luna-lang.org/parser-js/nightly/{}/scala-parser.js",
|
|
version.commit
|
|
);
|
|
let invalid_url_msg = format!("{} is an invalid URL.", url_string);
|
|
reqwest::Url::parse(&url_string).expect(&invalid_url_msg)
|
|
}
|
|
|
|
|
|
|
|
// ===================
|
|
// == ParserVersion ==
|
|
// ===================
|
|
|
|
/// Parser version described as commit hash from `enso` repository.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct ParserVersion {
|
|
pub commit: String,
|
|
}
|
|
|
|
impl ParserVersion {
|
|
/// Create a version described by given commit hash.
|
|
pub fn from_commit(commit: String) -> ParserVersion {
|
|
ParserVersion { commit }
|
|
}
|
|
|
|
/// The JS parser version required for this crate.
|
|
pub fn required() -> ParserVersion {
|
|
ParserVersion { commit: PARSER_COMMIT.into() }
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// ========================
|
|
// == Downloading parser ==
|
|
// ========================
|
|
|
|
/// Stores information which parser version should be provided where.
|
|
///
|
|
/// Implementation provides methods that download desired parser version, patch it and store to the
|
|
/// file, so parser can be consumed by `wasm_bindgen`.
|
|
struct ParserProvider {
|
|
/// Required parser version.
|
|
version: ParserVersion,
|
|
/// The path where JS file needs to be provided.
|
|
parser_path: PathBuf,
|
|
}
|
|
|
|
impl ParserProvider {
|
|
/// Creates a provider that obtains given parser version to a given path.
|
|
pub fn new(version: ParserVersion, parser_path: impl PathRef) -> ParserProvider {
|
|
let parser_path = PathBuf::from(parser_path.as_ref());
|
|
ParserProvider { version, parser_path }
|
|
}
|
|
|
|
/// Downloads contents of JS parser into memory.
|
|
pub async fn download(&self) -> bytes::Bytes {
|
|
let url = parser_url(&self.version);
|
|
let get_error = format!("Failed to get response from {}.", url);
|
|
let download_error = format!("Failed to download contents of {}.", url);
|
|
let server_error = format!("Server replied with error when getting {}.", url);
|
|
let response = reqwest::get(url).await.expect(&get_error);
|
|
let response = response.error_for_status().expect(&server_error);
|
|
response.bytes().await.expect(&download_error)
|
|
}
|
|
|
|
/// Stores JS parser into file, after patching with a `PARSER_PREAMBLE`.
|
|
pub fn patch_and_store(&self, js_parser: bytes::Bytes) {
|
|
let display_path = self.parser_path.display();
|
|
let open_error = format!("Failed to open {}.", display_path);
|
|
let write_error = format!("Failed to write {}.", display_path);
|
|
let flush_error = format!("Failed to flush {}.", display_path);
|
|
|
|
let mut file = File::create(&self.parser_path).expect(&open_error);
|
|
file.write_all(PARSER_PREAMBLE.as_bytes()).expect(&write_error);
|
|
file.write_all(&js_parser).expect(&write_error);
|
|
file.flush().expect(&flush_error);
|
|
}
|
|
|
|
/// Ensures that target's parent directory exists.
|
|
pub fn prepare_target_location(&self) {
|
|
let parent_directory =
|
|
self.parser_path.parent().expect("Unable to access parent directory.");
|
|
let create_dir_error =
|
|
format!("Failed to create directory: {}.", parent_directory.display());
|
|
create_dir_all(parent_directory).expect(&create_dir_error);
|
|
}
|
|
|
|
/// Places required parser version in the target location.
|
|
pub async fn run(&self) {
|
|
self.prepare_target_location();
|
|
let parent_directory =
|
|
self.parser_path.parent().expect("Unable to access parent directory.");
|
|
let fingerprint = parent_directory.join("parser.fingerprint");
|
|
let opt_version = fs::read_to_string(&fingerprint);
|
|
let changed = match opt_version {
|
|
Err(_) => true,
|
|
Ok(hash) => hash != PARSER_COMMIT,
|
|
};
|
|
if changed {
|
|
let parser_js = self.download().await;
|
|
self.patch_and_store(parser_js);
|
|
fs::write(&fingerprint, PARSER_COMMIT).expect("Unable to write parser fingerprint.");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// ==========
|
|
// == main ==
|
|
// ==========
|
|
|
|
#[tokio::main]
|
|
async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
|
|
if targeting_wasm() {
|
|
let required_version = ParserVersion::required();
|
|
let parser_path = absolute_path(PARSER_PATH)?;
|
|
let provider = ParserProvider::new(required_version, &parser_path);
|
|
provider.run().await;
|
|
}
|
|
println!("cargo:rerun-if-changed=build.rs");
|
|
println!("cargo:rerun-if-changed={}", PARSER_PATH);
|
|
|
|
Ok(())
|
|
}
|