Skip to content

Fix build_nuitka Python stdlib #95

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -5178,7 +5178,7 @@ def setup_bundled_environment(self):
return

# Target location: NEXT TO APP.EXE (portable setup)
target_venv = app_dir / "manim_studio_default"
target_venv = app_dir / "manim_studio_default"

print(f"📁 Extracting bundled environment to: {target_venv}")
print(f"🚀 Portable mode: Everything stays with app.exe")
Expand All @@ -5189,10 +5189,10 @@ def setup_bundled_environment(self):
shutil.rmtree(target_venv, ignore_errors=True)

# Copy bundled environment to target location
try:
print("📦 Copying bundled environment...")
shutil.copytree(venv_bundle, target_venv)
print("✅ Bundled environment extracted successfully!")
try:
print("📦 Copying bundled environment...")
shutil.copytree(venv_bundle, target_venv, dirs_exist_ok=True)
print("✅ Bundled environment extracted successfully!")
except Exception as e:
print(f"❌ Failed to extract bundled environment: {e}")
self._initialize_environment()
Expand Down
220 changes: 122 additions & 98 deletions build_nuitka.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,63 +84,68 @@ def find_best_python_source():
print(f"⚠️ Using system Python: {current_dir.parent}")
return current_dir.parent

def copy_with_fallback(source, dest, fallback_search_name=None):
"""Copy file with fallback search if source doesn't exist"""
if source.exists():
try:
shutil.copy2(source, dest)
return True
except Exception as e:
print(f"❌ Copy failed: {e}")

if fallback_search_name:
print(f"🔍 Searching system for {fallback_search_name}...")

# Search common locations
search_locations = [
Path("C:/Windows/System32"),
Path("C:/Windows/SysWOW64"),
Path(sys.executable).parent,
Path(sys.prefix),
Path(sys.prefix) / "Scripts",
Path(sys.prefix) / "DLLs",
]

# Add all Python installations
python_installs = find_python_installations()
for install in python_installs:
search_locations.extend([
install,
install / "Scripts",
install / "DLLs",
install.parent / "DLLs"
])

# Search PATH directories
path_env = os.environ.get('PATH', '')
for path_dir in path_env.split(os.pathsep):
search_locations.append(Path(path_dir))

# Remove duplicates
search_locations = list(set(search_locations))

for location in search_locations:
try:
if location.exists():
for found_file in location.rglob(fallback_search_name):
if found_file.is_file():
try:
shutil.copy2(found_file, dest)
print(f"✅ Found and copied {fallback_search_name} from: {found_file}")
return True
except Exception as e:
continue
except:
continue

print(f"❌ Could not find {fallback_search_name} anywhere on system")

return False
def copy_with_fallback(source, dest, fallback_search_name=None):
"""Copy file with extensive fallback search if source doesn't exist"""
if source and source.exists():
try:
shutil.copy2(source, dest)
return True
except Exception as e:
print(f"❌ Copy failed: {e}")

if fallback_search_name:
print(f"🔍 Searching system for {fallback_search_name}...")

# Common search locations
search_locations = {
Path("C:/Windows/System32"),
Path("C:/Windows/SysWOW64"),
Path(sys.executable).parent,
Path(sys.prefix),
Path(sys.prefix) / "Scripts",
Path(sys.prefix) / "DLLs",
}

# Python installations
for install in find_python_installations():
search_locations.update({
install,
install / "Scripts",
install / "DLLs",
install.parent / "DLLs",
})

# PATH directories
for path_dir in os.environ.get("PATH", "").split(os.pathsep):
if path_dir:
search_locations.add(Path(path_dir))

# Additional Program Files locations
if os.name == "nt":
for env_var in ["ProgramFiles", "ProgramFiles(x86)"]:
pf = os.environ.get(env_var)
if pf:
search_locations.add(Path(pf))

# Perform case-insensitive search
target_lower = fallback_search_name.lower()
for location in search_locations:
try:
if location.exists():
for found in location.rglob("*"):
if found.is_file() and found.name.lower() == target_lower:
try:
shutil.copy2(found, dest)
print(f"✅ Found and copied {fallback_search_name} from: {found}")
return True
except Exception:
continue
except Exception:
continue

print(f"❌ Could not find {fallback_search_name} anywhere on system")

return False
"""Check if build environment is ready"""
print("🔍 Checking build environment...")

Expand Down Expand Up @@ -232,8 +237,8 @@ def bundle_complete_environment(args):

total_files = 0

# Step 2: Copy site-packages (most critical)
print("\n📦 Step 1: Copying Python packages...")
# Step 2: Copy site-packages (most critical)
print("\n📦 Step 2: Copying Python packages...")
source_site_packages = None
possible_site_packages = [
source_python / "Lib" / "site-packages",
Expand Down Expand Up @@ -289,9 +294,34 @@ def bundle_complete_environment(args):
print("❌ No site-packages directory found!")
return None

# Step 3: Copy and find Python executables
print("\n🐍 Step 2: Setting up Python executables...")
scripts_dest = bundle_dir / "Scripts"
# Step 3: Copy Python standard library (excluding site-packages)
print("\n📚 Step 3: Copying Python standard library...")
source_stdlib = source_python / "Lib"
dest_stdlib = bundle_dir / "Lib"

if source_stdlib.exists():
try:
for item in source_stdlib.iterdir():
if item.name == "site-packages":
continue
dest_path = dest_stdlib / item.name
if item.is_dir():
shutil.copytree(item, dest_path, dirs_exist_ok=True)
else:
shutil.copy2(item, dest_path)
stdlib_count = sum(1 for _ in dest_stdlib.rglob('*') if _.is_file())
total_files += stdlib_count
print(f"✅ Copied standard library: {stdlib_count} files")
except Exception as e:
print(f"❌ Failed to copy standard library: {e}")
return None
else:
print("❌ Standard library not found!")
return None

# Step 4: Copy and find Python executables
print("\n🐍 Step 4: Setting up Python executables...")
scripts_dest = bundle_dir / "Scripts"

# Copy Scripts directory if it exists
source_scripts = source_python / "Scripts"
Expand Down Expand Up @@ -355,8 +385,8 @@ def bundle_complete_environment(args):
except Exception as e:
print(f"⚠️ Could not create python3.exe: {e}")

# Step 4: Copy DLLs and runtime files
print("\n📚 Step 3: Copying runtime libraries...")
# Step 5: Copy DLLs and runtime files
print("\n📚 Step 5: Copying runtime libraries...")

# Copy DLLs directory if it exists
source_dlls = source_python / "DLLs"
Expand Down Expand Up @@ -388,8 +418,8 @@ def bundle_complete_environment(args):
print(f"✅ Found critical DLL: {dll_name}")
total_files += 1

# Step 5: Copy configuration files
print("\n⚙️ Step 4: Copying configuration...")
# Step 6: Copy configuration files
print("\n⚙️ Step 6: Copying configuration...")

# Copy pyvenv.cfg
source_cfg = source_python / "pyvenv.cfg"
Expand Down Expand Up @@ -869,22 +899,30 @@ def setup_bundled_environment():
print("ERROR: venv_bundle directory not found")
return False

site_packages = venv_bundle / "Lib" / "site-packages"
scripts_dir = venv_bundle / "Scripts"
stdlib_dir = venv_bundle / "Lib"
site_packages = stdlib_dir / "site-packages"
scripts_dir = venv_bundle / "Scripts"

print(f"Setting up bundled environment from: {venv_bundle}")

if site_packages.exists():
# Add to Python path at the beginning
sys.path.insert(0, str(site_packages))
print(f"Added to Python path: {site_packages}")
if stdlib_dir.exists():
sys.path.insert(0, str(stdlib_dir))
print(f"Added to Python path: {stdlib_dir}")

if site_packages.exists():
# Add to Python path after stdlib
sys.path.insert(0, str(site_packages))
print(f"Added to Python path: {site_packages}")

# Set PYTHONPATH environment variable
current_pythonpath = os.environ.get('PYTHONPATH', '')
if current_pythonpath:
os.environ['PYTHONPATH'] = str(site_packages) + os.pathsep + current_pythonpath
else:
os.environ['PYTHONPATH'] = str(site_packages)
current_pythonpath = os.environ.get('PYTHONPATH', '')
new_paths = str(site_packages)
if stdlib_dir.exists():
new_paths = str(stdlib_dir) + os.pathsep + new_paths
if current_pythonpath:
os.environ['PYTHONPATH'] = new_paths + os.pathsep + current_pythonpath
else:
os.environ['PYTHONPATH'] = new_paths

if scripts_dir.exists():
# Add scripts directory to PATH
Expand Down Expand Up @@ -920,11 +958,12 @@ def build_executable(args):

# Validate bundle before proceeding
print(f"\n🔍 Final bundle validation...")
required_components = [
("Scripts/python.exe", "Python executable"),
("Lib/site-packages", "Python packages"),
("pyvenv.cfg", "Virtual environment config")
]
required_components = [
("Scripts/python.exe", "Python executable"),
("Lib", "Python standard library"),
("Lib/site-packages", "Python packages"),
("pyvenv.cfg", "Virtual environment config")
]

bundle_valid = True
for component, description in required_components:
Expand Down Expand Up @@ -1026,22 +1065,7 @@ def build_executable(args):

print(f"📦 Including bundle: {bundle_dir_abs} -> venv_bundle")

# Package inclusion
if args.minimal:
# Minimal package inclusion
critical_packages = ["numpy", "cv2", "PIL", "cairo", "manim", "customtkinter", "tkinter"]
else:
# Full package inclusion
critical_packages = [
"numpy", "cv2", "PIL", "cairo", "manim", "moderngl",
"customtkinter", "jedi", "matplotlib", "tkinter"
]

for package in critical_packages:
cmd.extend([
f"--include-package={package}",
f"--include-package-data={package}"
])
# Do not compile packages directly - they are provided in venv_bundle

# Core modules
core_modules = [
Expand Down