Update app.py
Browse files
app.py
CHANGED
@@ -18,12 +18,12 @@ def check_dependencies():
|
|
18 |
"""Check if required dependencies are available"""
|
19 |
missing_deps = []
|
20 |
|
21 |
-
# Check for patchelf
|
22 |
result = subprocess.run(["which", "patchelf"], capture_output=True)
|
23 |
if result.returncode != 0:
|
24 |
missing_deps.append("patchelf")
|
25 |
|
26 |
-
# Check for gcc
|
27 |
result = subprocess.run(["which", "gcc"], capture_output=True)
|
28 |
if result.returncode != 0:
|
29 |
missing_deps.append("gcc")
|
@@ -66,72 +66,23 @@ def get_nuitka_version():
|
|
66 |
return "unknown"
|
67 |
|
68 |
def install_system_packages(packages_content, progress=gr.Progress()):
|
69 |
-
"""
|
70 |
if not packages_content.strip():
|
71 |
return "No system packages specified."
|
72 |
|
73 |
-
|
|
|
|
|
74 |
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
install_log = ""
|
83 |
-
failed_packages = []
|
84 |
-
successful_packages = []
|
85 |
-
|
86 |
-
lines = packages_content.strip().split('\n')
|
87 |
-
total_packages = len([line for line in lines if line.strip() and not line.strip().startswith('#')])
|
88 |
-
current_package = 0
|
89 |
-
|
90 |
-
with open(temp_path, 'r') as f:
|
91 |
-
for line in f:
|
92 |
-
line = line.strip()
|
93 |
-
# Skip empty lines and comments
|
94 |
-
if not line or line.startswith('#'):
|
95 |
-
continue
|
96 |
-
|
97 |
-
current_package += 1
|
98 |
-
progress(current_package / total_packages, desc=f"Installing: {line}")
|
99 |
-
|
100 |
-
# Try to install package (Note: this might not work in HF Spaces)
|
101 |
-
install_process = subprocess.run(
|
102 |
-
["apt-get", "update", "-qq"],
|
103 |
-
capture_output=True,
|
104 |
-
text=True
|
105 |
-
)
|
106 |
-
|
107 |
-
install_process = subprocess.run(
|
108 |
-
["apt-get", "install", "-y", line],
|
109 |
-
capture_output=True,
|
110 |
-
text=True
|
111 |
-
)
|
112 |
-
|
113 |
-
if install_process.returncode == 0:
|
114 |
-
successful_packages.append(line)
|
115 |
-
install_log += f"β
Successfully installed: {line}\n"
|
116 |
-
else:
|
117 |
-
failed_packages.append(line)
|
118 |
-
install_log += f"β Failed to install: {line}\n"
|
119 |
-
|
120 |
-
summary = f"""
|
121 |
-
System Packages Summary:
|
122 |
-
β
Successful: {', '.join(successful_packages)}
|
123 |
-
β Failed: {', '.join(failed_packages)}
|
124 |
"""
|
125 |
-
|
126 |
-
|
127 |
-
except Exception as e:
|
128 |
-
error_msg = f"Error installing system packages: {str(e)}"
|
129 |
-
return error_msg
|
130 |
-
|
131 |
-
finally:
|
132 |
-
# Clean up
|
133 |
-
if os.path.exists(temp_path):
|
134 |
-
os.unlink(temp_path)
|
135 |
|
136 |
def find_compiled_binary(output_dir, output_filename):
|
137 |
"""Find the compiled binary, checking different possible paths"""
|
@@ -172,8 +123,6 @@ def compile_with_nuitka(code, requirements, packages, compilation_mode, output_e
|
|
172 |
|
173 |
# Check dependencies first
|
174 |
missing_deps = check_dependencies()
|
175 |
-
if missing_deps:
|
176 |
-
return f"Required dependencies are missing: {', '.join(missing_deps)}", None, None, False
|
177 |
|
178 |
# Create unique ID for this compilation
|
179 |
job_id = str(uuid.uuid4())
|
@@ -185,12 +134,10 @@ def compile_with_nuitka(code, requirements, packages, compilation_mode, output_e
|
|
185 |
ensure_dir(job_dir)
|
186 |
ensure_dir(output_dir)
|
187 |
|
188 |
-
progress(0.1, desc="
|
189 |
|
190 |
-
#
|
191 |
-
packages_result =
|
192 |
-
if packages.strip():
|
193 |
-
packages_result = install_system_packages(packages, progress)
|
194 |
|
195 |
# Write code to a Python file
|
196 |
script_path = os.path.join(job_dir, "user_script.py")
|
@@ -213,26 +160,20 @@ def compile_with_nuitka(code, requirements, packages, compilation_mode, output_e
|
|
213 |
)
|
214 |
|
215 |
if install_process.returncode == 0:
|
216 |
-
install_result = "Python requirements installed successfully."
|
217 |
else:
|
218 |
-
install_result = f"Installation completed with
|
219 |
except Exception as e:
|
220 |
-
install_result = f"Error: {str(e)}"
|
221 |
-
return
|
222 |
|
223 |
# Compilation
|
224 |
try:
|
225 |
progress(0.3, desc="Starting compilation...")
|
226 |
|
227 |
-
#
|
228 |
-
def build_cmd(base_cmd, use_static=False):
|
229 |
-
if use_static and has_static_libpython:
|
230 |
-
base_cmd.append("--static-libpython=yes")
|
231 |
-
return base_cmd
|
232 |
-
|
233 |
-
# Map compilation mode strings to actual configurations
|
234 |
if compilation_mode == "Maximum Compatibility (Recommended)":
|
235 |
-
cmd =
|
236 |
sys.executable, "-m", "nuitka",
|
237 |
"--standalone",
|
238 |
"--onefile", # Single portable file
|
@@ -241,23 +182,32 @@ def compile_with_nuitka(code, requirements, packages, compilation_mode, output_e
|
|
241 |
"--follow-imports",
|
242 |
"--assume-yes-for-downloads", # Auto-download missing dependencies
|
243 |
"--python-flag=no_site", # Reduce dependencies
|
|
|
244 |
script_path,
|
245 |
f"--output-dir={output_dir}"
|
246 |
-
]
|
|
|
|
|
|
|
|
|
|
|
247 |
mode_name = "Maximum Compatibility Binary"
|
|
|
248 |
elif compilation_mode == "Portable Binary":
|
249 |
-
cmd =
|
250 |
sys.executable, "-m", "nuitka",
|
251 |
"--show-progress",
|
252 |
"--remove-output",
|
253 |
"--assume-yes-for-downloads",
|
254 |
"--python-flag=no_site",
|
|
|
255 |
script_path,
|
256 |
f"--output-dir={output_dir}"
|
257 |
-
]
|
258 |
mode_name = "Portable Non-Standalone"
|
|
|
259 |
else: # Standalone Binary
|
260 |
-
cmd =
|
261 |
sys.executable, "-m", "nuitka",
|
262 |
"--standalone",
|
263 |
"--onefile",
|
@@ -265,9 +215,10 @@ def compile_with_nuitka(code, requirements, packages, compilation_mode, output_e
|
|
265 |
"--remove-output",
|
266 |
"--assume-yes-for-downloads",
|
267 |
"--python-flag=no_site",
|
|
|
268 |
script_path,
|
269 |
f"--output-dir={output_dir}"
|
270 |
-
]
|
271 |
mode_name = "Standalone Binary"
|
272 |
|
273 |
# Run compilation
|
@@ -291,7 +242,10 @@ def compile_with_nuitka(code, requirements, packages, compilation_mode, output_e
|
|
291 |
|
292 |
# Update progress
|
293 |
progress_val = 0.3 + (min(line_count / 200, 0.69) * 0.7)
|
294 |
-
|
|
|
|
|
|
|
295 |
|
296 |
process.wait()
|
297 |
|
@@ -319,20 +273,26 @@ def compile_with_nuitka(code, requirements, packages, compilation_mode, output_e
|
|
319 |
|
320 |
if process.returncode == 0 and binary_path:
|
321 |
# Check if it's really a binary file
|
322 |
-
|
323 |
-
|
|
|
|
|
|
|
324 |
|
325 |
# Check linking type
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
# Check what dynamic libraries are required
|
331 |
-
if ldd_process.returncode == 0:
|
332 |
-
libs = ldd_process.stdout.count("=>")
|
333 |
-
linking_info = f"π Dynamically linked ({libs} libraries) - designed for maximum compatibility"
|
334 |
else:
|
335 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
336 |
|
337 |
# Rename to desired extension
|
338 |
if output_extension in ['.bin', '.sh'] and not binary_path.endswith(output_extension):
|
@@ -348,301 +308,33 @@ def compile_with_nuitka(code, requirements, packages, compilation_mode, output_e
|
|
348 |
|
349 |
# Add system info to result
|
350 |
result_summary = f"""
|
351 |
-
Compilation
|
352 |
-
|
353 |
-
|
354 |
-
-
|
355 |
-
-
|
356 |
-
-
|
357 |
-
-
|
358 |
-
-
|
359 |
-
-
|
|
|
|
|
360 |
|
361 |
-
|
362 |
-
|
|
|
363 |
|
364 |
-
Binary Information:
|
|
|
365 |
|
366 |
-
|
367 |
- This binary was compiled with maximum compatibility settings
|
368 |
- Using --onefile for single-file distribution
|
369 |
- Added --assume-yes-for-downloads for automatic dependency resolution
|
370 |
- Used --python-flag=no_site to reduce system dependencies
|
371 |
- Should work on most compatible Linux systems
|
372 |
-
"""
|
373 |
-
|
374 |
-
progress(1.0, desc="Compilation successful!")
|
375 |
-
return result_summary, binary_path, compile_output, True
|
376 |
-
else:
|
377 |
-
return f"Compilation failed.\n\nSystem Packages: {packages_result}\nPython Requirements: {install_result}", None, compile_output, False
|
378 |
-
|
379 |
-
except Exception as e:
|
380 |
-
return f"Compilation error: {str(e)}", None, f"Error: {str(e)}", False
|
381 |
-
|
382 |
-
def run_compiled_binary(binary_path):
|
383 |
-
"""Run the compiled binary and return the output"""
|
384 |
-
if not binary_path or not os.path.exists(binary_path):
|
385 |
-
return "No binary available to run."
|
386 |
-
|
387 |
-
try:
|
388 |
-
# Make the binary executable
|
389 |
-
os.chmod(binary_path, 0o755)
|
390 |
-
|
391 |
-
# Run the binary with timeout
|
392 |
-
process = subprocess.run(
|
393 |
-
[binary_path],
|
394 |
-
capture_output=True,
|
395 |
-
text=True,
|
396 |
-
timeout=10
|
397 |
-
)
|
398 |
-
|
399 |
-
output = ""
|
400 |
-
if process.stdout:
|
401 |
-
output += f"[STDOUT]\n{process.stdout}\n"
|
402 |
-
if process.stderr:
|
403 |
-
output += f"[STDERR]\n{process.stderr}\n"
|
404 |
-
if process.returncode != 0:
|
405 |
-
output += f"[EXIT CODE] {process.returncode}\n"
|
406 |
-
|
407 |
-
return output if output else "No output."
|
408 |
-
except subprocess.TimeoutExpired:
|
409 |
-
return "Execution timed out after 10 seconds."
|
410 |
-
except Exception as e:
|
411 |
-
return f"Error running the binary: {str(e)}"
|
412 |
-
|
413 |
-
# Create Gradio interface
|
414 |
-
with gr.Blocks(title="Nuitka Python Compiler", theme=gr.themes.Soft()) as app:
|
415 |
-
gr.Markdown("# π Nuitka Python Compiler (Smart Compilation)")
|
416 |
-
gr.Markdown("Convert your Python code into portable executables using Nuitka with smart compatibility detection.")
|
417 |
-
|
418 |
-
# Check environment status
|
419 |
-
has_static = check_static_libpython()
|
420 |
-
missing_deps = check_dependencies()
|
421 |
-
|
422 |
-
if has_static:
|
423 |
-
gr.Markdown("π― **Static Libpython Available!** Maximum portability enabled.")
|
424 |
-
else:
|
425 |
-
gr.Markdown("π§ **Using alternative portable options.** Static libpython not available.")
|
426 |
-
|
427 |
-
if missing_deps:
|
428 |
-
gr.Markdown(f"β οΈ **Missing dependencies:** {', '.join(missing_deps)}")
|
429 |
-
else:
|
430 |
-
gr.Markdown("β
**All required dependencies available!**")
|
431 |
-
|
432 |
-
with gr.Tabs():
|
433 |
-
with gr.TabItem("π§ Compiler"):
|
434 |
-
with gr.Row():
|
435 |
-
with gr.Column(scale=2):
|
436 |
-
code_input = gr.Code(
|
437 |
-
value="""# Your Python code here
|
438 |
-
print('Hello from compiled Python!')
|
439 |
-
print('This is a smart-compiled binary!')
|
440 |
-
|
441 |
-
# This will work with automatic compatibility detection
|
442 |
-
import os, sys
|
443 |
-
print(f'Running from: {os.getcwd()}')
|
444 |
-
print(f'Python executable: {sys.executable}')
|
445 |
-
print('Compilation was optimized for your environment!')""",
|
446 |
-
language="python",
|
447 |
-
label="Your Python Code",
|
448 |
-
lines=20
|
449 |
-
)
|
450 |
-
|
451 |
-
with gr.Column(scale=1):
|
452 |
-
with gr.Tabs():
|
453 |
-
with gr.TabItem("Python Requirements"):
|
454 |
-
requirements_input = gr.Textbox(
|
455 |
-
placeholder="""# Add your Python dependencies here
|
456 |
-
# Example:
|
457 |
-
# numpy==1.24.0
|
458 |
-
# pandas==2.0.0
|
459 |
-
# requests>=2.28.0""",
|
460 |
-
lines=8,
|
461 |
-
label="requirements.txt content"
|
462 |
-
)
|
463 |
-
|
464 |
-
with gr.TabItem("System Packages"):
|
465 |
-
packages_input = gr.Textbox(
|
466 |
-
placeholder="""# Add system packages here (one per line)
|
467 |
-
# Example:
|
468 |
-
# build-essential
|
469 |
-
# libssl-dev
|
470 |
-
# ffmpeg
|
471 |
-
# imagemagick""",
|
472 |
-
lines=8,
|
473 |
-
label="packages.txt content"
|
474 |
-
)
|
475 |
-
|
476 |
-
# Fixed dropdown choices
|
477 |
-
compilation_mode = gr.Dropdown(
|
478 |
-
choices=[
|
479 |
-
"Maximum Compatibility (Recommended)",
|
480 |
-
"Portable Binary",
|
481 |
-
"Standalone Binary"
|
482 |
-
],
|
483 |
-
value="Maximum Compatibility (Recommended)",
|
484 |
-
label="Compilation Mode"
|
485 |
-
)
|
486 |
-
|
487 |
-
output_extension = gr.Dropdown(
|
488 |
-
choices=[".bin", ".sh"],
|
489 |
-
value=".bin",
|
490 |
-
label="Output File Extension"
|
491 |
-
)
|
492 |
-
|
493 |
-
gr.Markdown(f"π **Compiling with Python {get_current_python_version()}**")
|
494 |
-
if check_static_libpython():
|
495 |
-
gr.Markdown("π **Static libpython will be used!**")
|
496 |
-
else:
|
497 |
-
gr.Markdown("π§ **Using portable compilation flags**")
|
498 |
-
|
499 |
-
compile_btn = gr.Button("π Compile with Nuitka", variant="primary")
|
500 |
-
|
501 |
-
# Results section
|
502 |
-
with gr.Column(visible=False) as results_section:
|
503 |
-
result_summary = gr.Textbox(label="Compilation Results", lines=10)
|
504 |
-
compile_logs = gr.Textbox(label="Compilation Logs", lines=10)
|
505 |
-
download_file = gr.File(label="Download Compiled Binary")
|
506 |
-
|
507 |
-
# Test run section
|
508 |
-
with gr.Row():
|
509 |
-
run_btn = gr.Button("π§ͺ Test Run")
|
510 |
-
run_output = gr.Textbox(label="Execution Output", lines=8)
|
511 |
-
|
512 |
-
# Variables to store state
|
513 |
-
current_binary_path = gr.State(None)
|
514 |
-
compilation_success = gr.State(False)
|
515 |
-
|
516 |
-
def handle_compilation(code, requirements, packages, mode, extension, progress=gr.Progress()):
|
517 |
-
summary, binary_path, logs, success = compile_with_nuitka(
|
518 |
-
code, requirements, packages, mode, extension, progress
|
519 |
-
)
|
520 |
-
|
521 |
-
if success and binary_path:
|
522 |
-
# Create download file
|
523 |
-
download_filename = f"compiled_program{extension}"
|
524 |
-
download_path = os.path.join(os.path.dirname(binary_path), download_filename)
|
525 |
-
shutil.copy2(binary_path, download_path)
|
526 |
-
|
527 |
-
return (
|
528 |
-
gr.update(visible=True), # results_section
|
529 |
-
gr.update(value=summary), # result_summary
|
530 |
-
gr.update(value=logs), # compile_logs
|
531 |
-
gr.update(value=download_path), # download_file
|
532 |
-
binary_path, # current_binary_path state
|
533 |
-
True # compilation_success state
|
534 |
-
)
|
535 |
-
else:
|
536 |
-
return (
|
537 |
-
gr.update(visible=True), # results_section
|
538 |
-
gr.update(value=summary), # result_summary
|
539 |
-
gr.update(value=logs), # compile_logs
|
540 |
-
gr.update(visible=False), # download_file
|
541 |
-
None, # current_binary_path state
|
542 |
-
False # compilation_success state
|
543 |
-
)
|
544 |
-
|
545 |
-
def handle_run(binary_path):
|
546 |
-
if binary_path:
|
547 |
-
output = run_compiled_binary(binary_path)
|
548 |
-
return gr.update(value=output)
|
549 |
-
else:
|
550 |
-
return gr.update(value="No binary available to run.")
|
551 |
-
|
552 |
-
compile_btn.click(
|
553 |
-
handle_compilation,
|
554 |
-
inputs=[code_input, requirements_input, packages_input, compilation_mode, output_extension],
|
555 |
-
outputs=[results_section, result_summary, compile_logs, download_file, current_binary_path, compilation_success]
|
556 |
-
)
|
557 |
-
|
558 |
-
run_btn.click(
|
559 |
-
handle_run,
|
560 |
-
inputs=[current_binary_path],
|
561 |
-
outputs=[run_output]
|
562 |
-
)
|
563 |
-
|
564 |
-
with gr.TabItem("π How to Use"):
|
565 |
-
gr.Markdown("""
|
566 |
-
## π― Smart Compilation
|
567 |
-
|
568 |
-
**Automatic Environment Detection**
|
569 |
-
|
570 |
-
This app automatically detects your Python environment and chooses the best compilation strategy:
|
571 |
-
- Uses static libpython if available (maximum portability)
|
572 |
-
- Falls back to highly portable alternatives if not
|
573 |
-
- Automatically handles missing dependencies
|
574 |
-
- Optimizes for your specific environment
|
575 |
-
|
576 |
-
## π General Instructions
|
577 |
-
|
578 |
-
```bash
|
579 |
-
# Download the compiled binary
|
580 |
-
# Method 1: Direct run (if static libpython was used)
|
581 |
-
chmod +x compiled_program.bin
|
582 |
-
./compiled_program.bin
|
583 |
-
|
584 |
-
# Method 2: Copy to Linux filesystem first (recommended for WSL)
|
585 |
-
cp /mnt/c/Users/username/Downloads/compiled_program.bin ~/
|
586 |
-
cd ~
|
587 |
-
chmod +x compiled_program.bin
|
588 |
-
./compiled_program.bin
|
589 |
-
```
|
590 |
-
|
591 |
-
## π Environment Types
|
592 |
-
|
593 |
-
| Environment | Portability | Requirements | Best For |
|
594 |
-
|-------------|-------------|--------------|----------|
|
595 |
-
| Static Libpython | Maximum | None | Any Linux system |
|
596 |
-
| Non-Static Libpython | High | Compatible system | Similar environments |
|
597 |
-
""")
|
598 |
-
|
599 |
-
with gr.TabItem("βΉοΈ About"):
|
600 |
-
gr.Markdown(f"""
|
601 |
-
## π§ Smart Compilation Technology
|
602 |
-
|
603 |
-
**How it works:**
|
604 |
-
|
605 |
-
1. **Environment Detection**: Checks if static libpython is available
|
606 |
-
2. **Adaptive Options**: Uses the best available compilation flags
|
607 |
-
3. **Fallback Strategy**: Ensures compilation succeeds even without static linking
|
608 |
-
4. **Automatic Dependencies**: Resolves missing dependencies automatically
|
609 |
-
|
610 |
-
This approach maximizes compatibility across different Python environments.
|
611 |
-
|
612 |
-
## β
What This Solves
|
613 |
-
|
614 |
-
**Problems addressed:**
|
615 |
-
|
616 |
-
- Static libpython not available error
|
617 |
-
- Python version mismatches
|
618 |
-
- WSL compatibility issues
|
619 |
-
- Dependency resolution
|
620 |
-
- Cross-environment portability
|
621 |
-
|
622 |
-
## βοΈ Current Environment Status
|
623 |
-
|
624 |
-
```
|
625 |
-
Python Version: {get_current_python_version()}
|
626 |
-
Nuitka Version: {get_nuitka_version()}
|
627 |
-
Platform: {platform.platform()}
|
628 |
-
Architecture: {platform.architecture()[0]}
|
629 |
-
Machine: {platform.machine()}
|
630 |
-
Static Libpython: {'β
Available' if check_static_libpython() else 'β Not Available'}
|
631 |
-
```
|
632 |
-
|
633 |
-
## π Best Practices
|
634 |
-
|
635 |
-
**Recommendations:**
|
636 |
-
|
637 |
-
1. Always use "Maximum Compatibility" mode
|
638 |
-
2. Copy binaries to Linux filesystem in WSL
|
639 |
-
3. Test the binary in the target environment
|
640 |
-
4. Let the app automatically choose the best settings
|
641 |
-
5. Check the compilation details for specific optimization used
|
642 |
-
""")
|
643 |
-
|
644 |
-
gr.Markdown("---")
|
645 |
-
gr.Markdown("π€ Created by Claude 3.7 Sonnet | π Powered by Nuitka with Smart Compilation")
|
646 |
|
647 |
-
|
648 |
-
|
|
|
|
|
|
18 |
"""Check if required dependencies are available"""
|
19 |
missing_deps = []
|
20 |
|
21 |
+
# Check for patchelf (usually available in HF Spaces)
|
22 |
result = subprocess.run(["which", "patchelf"], capture_output=True)
|
23 |
if result.returncode != 0:
|
24 |
missing_deps.append("patchelf")
|
25 |
|
26 |
+
# Check for gcc (usually available in HF Spaces)
|
27 |
result = subprocess.run(["which", "gcc"], capture_output=True)
|
28 |
if result.returncode != 0:
|
29 |
missing_deps.append("gcc")
|
|
|
66 |
return "unknown"
|
67 |
|
68 |
def install_system_packages(packages_content, progress=gr.Progress()):
|
69 |
+
"""Note about system packages in HF Spaces - they cannot be installed"""
|
70 |
if not packages_content.strip():
|
71 |
return "No system packages specified."
|
72 |
|
73 |
+
# In HF Spaces, we can't install system packages
|
74 |
+
packages_list = [line.strip() for line in packages_content.strip().split('\n')
|
75 |
+
if line.strip() and not line.strip().startswith('#')]
|
76 |
|
77 |
+
if packages_list:
|
78 |
+
return f"""
|
79 |
+
β System packages cannot be installed in Hugging Face Spaces:
|
80 |
+
{', '.join(packages_list)}
|
81 |
+
|
82 |
+
βΉοΈ HF Spaces runs in a containerized environment without sudo access.
|
83 |
+
These packages need to be pre-installed in the Docker image or available as Python packages.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
84 |
"""
|
85 |
+
return "No system packages specified."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
86 |
|
87 |
def find_compiled_binary(output_dir, output_filename):
|
88 |
"""Find the compiled binary, checking different possible paths"""
|
|
|
123 |
|
124 |
# Check dependencies first
|
125 |
missing_deps = check_dependencies()
|
|
|
|
|
126 |
|
127 |
# Create unique ID for this compilation
|
128 |
job_id = str(uuid.uuid4())
|
|
|
134 |
ensure_dir(job_dir)
|
135 |
ensure_dir(output_dir)
|
136 |
|
137 |
+
progress(0.1, desc="Processing packages...")
|
138 |
|
139 |
+
# Handle system packages (just log them, can't install in HF Spaces)
|
140 |
+
packages_result = install_system_packages(packages, progress)
|
|
|
|
|
141 |
|
142 |
# Write code to a Python file
|
143 |
script_path = os.path.join(job_dir, "user_script.py")
|
|
|
160 |
)
|
161 |
|
162 |
if install_process.returncode == 0:
|
163 |
+
install_result = "β
Python requirements installed successfully."
|
164 |
else:
|
165 |
+
install_result = f"β οΈ Installation completed with warnings. Return code: {install_process.returncode}\n{install_process.stderr}"
|
166 |
except Exception as e:
|
167 |
+
install_result = f"β Error installing requirements: {str(e)}"
|
168 |
+
return install_result, None, f"Error: {str(e)}", False
|
169 |
|
170 |
# Compilation
|
171 |
try:
|
172 |
progress(0.3, desc="Starting compilation...")
|
173 |
|
174 |
+
# Build compilation command based on mode
|
|
|
|
|
|
|
|
|
|
|
|
|
175 |
if compilation_mode == "Maximum Compatibility (Recommended)":
|
176 |
+
cmd = [
|
177 |
sys.executable, "-m", "nuitka",
|
178 |
"--standalone",
|
179 |
"--onefile", # Single portable file
|
|
|
182 |
"--follow-imports",
|
183 |
"--assume-yes-for-downloads", # Auto-download missing dependencies
|
184 |
"--python-flag=no_site", # Reduce dependencies
|
185 |
+
"--python-flag=no_warnings", # Reduce warnings
|
186 |
script_path,
|
187 |
f"--output-dir={output_dir}"
|
188 |
+
]
|
189 |
+
|
190 |
+
# Add static linking if available
|
191 |
+
if has_static_libpython:
|
192 |
+
cmd.append("--static-libpython=yes")
|
193 |
+
|
194 |
mode_name = "Maximum Compatibility Binary"
|
195 |
+
|
196 |
elif compilation_mode == "Portable Binary":
|
197 |
+
cmd = [
|
198 |
sys.executable, "-m", "nuitka",
|
199 |
"--show-progress",
|
200 |
"--remove-output",
|
201 |
"--assume-yes-for-downloads",
|
202 |
"--python-flag=no_site",
|
203 |
+
"--python-flag=no_warnings",
|
204 |
script_path,
|
205 |
f"--output-dir={output_dir}"
|
206 |
+
]
|
207 |
mode_name = "Portable Non-Standalone"
|
208 |
+
|
209 |
else: # Standalone Binary
|
210 |
+
cmd = [
|
211 |
sys.executable, "-m", "nuitka",
|
212 |
"--standalone",
|
213 |
"--onefile",
|
|
|
215 |
"--remove-output",
|
216 |
"--assume-yes-for-downloads",
|
217 |
"--python-flag=no_site",
|
218 |
+
"--python-flag=no_warnings",
|
219 |
script_path,
|
220 |
f"--output-dir={output_dir}"
|
221 |
+
]
|
222 |
mode_name = "Standalone Binary"
|
223 |
|
224 |
# Run compilation
|
|
|
242 |
|
243 |
# Update progress
|
244 |
progress_val = 0.3 + (min(line_count / 200, 0.69) * 0.7)
|
245 |
+
if "INFO:" in line:
|
246 |
+
progress(progress_val, desc=f"Compiling: {line.strip()[:50]}...")
|
247 |
+
else:
|
248 |
+
progress(progress_val, desc="Compiling...")
|
249 |
|
250 |
process.wait()
|
251 |
|
|
|
273 |
|
274 |
if process.returncode == 0 and binary_path:
|
275 |
# Check if it's really a binary file
|
276 |
+
try:
|
277 |
+
file_process = subprocess.run(["file", binary_path], capture_output=True, text=True)
|
278 |
+
binary_info = file_process.stdout
|
279 |
+
except:
|
280 |
+
binary_info = "Binary file (unable to get detailed info)"
|
281 |
|
282 |
# Check linking type
|
283 |
+
try:
|
284 |
+
ldd_process = subprocess.run(["ldd", binary_path], capture_output=True, text=True)
|
285 |
+
if "not a dynamic executable" in ldd_process.stderr or "statically linked" in ldd_process.stdout:
|
286 |
+
linking_info = "β
Statically linked - fully portable!"
|
|
|
|
|
|
|
|
|
287 |
else:
|
288 |
+
# Check what dynamic libraries are required
|
289 |
+
if ldd_process.returncode == 0:
|
290 |
+
libs = ldd_process.stdout.count("=>")
|
291 |
+
linking_info = f"π Dynamically linked ({libs} libraries) - designed for maximum compatibility"
|
292 |
+
else:
|
293 |
+
linking_info = "βΉοΈ Compiled binary - should work on compatible systems"
|
294 |
+
except:
|
295 |
+
linking_info = "βΉοΈ Compiled binary created successfully"
|
296 |
|
297 |
# Rename to desired extension
|
298 |
if output_extension in ['.bin', '.sh'] and not binary_path.endswith(output_extension):
|
|
|
308 |
|
309 |
# Add system info to result
|
310 |
result_summary = f"""
|
311 |
+
# β
Compilation Successful!
|
312 |
+
|
313 |
+
## Compilation Details:
|
314 |
+
- **Mode**: {mode_name}
|
315 |
+
- **Nuitka Version**: {nuitka_version}
|
316 |
+
- **Exit Code**: {process.returncode}
|
317 |
+
- **Output Path**: {binary_path}
|
318 |
+
- **File Size**: {os.path.getsize(binary_path) / 1024:.2f} KB
|
319 |
+
- **Compiled with Python**: {current_python}
|
320 |
+
- **Static Libpython Available**: {'Yes' if has_static_libpython else 'No'}
|
321 |
+
- **Linking**: {linking_info}
|
322 |
|
323 |
+
## Environment Results:
|
324 |
+
**System Packages**: {packages_result}
|
325 |
+
**Python Requirements**: {install_result}
|
326 |
|
327 |
+
## Binary Information:
|
328 |
+
{binary_info}
|
329 |
|
330 |
+
## π Portability Notes:
|
331 |
- This binary was compiled with maximum compatibility settings
|
332 |
- Using --onefile for single-file distribution
|
333 |
- Added --assume-yes-for-downloads for automatic dependency resolution
|
334 |
- Used --python-flag=no_site to reduce system dependencies
|
335 |
- Should work on most compatible Linux systems
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
336 |
|
337 |
+
## π Usage Instructions:
|
338 |
+
```bash
|
339 |
+
chmod +x {os.path.basename(binary_path)}
|
340 |
+
./{os.path.basename(binary_path)}
|