-
-
Notifications
You must be signed in to change notification settings - Fork 447
Expand file tree
/
Copy pathsetup_pyi.py
More file actions
125 lines (100 loc) · 3.33 KB
/
setup_pyi.py
File metadata and controls
125 lines (100 loc) · 3.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#!/usr/bin/env python
import os
import shutil
import sys
from pathlib import Path
if sys.version_info[:2] < (3, 9):
raise RuntimeError("This app only supports Python 3.9 and later.")
ROOT = Path(__file__).resolve().parent
# Determine venv directory: use VIRTUAL_ENV if set, otherwise detect based on OS
VENV_DIR = os.getenv("VIRTUAL_ENV")
if VENV_DIR:
VENV_DIR = Path(VENV_DIR).relative_to(ROOT).as_posix()
elif (ROOT / ".venv-win").exists():
VENV_DIR = ".venv-win"
elif (ROOT / ".venv-posix").exists():
VENV_DIR = ".venv-posix"
else:
VENV_DIR = ".venv"
AVAILABLE_SITE_PACKAGES = list(ROOT.glob(f"{VENV_DIR}/**/site-packages"))
if not AVAILABLE_SITE_PACKAGES:
raise RuntimeError(f"No site-packages found in {VENV_DIR}")
SITE_PACKAGES = AVAILABLE_SITE_PACKAGES[0]
DIST_DIR = ROOT / "dist"
SPEC_DIR = ROOT / "windows"
BUILD_DIR = SPEC_DIR / "build"
def build_command():
command = [
str(ROOT / "lncrawl" / "__main__.py"),
"--noconsole",
"--onefile",
"--clean",
"--noconfirm",
"--name=lncrawl",
f"--icon={ROOT / 'res' / 'lncrawl.ico'}",
f"--distpath={DIST_DIR}",
f"--specpath={SPEC_DIR}",
f"--workpath={BUILD_DIR}",
]
command += gather_data_files()
command += gather_hidden_imports()
command += gather_excluded_modules()
return command
def gather_data_files():
file_map = {
ROOT / "lncrawl": "lncrawl",
ROOT / "sources": "sources",
SITE_PACKAGES / "wcwidth" / "version.json": "wcwidth",
SITE_PACKAGES / "text_unidecode" / "data.bin": "text_unidecode",
}
results = []
for src, dst in file_map.items():
if src.exists():
results.extend(["--add-data", f"{src.as_posix()}:{dst}"])
return results
def gather_hidden_imports():
hidden = [
"passlib.handlers.argon2",
"selenium.webdriver.chrome.options",
]
for py_file in (ROOT / "sources").rglob("*.py"):
rel_path = str(py_file.relative_to(ROOT / "sources"))
if all(x[0].isalnum() for x in rel_path.split(os.sep)):
module = "sources." + rel_path[:-3].replace(os.sep, ".")
hidden.append(module)
return [f"--hidden-import={module}" for module in hidden]
def gather_excluded_modules():
exclude = [
"pip",
"wheel",
"altgraph",
"macholib",
"pyinstaller",
"pkg_resources",
"pyinstaller-hooks-contrib",
]
return [flag for mod in exclude for flag in ["--exclude-module", mod]]
def package():
command = build_command()
print("🔧 Running PyInstaller:")
print(" ".join(command))
print("-" * 60)
# Cleanup and prepare build directory
shutil.rmtree(SPEC_DIR, ignore_errors=True)
SPEC_DIR.mkdir(parents=True, exist_ok=True)
# Run PyInstaller
from PyInstaller import __main__ as pyi # type: ignore
pyi.run(command)
# Cleanup temp build dir
shutil.rmtree(BUILD_DIR, ignore_errors=True)
# Final output confirmation
OUTPUT_EXE = DIST_DIR / "lncrawl.exe"
OUTPUT_POSIX = DIST_DIR / "lncrawl"
if OUTPUT_EXE.is_file():
print(f"✅ Executable created: {OUTPUT_EXE}")
elif OUTPUT_POSIX.is_file():
print(f"✅ Executable created: {OUTPUT_POSIX}")
else:
print("❌ Build failed: Output not found.")
if __name__ == "__main__":
package()