|
import streamlit as st |
|
import os |
|
import subprocess |
|
import sys |
|
import shutil |
|
import uuid |
|
import platform |
|
import time |
|
from pathlib import Path |
|
import tempfile |
|
|
|
|
|
st.set_page_config( |
|
page_title="Nuitka Python Compiler", |
|
page_icon="🚀", |
|
layout="wide" |
|
) |
|
|
|
def ensure_dir(dir_path): |
|
"""Ensure directory exists""" |
|
Path(dir_path).mkdir(parents=True, exist_ok=True) |
|
|
|
def fix_line_endings(file_path): |
|
"""Fix line endings to ensure Linux compatibility""" |
|
try: |
|
subprocess.run(["dos2unix", file_path], check=True, capture_output=True) |
|
return True |
|
except Exception as e: |
|
st.warning(f"Could not fix line endings: {str(e)}") |
|
return False |
|
|
|
def run_compiled_binary(binary_path): |
|
"""Run the compiled binary and return the output""" |
|
try: |
|
|
|
if binary_path.endswith(".exe"): |
|
return False, "Windows executables (.exe) cannot be run in this Linux environment." |
|
|
|
|
|
os.chmod(binary_path, 0o755) |
|
|
|
|
|
if binary_path.endswith(".sh"): |
|
fix_line_endings(binary_path) |
|
|
|
|
|
process = subprocess.Popen( |
|
[binary_path], |
|
stdout=subprocess.PIPE, |
|
stderr=subprocess.PIPE, |
|
text=True, |
|
bufsize=1, |
|
universal_newlines=True |
|
) |
|
|
|
|
|
output_placeholder = st.empty() |
|
output_text = "" |
|
|
|
|
|
start_time = time.time() |
|
while process.poll() is None: |
|
|
|
if time.time() - start_time > 10: |
|
process.terminate() |
|
return False, "Execution timed out after 10 seconds." |
|
|
|
|
|
stdout_line = process.stdout.readline() |
|
if stdout_line: |
|
output_text += f"[STDOUT] {stdout_line}" |
|
output_placeholder.text(output_text) |
|
|
|
|
|
stderr_line = process.stderr.readline() |
|
if stderr_line: |
|
output_text += f"[STDERR] {stderr_line}" |
|
output_placeholder.text(output_text) |
|
|
|
|
|
time.sleep(0.1) |
|
|
|
|
|
stdout, stderr = process.communicate() |
|
if stdout: |
|
output_text += f"[STDOUT] {stdout}" |
|
if stderr: |
|
output_text += f"[STDERR] {stderr}" |
|
|
|
output_placeholder.text(output_text) |
|
|
|
return True, output_text |
|
except Exception as e: |
|
return False, f"Error running the binary: {str(e)}" |
|
|
|
def install_system_packages(packages_content, status_container): |
|
"""Install system packages from packages.txt content""" |
|
if not packages_content.strip(): |
|
return "No system packages specified." |
|
|
|
|
|
fd, temp_path = tempfile.mkstemp(suffix='.txt') |
|
try: |
|
with os.fdopen(fd, 'w') as tmp: |
|
tmp.write(packages_content) |
|
|
|
status_container.info("Installing system packages...") |
|
|
|
|
|
process = subprocess.run( |
|
["/app/install_packages.sh", temp_path], |
|
capture_output=True, |
|
text=True |
|
) |
|
|
|
if process.returncode == 0: |
|
status_container.success("System packages installed successfully.") |
|
return f"System packages installation:\n{process.stdout}\n{process.stderr}" |
|
else: |
|
status_container.error("Failed to install system packages.") |
|
return f"System packages installation failed:\n{process.stdout}\n{process.stderr}" |
|
|
|
finally: |
|
|
|
if os.path.exists(temp_path): |
|
os.unlink(temp_path) |
|
|
|
def compile_for_windows(code, output_dir, status_container): |
|
"""Compile Python code for Windows using Wine and MinGW""" |
|
|
|
fd, script_path = tempfile.mkstemp(suffix='.py') |
|
try: |
|
with os.fdopen(fd, 'w') as tmp: |
|
tmp.write(code) |
|
|
|
status_container.info("Starting Windows compilation...") |
|
|
|
|
|
output_name = "program.exe" |
|
process = subprocess.Popen( |
|
["/app/compile_windows.sh", script_path, output_dir, output_name], |
|
stdout=subprocess.PIPE, |
|
stderr=subprocess.STDOUT, |
|
text=True, |
|
bufsize=1, |
|
universal_newlines=True |
|
) |
|
|
|
|
|
compile_output = "" |
|
for line in iter(process.stdout.readline, ''): |
|
compile_output += line |
|
status_container.text(f"Windows compilation progress:\n{compile_output}") |
|
|
|
process.wait() |
|
|
|
if process.returncode == 0: |
|
status_container.success("Windows compilation successful!") |
|
return True, compile_output, os.path.join(output_dir, output_name) |
|
else: |
|
status_container.error("Windows compilation failed.") |
|
return False, compile_output, None |
|
|
|
except Exception as e: |
|
status_container.error(f"Windows compilation error: {str(e)}") |
|
return False, str(e), None |
|
|
|
finally: |
|
|
|
if os.path.exists(script_path): |
|
os.unlink(script_path) |
|
|
|
def compile_with_nuitka(code, requirements, packages, target_platform, output_extension=".bin"): |
|
"""Compile Python code with Nuitka without using threads""" |
|
|
|
status = st.empty() |
|
status_container = st.container() |
|
status_container.info("Starting compilation process...") |
|
|
|
|
|
job_id = str(uuid.uuid4()) |
|
base_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "user_code") |
|
job_dir = os.path.join(base_dir, job_id) |
|
output_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "compiled_output", job_id) |
|
|
|
|
|
ensure_dir(job_dir) |
|
ensure_dir(output_dir) |
|
|
|
|
|
system_info = f""" |
|
System Information: |
|
- Python Version: {sys.version} |
|
- Platform: {platform.platform()} |
|
- Architecture: {platform.architecture()} |
|
- Machine: {platform.machine()} |
|
- Target Platform: {target_platform} |
|
""" |
|
status_container.info(system_info) |
|
|
|
|
|
if packages.strip(): |
|
packages_result = install_system_packages(packages, status_container) |
|
status_container.text(packages_result) |
|
|
|
|
|
script_path = os.path.join(job_dir, "user_script.py") |
|
with open(script_path, "w") as f: |
|
f.write(code) |
|
|
|
|
|
install_result = "No Python requirements specified." |
|
if requirements.strip(): |
|
req_path = os.path.join(job_dir, "requirements.txt") |
|
with open(req_path, "w") as f: |
|
f.write(requirements) |
|
|
|
|
|
if target_platform == "linux": |
|
try: |
|
status_container.info("Installing Python requirements...") |
|
install_process = subprocess.Popen( |
|
[sys.executable, "-m", "pip", "install", "--no-cache-dir", "-r", req_path], |
|
stdout=subprocess.PIPE, |
|
stderr=subprocess.STDOUT, |
|
text=True, |
|
bufsize=1, |
|
universal_newlines=True |
|
) |
|
|
|
|
|
install_output = "" |
|
for line in iter(install_process.stdout.readline, ''): |
|
install_output += line |
|
status_container.text(f"Python requirements installation:\n{install_output}") |
|
|
|
install_process.wait() |
|
|
|
if install_process.returncode == 0: |
|
status_container.success("Python requirements installed successfully.") |
|
else: |
|
status_container.warning("Python requirements installation completed with warnings.") |
|
|
|
install_result = f"Python requirements installation completed with return code: {install_process.returncode}" |
|
status_container.info(f"{install_result}\n\nStarting compilation...") |
|
except Exception as e: |
|
install_result = f"Error installing Python requirements: {str(e)}" |
|
status_container.error(install_result) |
|
return install_result, "", None, None |
|
|
|
|
|
if target_platform == "windows": |
|
success, win_output, win_binary = compile_for_windows(code, output_dir, status_container) |
|
if success and win_binary: |
|
|
|
file_process = subprocess.run( |
|
["file", win_binary], |
|
capture_output=True, |
|
text=True |
|
) |
|
binary_info = file_process.stdout |
|
|
|
return install_result, win_output, win_binary, binary_info |
|
else: |
|
return install_result, win_output, None, "Windows binary compilation failed." |
|
|
|
|
|
try: |
|
|
|
output_filename = f"user_script{output_extension}" |
|
|
|
|
|
compile_cmd = [ |
|
sys.executable, "-m", "nuitka", |
|
"--onefile", |
|
"--show-progress", |
|
"--show-modules", |
|
script_path, |
|
f"--output-filename={output_filename}", |
|
f"--output-dir={output_dir}" |
|
] |
|
|
|
status_container.info(f"Starting compilation with command:\n{' '.join(compile_cmd)}") |
|
|
|
|
|
process = subprocess.Popen( |
|
compile_cmd, |
|
stdout=subprocess.PIPE, |
|
stderr=subprocess.STDOUT, |
|
text=True, |
|
bufsize=1, |
|
universal_newlines=True |
|
) |
|
|
|
|
|
progress_bar = st.progress(0) |
|
|
|
|
|
compile_output = "" |
|
output_buffer = [] |
|
line_count = 0 |
|
total_lines_estimate = 500 |
|
|
|
|
|
for line in iter(process.stdout.readline, ''): |
|
compile_output += line |
|
output_buffer.append(line) |
|
line_count += 1 |
|
|
|
|
|
if len(output_buffer) > 20: |
|
output_buffer.pop(0) |
|
|
|
|
|
progress = min(line_count / total_lines_estimate, 0.99) |
|
progress_bar.progress(progress) |
|
|
|
|
|
status_container.text(f"Compilation in progress...\n\n{''.join(output_buffer)}") |
|
|
|
|
|
progress_bar.progress(1.0) |
|
|
|
|
|
process.wait() |
|
|
|
status_container.info(f"Compilation finished with exit code: {process.returncode}") |
|
|
|
|
|
binary_path = os.path.join(output_dir, output_filename) |
|
|
|
|
|
binary_info = "Binary file not found." |
|
if os.path.exists(binary_path): |
|
file_process = subprocess.run( |
|
["file", binary_path], |
|
capture_output=True, |
|
text=True |
|
) |
|
binary_info = file_process.stdout |
|
status_container.info(f"Binary information: {binary_info}") |
|
else: |
|
status_container.error(binary_info) |
|
|
|
|
|
if os.path.exists(binary_path): |
|
|
|
os.chmod(binary_path, 0o755) |
|
|
|
|
|
if binary_path.endswith(".sh"): |
|
fix_line_endings(binary_path) |
|
|
|
status_container.success("Compilation complete! You can download the executable now.") |
|
|
|
|
|
return install_result, compile_output, binary_path, binary_info |
|
else: |
|
status_container.error("Compilation failed. Could not find executable file.") |
|
return install_result, f"{compile_output}\n\nCompilation failed. See output for details.", None, binary_info |
|
except Exception as e: |
|
status_container.error(f"Error during compilation: {str(e)}") |
|
return install_result, f"Error during compilation: {str(e)}", None, "Binary compilation failed." |
|
|
|
|
|
st.title("🚀 Nuitka Python Compiler") |
|
st.markdown(""" |
|
This tool compiles your Python code into a single executable file using Nuitka. |
|
You can compile for Linux or Windows platforms. |
|
""") |
|
|
|
|
|
tab1, tab2, tab3 = st.tabs(["Compiler", "How to Use", "About"]) |
|
|
|
with tab1: |
|
|
|
col1, col2 = st.columns([2, 1]) |
|
|
|
with col1: |
|
code = st.text_area( |
|
"Your Python Code", |
|
value="# Enter your Python code here\n\nprint('Hello from compiled Python!')\nprint('This is running as a native executable')\n\n# This code will be compiled into an executable", |
|
height=300 |
|
) |
|
|
|
with col2: |
|
|
|
req_tab1, req_tab2 = st.tabs(["Python Requirements", "System Packages"]) |
|
|
|
with req_tab1: |
|
requirements = st.text_area( |
|
"requirements.txt", |
|
placeholder="numpy==1.24.0\npandas==2.0.0\n# Add your Python dependencies here", |
|
height=230 |
|
) |
|
|
|
with req_tab2: |
|
packages = st.text_area( |
|
"packages.txt", |
|
placeholder="# Add system packages that require apt-get install\nlibssl-dev\nlibffi-dev", |
|
height=230, |
|
help="These packages will be installed using sudo apt-get install" |
|
) |
|
|
|
|
|
target_platform = st.radio( |
|
"Target Platform", |
|
options=["linux", "windows"], |
|
index=0, |
|
help="Select the platform for which to compile the executable." |
|
) |
|
|
|
|
|
if target_platform == "linux": |
|
output_extension = st.selectbox( |
|
"Output File Extension", |
|
options=[".bin", ".sh"], |
|
index=0, |
|
help="Choose the file extension for the compiled Linux executable." |
|
) |
|
else: |
|
output_extension = ".exe" |
|
|
|
|
|
if st.button("Compile with Nuitka", type="primary"): |
|
install_result, compile_output, binary_path, binary_info = compile_with_nuitka(code, requirements, packages, target_platform, output_extension) |
|
|
|
|
|
with st.expander("Compilation Details", expanded=True): |
|
st.subheader("Requirements Installation") |
|
st.text(install_result) |
|
|
|
st.subheader("Compilation Log") |
|
st.text(compile_output) |
|
|
|
if binary_info: |
|
st.subheader("Binary Information") |
|
st.text(binary_info) |
|
|
|
|
|
if binary_path and os.path.exists(binary_path): |
|
st.success("✅ Compilation successful!") |
|
|
|
|
|
if target_platform == "linux": |
|
download_filename = f"compiled_program{output_extension}" |
|
else: |
|
download_filename = "compiled_program.exe" |
|
|
|
|
|
with open(binary_path, "rb") as f: |
|
st.download_button( |
|
label=f"Download Compiled Program ({os.path.basename(binary_path)})", |
|
data=f, |
|
file_name=download_filename, |
|
mime="application/octet-stream" |
|
) |
|
|
|
|
|
if target_platform == "linux": |
|
st.info(f""" |
|
**To run on Linux:** |
|
1. Save the file to your computer |
|
2. Open a terminal in the directory where you saved it |
|
3. Make it executable: `chmod +x {download_filename}` |
|
4. Run it: `./{download_filename}` |
|
|
|
**To run on Windows with WSL:** |
|
1. Install WSL (Windows Subsystem for Linux) |
|
2. Copy the file to your WSL environment (not the Windows file system) |
|
3. Make it executable: `chmod +x {download_filename}` |
|
4. Run it: `./{download_filename}` |
|
""") |
|
else: |
|
st.info(f""" |
|
**To run on Windows:** |
|
1. Save the .exe file to your computer |
|
2. Double-click the file to run it |
|
|
|
**Note:** This is a Windows executable and will only run on Windows systems. |
|
""") |
|
|
|
|
|
if target_platform == "linux": |
|
st.subheader("Test Run") |
|
if st.button("Run Compiled Binary"): |
|
st.write("Running the compiled binary... (output will appear below)") |
|
with st.spinner("Executing..."): |
|
success, result = run_compiled_binary(binary_path) |
|
|
|
if success: |
|
st.success("Binary executed successfully!") |
|
else: |
|
st.warning("Binary execution encountered issues.") |
|
|
|
st.text_area("Execution Output", result, height=300) |
|
else: |
|
st.warning("Windows executables cannot be test-run in this environment. Please download and run on a Windows system.") |
|
else: |
|
st.error("❌ Compilation failed or file creation failed.") |
|
|
|
with tab2: |
|
st.header("How to Use the Compiled Binary") |
|
|
|
st.subheader("Linux Executables (.bin/.sh)") |
|
st.markdown(""" |
|
1. Download the compiled executable (.bin or .sh) |
|
2. Open a terminal in the directory where you saved it |
|
3. Make the file executable: `chmod +x your_program.bin` |
|
4. Run the program: `./your_program.bin` |
|
""") |
|
|
|
st.subheader("Windows Executables (.exe)") |
|
st.markdown(""" |
|
1. Download the compiled .exe file |
|
2. Double-click to run it on any Windows system |
|
|
|
**Note:** The Windows executable is cross-compiled using Wine and MinGW. While this should work for most simple Python scripts, complex applications might have issues. |
|
""") |
|
|
|
st.subheader("WSL Instructions for Linux Executables") |
|
st.markdown(""" |
|
If you're trying to run Linux executables on Windows: |
|
|
|
1. Install WSL from Microsoft Store |
|
2. Open WSL |
|
3. Copy the file to a native WSL directory (not inside /mnt/c/) |
|
4. Make it executable: `chmod +x your_program.bin` |
|
5. Run it: `./your_program.bin` |
|
|
|
If you get "cannot execute binary file" error: |
|
- Make sure you're running it with `./your_program.bin` |
|
- Try fixing line endings: `dos2unix your_program.bin` |
|
- Make sure you're running it from a Linux environment, not Windows cmd/PowerShell |
|
""") |
|
|
|
st.subheader("About Onefile Mode") |
|
st.markdown(""" |
|
This tool uses Nuitka's "onefile" mode which: |
|
|
|
1. Creates a single standalone executable |
|
2. Contains all dependencies in one file |
|
3. Unpacks necessary files to a temporary directory at runtime |
|
4. Cleans up after execution |
|
|
|
This makes distribution simple - just share this one file! |
|
""") |
|
|
|
with tab3: |
|
st.header("About Nuitka") |
|
st.markdown(""" |
|
Nuitka is a Python compiler that converts Python code to C/C++ code, which is then compiled to a native executable. |
|
|
|
**Benefits of using Nuitka:** |
|
- Improved performance |
|
- Protection of source code |
|
- Standalone applications (no Python required on the target machine) |
|
- Reduced startup time |
|
|
|
**Technical Details:** |
|
- This tool can compile for both Linux and Windows platforms |
|
- Linux binaries are 64-bit ELF format |
|
- Windows binaries are 64-bit PE format (.exe) |
|
- Windows compilation uses cross-compilation via Wine and MinGW |
|
""") |
|
|
|
st.header("About This Tool") |
|
st.markdown(""" |
|
This tool provides a simple web interface for compiling Python code with Nuitka in onefile mode. |
|
|
|
**Features:** |
|
- Compiles to single executable files (.bin, .sh, or .exe) |
|
- Cross-platform compilation (Linux and Windows) |
|
- Includes all dependencies |
|
- Supports system packages via packages.txt |
|
- Real-time progress tracking |
|
|
|
**Limitations:** |
|
- Windows executables are cross-compiled and may not work with complex dependencies |
|
- Very large projects might time out in the Hugging Face Space environment |
|
- Some advanced Nuitka features may not be supported |
|
""") |
|
|
|
|
|
st.markdown("---") |
|
st.caption("Created by Claude 3.7 Sonnet| Nuitka is a Python compiler that converts your Python code to native executables") |