use std::path::{Path, PathBuf};
use crate::error::Result;
pub struct PythonBindings {
output_dir: PathBuf,
package_name: String,
version: String,
min_python_version: String,
dependencies: Vec<String>,
}
impl PythonBindings {
pub fn new(output_dir: impl AsRef<Path>) -> Self {
Self {
output_dir: output_dir.as_ref().to_path_buf(),
package_name: "wasm_sandbox".to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
min_python_version: "3.8".to_string(),
dependencies: vec![
"cffi>=1.15.0".to_string(),
"pydantic>=2.0.0".to_string(),
],
}
}
pub fn with_package_name(mut self, name: impl Into<String>) -> Self {
self.package_name = name.into();
self
}
pub fn with_version(mut self, version: impl Into<String>) -> Self {
self.version = version.into();
self
}
pub fn with_min_python_version(mut self, version: impl Into<String>) -> Self {
self.min_python_version = version.into();
self
}
pub fn add_dependency(mut self, dependency: impl Into<String>) -> Self {
self.dependencies.push(dependency.into());
self
}
pub fn generate(&self) -> Result<()> {
std::fs::create_dir_all(&self.output_dir)?;
self.generate_package_structure()?;
self.generate_core_bindings()?;
self.generate_setup_py()?;
self.generate_readme()?;
self.generate_pyproject_toml()?;
Ok(())
}
fn generate_package_structure(&self) -> Result<()> {
let package_dir = self.output_dir.join(&self.package_name);
std::fs::create_dir_all(&package_dir)?;
let init_content = self.generate_init_py_content();
std::fs::write(package_dir.join("__init__.py"), init_content)?;
let modules = ["sandbox", "security", "communication", "utils"];
for module in &modules {
let module_dir = package_dir.join(module);
std::fs::create_dir_all(&module_dir)?;
let module_init_content = format!("# {} module\n\n", module);
std::fs::write(module_dir.join("__init__.py"), module_init_content)?;
}
Ok(())
}
fn generate_core_bindings(&self) -> Result<()> {
let sandbox_module_path = self.output_dir
.join(&self.package_name)
.join("sandbox")
.join("sandbox.py");
let sandbox_content = r#"
import os
import json
import time
from typing import Any, Dict, List, Optional, TypeVar, Generic, Union
from pathlib import Path
from .._native import lib as native_lib
from ..security import Capabilities, ResourceLimits
T = TypeVar('T')
R = TypeVar('R')
class WasmSandbox:
"""WebAssembly sandbox for secure code execution"""
def __init__(self):
"""Initialize a new sandbox with default settings"""
self._handle = native_lib.sandbox_new()
if not self._handle:
raise RuntimeError("Failed to create sandbox")
def __del__(self):
"""Clean up sandbox resources"""
if hasattr(self, '_handle') and self._handle:
native_lib.sandbox_free(self._handle)
@classmethod
def builder(cls):
"""Create a sandbox builder for customized configuration"""
return SandboxBuilder()
@classmethod
def from_source(cls, source_path: Union[str, Path]):
"""Create a sandbox from source code"""
sandbox = cls()
source_path_str = str(source_path)
result = native_lib.sandbox_load_from_source(sandbox._handle, source_path_str)
if result != 0:
error_msg = native_lib.get_last_error()
raise RuntimeError(f"Failed to load from source: {error_msg}")
return sandbox
def load_module(self, wasm_bytes: bytes):
"""Load a WebAssembly module from bytes"""
module_id = native_lib.sandbox_load_module(self._handle, wasm_bytes, len(wasm_bytes))
if not module_id:
error_msg = native_lib.get_last_error()
raise RuntimeError(f"Failed to load module: {error_msg}")
return module_id
def call(self, function_name: str, params: Any) -> Any:
"""Call a function in the sandbox"""
# Serialize params to JSON
params_json = json.dumps(params)
# Call the function
result_ptr = native_lib.sandbox_call_function(
self._handle,
function_name.encode('utf-8'),
params_json.encode('utf-8')
)
if not result_ptr:
error_msg = native_lib.get_last_error()
raise RuntimeError(f"Function call failed: {error_msg}")
# Get the result
result_json = native_lib.get_result_json(result_ptr).decode('utf-8')
native_lib.free_result(result_ptr)
# Parse and return the result
return json.loads(result_json)
class SandboxBuilder:
"""Builder for configuring and creating WasmSandbox instances"""
def __init__(self):
"""Initialize the builder with default settings"""
self._handle = native_lib.sandbox_builder_new()
if not self._handle:
raise RuntimeError("Failed to create sandbox builder")
def __del__(self):
"""Clean up builder resources"""
if hasattr(self, '_handle') and self._handle:
native_lib.sandbox_builder_free(self._handle)
def source(self, path: Union[str, Path]):
"""Set the source code path"""
native_lib.sandbox_builder_source(self._handle, str(path).encode('utf-8'))
return self
def timeout_duration(self, duration_seconds: float):
"""Set the execution timeout duration"""
native_lib.sandbox_builder_timeout(self._handle, int(duration_seconds * 1000))
return self
def memory_limit(self, limit_bytes: int):
"""Set the memory limit in bytes"""
native_lib.sandbox_builder_memory_limit(self._handle, limit_bytes)
return self
def enable_file_access(self, enabled: bool):
"""Enable or disable file system access"""
native_lib.sandbox_builder_file_access(self._handle, 1 if enabled else 0)
return self
def enable_network(self, enabled: bool):
"""Enable or disable network access"""
native_lib.sandbox_builder_network(self._handle, 1 if enabled else 0)
return self
def build(self):
"""Build the sandbox with the configured settings"""
sandbox_handle = native_lib.sandbox_builder_build(self._handle)
if not sandbox_handle:
error_msg = native_lib.get_last_error()
raise RuntimeError(f"Failed to build sandbox: {error_msg}")
sandbox = WasmSandbox.__new__(WasmSandbox)
sandbox._handle = sandbox_handle
return sandbox
def run(source_path: Union[str, Path], function_name: str, params: Any) -> Any:
"""Run a function in a sandbox with default settings"""
sandbox = WasmSandbox.from_source(source_path)
return sandbox.call(function_name, params)
def run_with_timeout(
source_path: Union[str, Path],
function_name: str,
params: Any,
timeout_seconds: float
) -> Any:
"""Run a function in a sandbox with a timeout"""
sandbox = WasmSandbox.builder() \
.source(source_path) \
.timeout_duration(timeout_seconds) \
.build()
return sandbox.call(function_name, params)
"#;
std::fs::write(sandbox_module_path, sandbox_content)?;
let native_module_path = self.output_dir
.join(&self.package_name)
.join("_native.py");
let native_content = r#"
import os
import platform
import sys
from cffi import FFI
ffi = FFI()
ffi.cdef("""
// Sandbox functions
void* sandbox_new(void);
void sandbox_free(void* sandbox);
int sandbox_load_from_source(void* sandbox, const char* source_path);
const char* sandbox_load_module(void* sandbox, const void* wasm_bytes, size_t wasm_len);
void* sandbox_call_function(void* sandbox, const char* function_name, const char* params_json);
// Builder functions
void* sandbox_builder_new(void);
void sandbox_builder_free(void* builder);
void sandbox_builder_source(void* builder, const char* source_path);
void sandbox_builder_timeout(void* builder, unsigned int timeout_ms);
void sandbox_builder_memory_limit(void* builder, size_t limit_bytes);
void sandbox_builder_file_access(void* builder, int enabled);
void sandbox_builder_network(void* builder, int enabled);
void* sandbox_builder_build(void* builder);
// Result handling
const char* get_result_json(void* result);
void free_result(void* result);
// Error handling
const char* get_last_error(void);
""")
# Determine the shared library name based on platform
if platform.system() == "Windows":
lib_name = "wasm_sandbox_python.dll"
elif platform.system() == "Darwin":
lib_name = "libwasm_sandbox_python.dylib"
else:
lib_name = "libwasm_sandbox_python.so"
# Try to find the library
lib_paths = [
# Current directory
os.path.dirname(os.path.abspath(__file__)),
# Parent directory
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
# System paths
"/usr/local/lib",
"/usr/lib",
]
lib_path = None
for path in lib_paths:
full_path = os.path.join(path, lib_name)
if os.path.exists(full_path):
lib_path = full_path
break
if lib_path is None:
raise ImportError(f"Could not find {lib_name}. Make sure it's installed correctly.")
lib = ffi.dlopen(lib_path)
"#;
std::fs::write(native_module_path, native_content)?;
let security_module_path = self.output_dir
.join(&self.package_name)
.join("security")
.join("security.py");
let security_content = r#"
from dataclasses import dataclass, field
from typing import List, Optional, Dict, Set
@dataclass
class MemoryLimits:
"""Memory limits for sandbox instances"""
max_memory_pages: int = 1024 # 64MB (1 page = 64KB)
reserved_memory_pages: int = 1 # 64KB
max_growth_rate: Optional[int] = None # pages per second, None for unlimited
@dataclass
class CpuLimits:
"""CPU limits for sandbox instances"""
max_execution_time: int = 30000 # milliseconds
target_cpu_usage: Optional[float] = None # percentage (0.0-1.0), None for unlimited
throttle_threshold: Optional[float] = None # percentage (0.0-1.0), None for no throttling
@dataclass
class IoLimits:
"""I/O limits for sandbox instances"""
max_read_bytes: Optional[int] = None # bytes, None for unlimited
max_write_bytes: Optional[int] = None # bytes, None for unlimited
max_read_files: Optional[int] = None # count, None for unlimited
max_write_files: Optional[int] = None # count, None for unlimited
max_read_rate: Optional[int] = None # bytes per second, None for unlimited
max_write_rate: Optional[int] = None # bytes per second, None for unlimited
@dataclass
class TimeLimits:
"""Time limits for sandbox instances"""
max_clock_drift: Optional[int] = None # milliseconds, None for unlimited
max_execution_time: int = 30000 # milliseconds
idle_timeout: Optional[int] = None # milliseconds, None for no idle timeout
@dataclass
class FilesystemCapability:
"""Filesystem capabilities for sandbox instances"""
readable_dirs: List[str] = field(default_factory=list)
writable_dirs: List[str] = field(default_factory=list)
max_file_size: Optional[int] = None # bytes, None for unlimited
allow_create: bool = False
allow_delete: bool = False
@dataclass
class NetworkCapability:
"""Network capability configuration"""
allow_outbound: bool = False
allow_inbound: bool = False
allowed_hosts: List[str] = field(default_factory=list)
allowed_ports: List[int] = field(default_factory=list)
allowed_protocols: List[str] = field(default_factory=list)
@dataclass
class Capabilities:
"""Security capabilities for sandbox instances"""
filesystem: FilesystemCapability = field(default_factory=FilesystemCapability)
network: NetworkCapability = field(default_factory=NetworkCapability)
environment_variables: Set[str] = field(default_factory=set)
allow_process_creation: bool = False
allow_dynamic_code: bool = False
allow_stdout: bool = True
allow_stderr: bool = True
@classmethod
def minimal(cls):
"""Create minimal capabilities - most restrictive"""
return cls(
filesystem=FilesystemCapability(),
network=NetworkCapability(),
environment_variables=set(),
allow_process_creation=False,
allow_dynamic_code=False,
allow_stdout=False,
allow_stderr=False,
)
@dataclass
class ResourceLimits:
"""Resource limits for sandbox instances"""
memory: MemoryLimits = field(default_factory=MemoryLimits)
cpu: CpuLimits = field(default_factory=CpuLimits)
io: IoLimits = field(default_factory=IoLimits)
time: TimeLimits = field(default_factory=TimeLimits)
"#;
std::fs::write(security_module_path, security_content)?;
Ok(())
}
fn generate_setup_py(&self) -> Result<()> {
let setup_py = format!(r#"#!/usr/bin/env python3
from setuptools import setup, find_packages
setup(
name="{package_name}",
version="{version}",
description="Python bindings for wasm-sandbox - A secure WebAssembly sandbox",
long_description=open("README.md").read(),
long_description_content_type="text/markdown",
author="Eric Evans",
author_email="ciresnave@gmail.com",
url="https://github.com/ciresnave/wasm-sandbox",
packages=find_packages(),
python_requires=">={min_python_version}",
install_requires=[
{dependencies}
],
classifiers=[
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: System :: Software Distribution",
],
)
"#,
package_name = self.package_name,
version = self.version,
min_python_version = self.min_python_version,
dependencies = self.dependencies.iter()
.map(|dep| format!(" \"{}\",", dep))
.collect::<Vec<_>>()
.join("\n"),
);
std::fs::write(self.output_dir.join("setup.py"), setup_py)?;
Ok(())
}
fn generate_readme(&self) -> Result<()> {
let readme = format!(r#"# {package_name}
Python bindings for [wasm-sandbox](https://github.com/ciresnave/wasm-sandbox) - A secure WebAssembly sandbox for running untrusted code.
## Installation
```bash
pip install {package_name}
```
## Quick Start
```python
from {package_name} import run, run_with_timeout
import time
# Simple one-line execution
result = run("calculator.py", "add", [5, 3])
print(f"5 + 3 = {{result}}") # Output: 5 + 3 = 8
# With timeout
result = run_with_timeout("processor.py", "process", "data", timeout_seconds=30)
print(f"Processed: {{result}}")
# Using the builder pattern
from {package_name} import WasmSandbox
sandbox = WasmSandbox.builder() \
.source("my_program.py") \
.timeout_duration(60) \
.memory_limit(64 * 1024 * 1024) \
.enable_file_access(False) \
.build()
# Call multiple functions
add_result = sandbox.call("add", [10, 20])
print(f"10 + 20 = {{add_result}}") # Output: 10 + 20 = 30
```
## Features
- **🔒 Secure Execution**: Run untrusted code safely in an isolated sandbox
- **🚀 Simple API**: One-line execution for common use cases
- **⏱️ Resource Limits**: Control memory usage, execution time, and more
- **🔧 Flexible Configuration**: Full control over security capabilities
## Requirements
- Python {min_python_version}+
- CFFI 1.15.0+
- wasm-sandbox native library
"#,
package_name = self.package_name,
min_python_version = self.min_python_version,
);
std::fs::write(self.output_dir.join("README.md"), readme)?;
Ok(())
}
fn generate_pyproject_toml(&self) -> Result<()> {
let pyproject_toml = format!(r#"[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[tool.black]
line-length = 100
target-version = ["py38"]
[tool.isort]
profile = "black"
multi_line_output = 3
[tool.mypy]
python_version = "{min_python_version}"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = "test_*.py"
"#,
min_python_version = self.min_python_version,
);
std::fs::write(self.output_dir.join("pyproject.toml"), pyproject_toml)?;
Ok(())
}
fn generate_init_py_content(&self) -> String {
format!(r#"""Python bindings for wasm-sandbox
A secure WebAssembly sandbox for running untrusted code.
"""
__version__ = "{version}"
from .sandbox.sandbox import WasmSandbox, run, run_with_timeout
from .security.security import (
Capabilities, ResourceLimits, MemoryLimits, CpuLimits,
FilesystemCapability, NetworkCapability
)
__all__ = [
"WasmSandbox",
"run",
"run_with_timeout",
"Capabilities",
"ResourceLimits",
"MemoryLimits",
"CpuLimits",
"FilesystemCapability",
"NetworkCapability",
]
"#,
version = self.version,
)
}
}
pub struct CBindingsGenerator {
output_dir: PathBuf,
}
impl CBindingsGenerator {
pub fn new(output_dir: impl AsRef<Path>) -> Self {
Self {
output_dir: output_dir.as_ref().to_path_buf(),
}
}
pub fn generate(&self) -> Result<()> {
std::fs::create_dir_all(&self.output_dir)?;
self.generate_header()?;
self.generate_implementation()?;
self.generate_build_script()?;
Ok(())
}
fn generate_header(&self) -> Result<()> {
let header_content = r#"/**
* wasm_sandbox_python.h
* C bindings for wasm-sandbox Python integration
*/
#ifndef WASM_SANDBOX_PYTHON_H
#define WASM_SANDBOX_PYTHON_H
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Create a new sandbox
* @return Handle to the sandbox or NULL on error
*/
void* sandbox_new(void);
/**
* Free a sandbox
* @param sandbox Handle to the sandbox
*/
void sandbox_free(void* sandbox);
/**
* Load a WebAssembly module from source code
* @param sandbox Handle to the sandbox
* @param source_path Path to the source code
* @return 0 on success, non-zero on error
*/
int sandbox_load_from_source(void* sandbox, const char* source_path);
/**
* Load a WebAssembly module from bytes
* @param sandbox Handle to the sandbox
* @param wasm_bytes WebAssembly module bytes
* @param wasm_len Length of WebAssembly module bytes
* @return Module ID string or NULL on error
*/
const char* sandbox_load_module(void* sandbox, const void* wasm_bytes, size_t wasm_len);
/**
* Call a function in the sandbox
* @param sandbox Handle to the sandbox
* @param function_name Name of the function to call
* @param params_json JSON-encoded parameters
* @return Result handle or NULL on error
*/
void* sandbox_call_function(void* sandbox, const char* function_name, const char* params_json);
/**
* Create a new sandbox builder
* @return Handle to the builder or NULL on error
*/
void* sandbox_builder_new(void);
/**
* Free a sandbox builder
* @param builder Handle to the builder
*/
void sandbox_builder_free(void* builder);
/**
* Set the source code path
* @param builder Handle to the builder
* @param source_path Path to the source code
*/
void sandbox_builder_source(void* builder, const char* source_path);
/**
* Set the execution timeout duration
* @param builder Handle to the builder
* @param timeout_ms Timeout in milliseconds
*/
void sandbox_builder_timeout(void* builder, unsigned int timeout_ms);
/**
* Set the memory limit
* @param builder Handle to the builder
* @param limit_bytes Memory limit in bytes
*/
void sandbox_builder_memory_limit(void* builder, size_t limit_bytes);
/**
* Enable or disable file system access
* @param builder Handle to the builder
* @param enabled 1 to enable, 0 to disable
*/
void sandbox_builder_file_access(void* builder, int enabled);
/**
* Enable or disable network access
* @param builder Handle to the builder
* @param enabled 1 to enable, 0 to disable
*/
void sandbox_builder_network(void* builder, int enabled);
/**
* Build the sandbox with the configured settings
* @param builder Handle to the builder
* @return Handle to the sandbox or NULL on error
*/
void* sandbox_builder_build(void* builder);
/**
* Get the JSON-encoded result
* @param result Handle to the result
* @return JSON-encoded result or NULL on error
*/
const char* get_result_json(void* result);
/**
* Free a result
* @param result Handle to the result
*/
void free_result(void* result);
/**
* Get the last error message
* @return Error message or NULL if no error
*/
const char* get_last_error(void);
#ifdef __cplusplus
}
#endif
#endif /* WASM_SANDBOX_PYTHON_H */
"#;
std::fs::write(self.output_dir.join("wasm_sandbox_python.h"), header_content)?;
Ok(())
}
fn generate_implementation(&self) -> Result<()> {
let implementation = r#"/**
* wasm_sandbox_python.c
* C bindings for wasm-sandbox Python integration
*/
#include "wasm_sandbox_python.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define WASM_SANDBOX_EXPORT __declspec(dllexport)
// Thread-local error message buffer
static __thread char error_buffer[1024];
// Set the last error message
static void set_error(const char* message) {
if (message) {
strncpy(error_buffer, message, sizeof(error_buffer) - 1);
error_buffer[sizeof(error_buffer) - 1] = '\0';
} else {
error_buffer[0] = '\0';
}
}
// These are just stub implementations that would be replaced with actual
// bindings to the Rust library using technologies like cxx, cbindgen, or bindgen
WASM_SANDBOX_EXPORT void* sandbox_new(void) {
// Placeholder for actual implementation
void* handle = malloc(8); // Just a placeholder
if (!handle) {
set_error("Failed to allocate memory for sandbox");
return NULL;
}
return handle;
}
WASM_SANDBOX_EXPORT void sandbox_free(void* sandbox) {
if (sandbox) {
free(sandbox);
}
}
WASM_SANDBOX_EXPORT int sandbox_load_from_source(void* sandbox, const char* source_path) {
if (!sandbox || !source_path) {
set_error("Invalid arguments");
return -1;
}
// Placeholder
return 0;
}
WASM_SANDBOX_EXPORT const char* sandbox_load_module(void* sandbox, const void* wasm_bytes, size_t wasm_len) {
if (!sandbox || !wasm_bytes || wasm_len == 0) {
set_error("Invalid arguments");
return NULL;
}
// Placeholder: Just return a fixed string
static char module_id[] = "module-12345";
return module_id;
}
WASM_SANDBOX_EXPORT void* sandbox_call_function(void* sandbox, const char* function_name, const char* params_json) {
if (!sandbox || !function_name || !params_json) {
set_error("Invalid arguments");
return NULL;
}
// Placeholder: Allocate a result to return
void* result = malloc(16);
if (!result) {
set_error("Failed to allocate result");
return NULL;
}
// In a real implementation, this would call into the Rust library
return result;
}
WASM_SANDBOX_EXPORT void* sandbox_builder_new(void) {
void* handle = malloc(8); // Just a placeholder
if (!handle) {
set_error("Failed to allocate memory for builder");
return NULL;
}
return handle;
}
WASM_SANDBOX_EXPORT void sandbox_builder_free(void* builder) {
if (builder) {
free(builder);
}
}
WASM_SANDBOX_EXPORT void sandbox_builder_source(void* builder, const char* source_path) {
// Placeholder
}
WASM_SANDBOX_EXPORT void sandbox_builder_timeout(void* builder, unsigned int timeout_ms) {
// Placeholder
}
WASM_SANDBOX_EXPORT void sandbox_builder_memory_limit(void* builder, size_t limit_bytes) {
// Placeholder
}
WASM_SANDBOX_EXPORT void sandbox_builder_file_access(void* builder, int enabled) {
// Placeholder
}
WASM_SANDBOX_EXPORT void sandbox_builder_network(void* builder, int enabled) {
// Placeholder
}
WASM_SANDBOX_EXPORT void* sandbox_builder_build(void* builder) {
if (!builder) {
set_error("Invalid builder");
return NULL;
}
void* sandbox = malloc(8); // Just a placeholder
if (!sandbox) {
set_error("Failed to create sandbox");
return NULL;
}
return sandbox;
}
WASM_SANDBOX_EXPORT const char* get_result_json(void* result) {
if (!result) {
set_error("Invalid result");
return NULL;
}
// Placeholder: Return a fixed JSON result
static char json_result[] = "{\"result\": 42}";
return json_result;
}
WASM_SANDBOX_EXPORT void free_result(void* result) {
if (result) {
free(result);
}
}
WASM_SANDBOX_EXPORT const char* get_last_error(void) {
if (error_buffer[0] == '\0') {
return NULL;
}
return error_buffer;
}
"#;
std::fs::write(self.output_dir.join("wasm_sandbox_python.c"), implementation)?;
Ok(())
}
fn generate_build_script(&self) -> Result<()> {
let build_script = r#"#!/bin/bash
set -e
# Build settings
CC=${CC:-gcc}
CFLAGS="-Wall -Wextra -O2 -fPIC -I."
OUTPUT_NAME="libwasm_sandbox_python.so"
if [[ "$OSTYPE" == "darwin"* ]]; then
CFLAGS="$CFLAGS -dynamiclib"
OUTPUT_NAME="libwasm_sandbox_python.dylib"
elif [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" || "$OSTYPE" == "win32" ]]; then
CC=${CC:-cl}
CFLAGS="/W4 /O2 /LD"
OUTPUT_NAME="wasm_sandbox_python.dll"
fi
echo "Building $OUTPUT_NAME..."
if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" || "$OSTYPE" == "win32" ]]; then
# Windows with MSVC
$CC $CFLAGS wasm_sandbox_python.c /Fe$OUTPUT_NAME
else
# Unix-like systems
$CC $CFLAGS -o $OUTPUT_NAME wasm_sandbox_python.c -lm
fi
echo "Build complete: $OUTPUT_NAME"
"#;
std::fs::write(self.output_dir.join("build.sh"), build_script)?;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perm = std::fs::metadata(self.output_dir.join("build.sh"))?.permissions();
perm.set_mode(0o755);
std::fs::set_permissions(self.output_dir.join("build.sh"), perm)?;
}
Ok(())
}
}
pub struct LanguageBindings;
impl LanguageBindings {
pub fn generate_python_bindings(output_dir: impl AsRef<Path>) -> Result<()> {
let python_dir = output_dir.as_ref().join("python");
let python_bindings = PythonBindings::new(&python_dir);
python_bindings.generate()?;
let c_bindings_dir = output_dir.as_ref().join("python_c_bindings");
let c_bindings = CBindingsGenerator::new(c_bindings_dir);
c_bindings.generate()?;
Ok(())
}
}