diff --git a/.vscode/launch.json b/.vscode/launch.json index 2094bef9..2f1a58d0 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,7 +7,7 @@ { "type": "lldb", "request": "launch", - "name": "Debug CLI", + "name": "CLI", "preLaunchTask": "BuildCompiler", "cargo": { "args": [ @@ -32,7 +32,7 @@ { "type": "lldb", "request": "launch", - "name": "Debug playground", + "name": "Playground", "preLaunchTask": "BuildCompiler", "cargo": { "args": [ diff --git a/core/wasm-codegen/build.rs b/core/wasm-codegen/build.rs index 9cbbe65b..b9a036d3 100644 --- a/core/wasm-codegen/build.rs +++ b/core/wasm-codegen/build.rs @@ -1,6 +1,7 @@ use std::env; use std::fs; use std::path::PathBuf; +use std::process::Command; fn main() { let platform = if cfg!(target_os = "linux") { @@ -69,6 +70,9 @@ fn main() { .permissions(); perms.set_mode(0o755); fs::set_permissions(&dest_llc, perms).expect("Failed to set executable permissions"); + + // Set RPATH to look in the same directory as the binary + set_rpath(&dest_llc); } println!("cargo:info=Copied inf-llc to {}", dest_llc.display()); @@ -98,6 +102,12 @@ fn main() { perms.set_mode(0o755); fs::set_permissions(&dest_rust_lld, perms) .expect("Failed to set executable permissions"); + + // Copy LLVM libraries to make rust-lld self-contained + copy_llvm_libraries(bin_dir); + + // Set RPATH to look in the same directory as the binary + set_rpath(&dest_rust_lld); } println!("cargo:info=Copied rust-lld to {}", dest_rust_lld.display()); @@ -142,3 +152,47 @@ fn main() { println!("cargo:rerun-if-changed={}", source_rust_lld.display()); println!("cargo:rerun-if-changed={}", source_lib_llvm.display()); } + +fn copy_llvm_libraries(bin_dir: PathBuf) { + // Get the Rust toolchain's lib directory + if let Ok(output) = Command::new("rustc").arg("--print").arg("sysroot").output() + && output.status.success() + { + let sysroot = String::from_utf8_lossy(&output.stdout).trim().to_string(); + let lib_dir = PathBuf::from(sysroot).join("lib"); + + if lib_dir.exists() { + // Copy LLVM shared libraries + if let Ok(entries) = fs::read_dir(&lib_dir) { + for entry in entries.flatten() { + let path = entry.path(); + if let Some(file_name) = path.file_name() { + let name_str = file_name.to_string_lossy(); + // Copy LLVM libraries (both .so and .so.version files) + if name_str.contains("libLLVM") && name_str.contains(".so") { + let dest_path = bin_dir.join(file_name); + if let Err(e) = fs::copy(&path, &dest_path) { + eprintln!("Warning: Failed to copy {}: {}", path.display(), e); + } else { + println!( + "cargo:info=Copied LLVM library {} to {}", + path.display(), + dest_path.display() + ); + } + } + } + } + } + } + } +} + +fn set_rpath(binary_path: &PathBuf) { + // Set RPATH to $ORIGIN so the binary looks for libraries in its own directory + let _ = Command::new("patchelf") + .arg("--set-rpath") + .arg("$ORIGIN") + .arg(binary_path) + .output(); +} diff --git a/core/wasm-codegen/src/utils.rs b/core/wasm-codegen/src/utils.rs index 982ad982..cea13dba 100644 --- a/core/wasm-codegen/src/utils.rs +++ b/core/wasm-codegen/src/utils.rs @@ -40,9 +40,22 @@ pub(crate) fn compile_to_wasm( } let rust_lld_path = get_rust_lld_path()?; let wasm_path = temp_dir.path().join(output_fname).with_extension("wasm"); - let mut lld_cmd = Command::new(&rust_lld_path); - configure_llvm_env(&mut lld_cmd)?; - let wasm_lld_output = lld_cmd + + // Make rust-lld self-contained by pointing the dynamic linker to the directory + // where rust-lld and the copied LLVM libraries live (see build.rs). + let mut wasm_ld_cmd = Command::new(&wasm_ld_path); + if let Some(bin_dir) = wasm_ld_path.parent() { + if let Ok(existing_ld_path) = std::env::var("LD_LIBRARY_PATH") { + wasm_ld_cmd.env( + "LD_LIBRARY_PATH", + format!("{}:{existing_ld_path}", bin_dir.display()), + ); + } else { + wasm_ld_cmd.env("LD_LIBRARY_PATH", bin_dir.as_os_str()); + } + } + + let wasm_ld_output = wasm_ld_cmd .arg("-flavor") .arg("wasm") .arg(&obj_path) diff --git a/tools/playground-server/Cargo.toml b/tools/playground-server/Cargo.toml index c2dbe4b7..6833cbb2 100644 --- a/tools/playground-server/Cargo.toml +++ b/tools/playground-server/Cargo.toml @@ -9,8 +9,9 @@ repository = { workspace = true } [dependencies] actix-web = "4" actix-cors = "0.7.0" -serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" inference.workspace = true +inf-wast.workspace = true wat-fmt.workspace = true wasm-fmt.workspace = true +serde.workspace = true diff --git a/tools/playground-server/src/main.rs b/tools/playground-server/src/main.rs index 72ba8ab7..80ec719d 100644 --- a/tools/playground-server/src/main.rs +++ b/tools/playground-server/src/main.rs @@ -1,7 +1,9 @@ use actix_cors::Cors; -use actix_web::{post, web, App, HttpResponse, HttpServer, Responder}; +use actix_web::{App, HttpResponse, HttpServer, Responder, post, web}; use serde::{Deserialize, Serialize}; +use inference::{codegen, parse, wasm_to_v}; + #[derive(Deserialize)] struct CompileRequest { code: String, @@ -9,55 +11,62 @@ struct CompileRequest { #[derive(Deserialize, Serialize)] struct Response { - wat: String, + ll: String, wasm: Vec, wasm_str: String, v: String, errors: Vec, } -fn parse_inf_file(_: &str) -> Response { - // let mut wasm = vec![]; - let v = String::new(); +fn parse_inf_file(inf_code: &str) -> Response { let errors = vec![]; - // let wat = match compile_to_wat(input) { - // Ok(w) => w, - // Err(e) => { - // errors.push(e.to_string()); - // return Response { - // wat: String::new(), - // wasm: vec![], - // wasm_str: String::new(), - // v: String::new(), - // errors, - // }; - // } - // }; + let parse_result = match parse(inf_code) { + Ok(result) => result, + Err(e) => { + return Response { + ll: String::new(), + wasm: vec![], + wasm_str: String::new(), + v: String::new(), + errors: vec![e.to_string()], + }; + } + }; - // if !wat.is_empty() { - // wat_to_wasm(&wat) - // .map(|w| wasm = w) - // .unwrap_or_else(|e| errors.push(e.to_string())); + let wasm_bytes = match codegen(&parse_result) { + Ok(bytes) => bytes, + Err(e) => { + return Response { + ll: String::new(), + wasm: vec![], + wasm_str: String::new(), + v: String::new(), + errors: vec![e.to_string()], + }; + } + }; - // wasm_to_v("playground", &wasm) - // .map(|v_str| v = v_str) - // .unwrap_or_else(|e| errors.push(e.to_string())); + let v = match wasm_to_v("playground", &wasm_bytes) { + Ok(v_str) => v_str, + Err(e) => { + return Response { + ll: String::new(), + wasm: vec![], + wasm_str: String::new(), + v: String::new(), + errors: vec![e.to_string()], + }; + } + }; - // let wat = wat_format(&wat); - // let wasm_str = wasm_format(&wasm); - // Response { - // wat, - // wasm, - // wasm_str, - // v, - // errors, - // } - // } else { Response { - wat: String::new(), - wasm: vec![], - wasm_str: String::new(), + ll: String::new(), + wasm: wasm_bytes.clone(), + wasm_str: wasm_bytes + .iter() + .map(|b| format!("{:02x}", b)) + .collect::(), v, errors, } @@ -78,13 +87,14 @@ async fn main() -> std::io::Result<()> { .wrap( Cors::default() .allowed_origin("http://localhost:3000") + .allowed_origin("http://localhost:3001") .allowed_methods(vec!["POST", "GET"]) .allowed_headers(vec!["Content-Type"]) .supports_credentials(), ) .service(compile_code) }) - .bind(("127.0.0.1", 8080))? + .bind(("127.0.0.1", 8181))? .run() .await }