Skip to content

Commit

Permalink
feat: print detailed error if any in user console
Browse files Browse the repository at this point in the history
Signed-off-by: Tarek <tareknaser360@gmail.com>
  • Loading branch information
tareknaser committed Sep 8, 2024
1 parent 6a86e46 commit 9b632b7
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 23 deletions.
1 change: 1 addition & 0 deletions crates/backend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ tokio = { version = "1.28.2", features = [
] }
anyhow = "1.0"
log = "0.4"
regex = "1.10"

typescript-type-def = "0.5"
actix-web = "4.5"
Expand Down
12 changes: 12 additions & 0 deletions crates/backend/src/services/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,24 @@ pub enum CompilationResult {
Success {
/// The compiled contract
wasm: Vec<u8>,
/// The standard output of the docker command
stdout: String,
/// The standard error of the docker command
stderr: String,
/// The standard output of the compile command
compile_stdout: String,
/// The standard error of the compile command
compile_stderr: String,
},
Error {
/// The standard output of the docker command
stdout: String,
/// The standard error of the docker command
stderr: String,
/// The standard output of the compile command
compile_stdout: String,
/// The standard error of the compile command
compile_stderr: String,
},
}

Expand Down
83 changes: 75 additions & 8 deletions crates/backend/src/services/sandbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ pub fn build_compile_command(input_file: &Path, output_dir: &Path) -> Command {
// Building the compile command
let remove_command = format!("rm -rf {}*.wasm {}*.contract", DOCKER_OUTPUT, DOCKER_OUTPUT);
let compile_command = format!(
"solang compile --target polkadot -o /playground-result {} 2>&1",
"solang compile --target polkadot -o /playground-result {} > /playground-result/stdout.log 2> /playground-result/stderr.log",
file_name
);
let sh_command = format!("{} && {}", remove_command, compile_command);
Expand All @@ -96,7 +96,7 @@ impl Sandbox {
/// Creates a new sandbox
pub fn new() -> Result<Self> {
let scratch = TempDir::with_prefix("solang_playground").context("failed to create scratch directory")?;
let input_file = scratch.path().join("input.rs");
let input_file = scratch.path().join("input.sol");
let output_dir = scratch.path().join("output");
fs::create_dir(&output_dir).context("failed to create output directory")?;

Expand Down Expand Up @@ -124,16 +124,62 @@ impl Sandbox {
.map(|entry| entry.path())
.find(|path| path.extension() == Some(OsStr::new("contract")));

// The file `stdout.log` is in the same directory as the contract file
let compile_log_stdout_file_path = fs::read_dir(&self.output_dir)
.context("failed to read output directory")?
.flatten()
.find(|entry| entry.file_name() == "stdout.log")
.map(|entry| entry.path());

// The file `stderr.log` is in the same directory as the contract file
let compile_log_stderr_file_path = fs::read_dir(&self.output_dir)
.context("failed to read output directory")?
.flatten()
.find(|entry| entry.file_name() == "stderr.log")
.map(|entry| entry.path());

let compile_stdout = match compile_log_stdout_file_path {
Some(path) => fs::read_to_string(&path).context("failed to read compile stdout")?,
None => "No stdout.log file found".to_string(),
};

let compile_stderr = match compile_log_stderr_file_path {
Some(path) => fs::read_to_string(&path).context("failed to read compile stderr")?,
None => "No stderr.log file found".to_string(),
};
let compile_stderr = extract_error_message(&compile_stderr);

let stdout = String::from_utf8(output.stdout).context("failed to convert vec to string")?;
let stderr = String::from_utf8(output.stderr).context("failed to convert vec to string")?;

let compile_response = match file {
Some(file) => match read(&file) {
Ok(Some(wasm)) => CompilationResult::Success { wasm, stderr, stdout },
Ok(None) => CompilationResult::Error { stderr, stdout },
Err(_) => CompilationResult::Error { stderr, stdout },
Ok(Some(wasm)) => CompilationResult::Success {
wasm,
stderr,
stdout,
compile_stdout,
compile_stderr,
},
Ok(None) => CompilationResult::Error {
stderr,
stdout,
compile_stdout,
compile_stderr,
},
Err(_) => CompilationResult::Error {
stderr,
stdout,
compile_stdout,
compile_stderr,
},
},
None => CompilationResult::Error {
stderr,
stdout,
compile_stdout,
compile_stderr,
},
None => CompilationResult::Error { stderr, stdout },
};

Ok(compile_response)
Expand Down Expand Up @@ -175,9 +221,8 @@ async fn run_command(mut command: Command) -> Result<std::process::Output> {
let timeout = TIMEOUT;
println!("executing command!");
let output = command.output().await.context("failed to start compiler")?;
println!("Done! {:?}", output);
let stdout = String::from_utf8_lossy(&output.stdout);

let stdout = String::from_utf8_lossy(&output.stdout);
let id = stdout.lines().next().context("missing compiler ID")?.trim();
let stderr = &output.stderr;

Expand Down Expand Up @@ -210,3 +255,25 @@ async fn run_command(mut command: Command) -> Result<std::process::Output> {

Ok(output)
}

pub fn extract_error_message(log: &str) -> String {
// Remove ANSI escape codes (used for terminal colors)
let cleaned_log = remove_ansi_escape_codes(log);

// Find the start of the actual error message by looking for the keyword "error:"
if let Some(start) = cleaned_log.find("error:") {
// Extract the error message starting from the keyword "error:" but ignore the keyword itself
let error_message = &cleaned_log[start + "error:".len()..];
error_message.to_string()
} else {
// If no error message is found, return a default message
"No error message found".to_string()
}
}

/// Helper function to remove ANSI escape codes from a string
fn remove_ansi_escape_codes(log: &str) -> String {
// Use a regex pattern to remove ANSI escape codes
let re = regex::Regex::new(r"\x1B\[[0-9;]*[a-zA-Z]").unwrap();
re.replace_all(log, "").to_string()
}
8 changes: 1 addition & 7 deletions crates/browser/tests/server_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,17 @@ use solang::languageserver::{Files, GlobalCache, SolangServer};
use std::collections::HashMap;
use std::str::FromStr;
use tokio::sync::Mutex;
use tower_lsp::lsp_types::{
Diagnostic, DiagnosticSeverity,Position, Range,
};
use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity, Position, Range};
use tower_lsp::LspService;
use tower_test::mock::Spawn;


use solang::Target;

#[cfg(test)]
mod tests {

use super::*;



const INITIALIZE_REQUEST: &'static str = r#"{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"processId":null,"clientInfo":{"name":"demo-language-client"},"capabilities":{},"rootUri":null}}"#;
const INITIALIZE_EXPECTED_RESPONSE: &'static str = r#"{"jsonrpc":"2.0","result":{"capabilities":{"completionProvider":{"resolveProvider":false,"triggerCharacters":["."]},"declarationProvider":true,"definitionProvider":true,"documentFormattingProvider":true,"executeCommandProvider":{"commands":[]},"hoverProvider":true,"implementationProvider":true,"referencesProvider":true,"renameProvider":true,"signatureHelpProvider":{},"textDocumentSync":2,"typeDefinitionProvider":true,"workspace":{"workspaceFolders":{"changeNotifications":true,"supported":true}},"workspaceSymbolProvider":true}},"id":1}"#;

Expand All @@ -29,7 +24,6 @@ mod tests {

const CHANGE_DOCUMENT_REQUEST: &str = r#"{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"version":0,"uri":"inmemory://demo.js"},"contentChanges":[{"range":{"start":{"line":0,"character":0},"end":{"line":11,"character":0}},"text":" // SPDX-License-Identifier: MIT\n pragma solidity >=0.6.12 <0.9.0;\n contract HelloWorld {\n uint sesa;\n /**\n * @dev Prints Hello World string\n */\n function print() public pure returns (string memory) {\n return \"Hello World!\";\n }\n }\n"}]}}"#;


fn create_mock_service() -> Spawn<LspService<SolangServer>> {
let importpaths = Vec::new();
let importmaps = Vec::new();
Expand Down
5 changes: 1 addition & 4 deletions crates/solang/src/languageserver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2065,10 +2065,7 @@ impl LanguageServer for SolangServer {

async fn initialized(&self, _: InitializedParams) {
self.client
.log_message(
MessageType::INFO,
format!("Solang language server initialized"),
)
.log_message(MessageType::INFO, format!("Solang language server initialized"))
.await;
}

Expand Down
16 changes: 12 additions & 4 deletions packages/app/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,11 +170,19 @@ export default class App {
{ source: code }
);

console.log("Compilation result: ", result);

// If the compilation was successful, download the wasm blob and print a success message
if (result.type === 'OK' && result.payload.type === 'SUCCESS') {
client.printToConsole(proto.MessageType.Info, "Compilation successful");
const wasm = result.payload.payload.wasm;
downloadBlob(wasm);
if (result.type === 'OK') {
if (result.payload.type === 'SUCCESS') {
client.printToConsole(proto.MessageType.Info, "Compilation successful");
const wasm = result.payload.payload.wasm;
downloadBlob(wasm);
}
else {
let message = result.payload.payload.compile_stderr;
client.printToConsole(proto.MessageType.Error, message);
}
} else {
let message = result.type === 'SERVER_ERROR'
? `Server error: ${result.payload.status}`
Expand Down

0 comments on commit 9b632b7

Please sign in to comment.