import gradio as gr import os import subprocess import shutil import numpy as np from PIL import Image import pywasm SAVE_DIR = "/data" EMSDK = "/data/emsdk" if not os.path.exists(SAVE_DIR): os.makedirs(SAVE_DIR, exist_ok=True) _emcc_cached = None def _find_emcc(): global _emcc_cached if _emcc_cached is not None: return _emcc_cached emcc = shutil.which("emcc") if emcc: _emcc_cached = [emcc] return _emcc_cached emcc_sh = os.path.join(EMSDK, "upstream/emscripten/emcc") if os.path.exists(emcc_sh): _emcc_cached = ["/bin/sh", emcc_sh] return _emcc_cached return None def _install_emsdk(): try: if os.path.exists(EMSDK): shutil.rmtree(EMSDK) subprocess.run(["git", "clone", "https://github.com/emscripten-core/emsdk.git", EMSDK], check=True, capture_output=True, timeout=120) subprocess.run([os.path.join(EMSDK, "emsdk"), "install", "latest"], check=True, capture_output=True, timeout=180) subprocess.run([os.path.join(EMSDK, "emsdk"), "activate", "latest"], check=True, capture_output=True, timeout=60) cfg = f"""import os LLVM_ROOT = '{EMSDK}/upstream/bin' NODE_JS = '{EMSDK}/node/22.16.0_64bit/bin/node' BINARYEN_ROOT = '{EMSDK}/upstream' EMSCRIPTEN_ROOT = '{EMSDK}/upstream/emscripten' CACHE = '/tmp/.emscripten_cache' """ with open(os.path.join(EMSDK, ".emscripten"), "w") as f: f.write(cfg) return True except Exception as e: return str(e) def get_wasm_files(): if not os.path.exists(SAVE_DIR): return [] return [f for f in os.listdir(SAVE_DIR) if f.endswith(".wasm")] def compile_c(c_code, filename): if not c_code or not filename: return "Error: Provide C code and filename." emcc = _find_emcc() if not emcc: result = _install_emsdk() if result is not True: return f"Error: emsdk install failed: {result}" _emcc_cached = None emcc = _find_emcc() if not emcc: return "Error: emcc not found after install." base = filename.split(".")[0] c_file = f"/tmp/{base}.c" wasm_tmp = f"/tmp/{base}.wasm" wasm_out = os.path.join(SAVE_DIR, f"{base}.wasm") with open(c_file, "w") as f: f.write(c_code) env = os.environ.copy() env["EM_CONFIG"] = os.path.join(EMSDK, ".emscripten") env["PATH"] = f"{EMSDK}/upstream/bin:{EMSDK}/upstream/emscripten:{EMSDK}/node/22.16.0_64bit/bin:" + env.get("PATH", "") try: res = subprocess.run( emcc + [c_file, "-O3", "-s", "WASM=1", "-s", "SIDE_MODULE=1", "-o", wasm_tmp], capture_output=True, text=True, timeout=60, env=env, ) if res.returncode != 0: return f"Compiler Error:\n{res.stderr[-1500:]}" if os.path.exists(wasm_tmp): if os.path.exists(wasm_out): os.remove(wasm_out) shutil.move(wasm_tmp, wasm_out) try: rt = pywasm.Runtime() mod = rt.instance_from_file(wasm_out) exports = [e.name for e in mod.exps if not e.name.startswith("__")] return f"Forged: {base}.wasm\n\nExports: {', '.join(exports) if exports else 'none'}" except Exception: return f"Forged: {base}.wasm" return "Error: WASM not generated." except subprocess.TimeoutExpired: return "Error: Compilation timed out." except Exception as e: return f"Error: {e}" def run_wasm(wasm_file, func_name, args_str): if not wasm_file: return "Error: Select a WASM file." if not func_name: return "Error: Provide a function name." path = os.path.join(SAVE_DIR, wasm_file) if not os.path.exists(path): return f"Error: {wasm_file} not found." try: args = [int(x.strip()) for x in args_str.split(",") if x.strip()] if args_str else [] rt = pywasm.Runtime() mod = rt.instance_from_file(path) for name in [func_name, f"_{func_name}"]: try: result = rt.invocate(mod, name, args) return f"Result: {result}" except (AssertionError, Exception): continue exports = [e.name for e in mod.exps if not e.name.startswith("__")] return f"Error: function not found.\nExports: {', '.join(exports) if exports else 'none'}" except Exception as e: return f"Error: {type(e).__name__}: {e}" def render_wasm(wasm_file): if not wasm_file: return None, "Select a file." path = os.path.join(SAVE_DIR, wasm_file) if not os.path.exists(path): return None, f"{wasm_file} not found." try: with open(path, "rb") as f: data = f.read() if len(data) == 0: return None, "Empty file." arr = np.frombuffer(data, dtype=np.uint8).copy() if arr.max() > arr.min(): arr = ((arr - arr.min()) * (255.0 / (arr.max() - arr.min()))).astype(np.uint8) canvas = 1920 * 1080 c = np.tile(arr, (canvas // len(arr)) + 1)[:canvas] if len(arr) > 0 else np.zeros(canvas, dtype=np.uint8) img = Image.fromarray(c.reshape((1080, 1920)), mode="L") return img, f"{len(data)} bytes visualized" except Exception as e: return None, f"Error: {e}" with gr.Blocks() as demo: gr.Markdown("# LCPU: C to WASM Engine") with gr.Tab("Forge"): fn = gr.Textbox(label="Filename", value="logic") code = gr.TextArea(label="C Code", value="int add(int a, int b) {\n return a + b;\n}", lines=8) btn = gr.Button("Build", variant="primary") out = gr.Textbox(label="Status", interactive=False) with gr.Tab("Runner"): sel = gr.Dropdown(label="WASM", choices=get_wasm_files()) rf = gr.Button("Refresh") func = gr.Textbox(label="Function", value="add") args = gr.Textbox(label="Args (comma)", placeholder="10, 20") run = gr.Button("Run", variant="primary") rout = gr.Textbox(label="Result", interactive=False) with gr.Tab("Visualizer"): vsel = gr.Dropdown(label="WASM", choices=get_wasm_files()) vrf = gr.Button("Refresh") viz = gr.Button("Visualize", variant="primary") img = gr.Image(label="Texture") vout = gr.Textbox(label="Status", interactive=False) btn.click(compile_c, [code, fn], out) rf.click(lambda: gr.Dropdown(choices=get_wasm_files()), outputs=sel) run.click(run_wasm, [sel, func, args], rout) vrf.click(lambda: gr.Dropdown(choices=get_wasm_files()), outputs=vsel) viz.click(render_wasm, vsel, [img, vout]) if __name__ == "__main__": demo.launch()