| 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() |
|
|