-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbuild_executable.py
More file actions
160 lines (132 loc) · 4.49 KB
/
build_executable.py
File metadata and controls
160 lines (132 loc) · 4.49 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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#!/usr/bin/env python3
"""
Build script to create a standalone executable using PyInstaller.
This allows distribution without requiring Python to be installed.
"""
import shutil
import subprocess
import sys
from pathlib import Path
def run_command(cmd, description):
"""Run a command and handle errors."""
print(f"[*] {description}...")
try:
if isinstance(cmd, list):
result = subprocess.run(cmd, check=True, capture_output=True, text=True)
else:
result = subprocess.run(
cmd, shell=True, check=True, capture_output=True, text=True
)
if result.stdout.strip():
print(f"[+] {result.stdout.strip()}")
return True
except subprocess.CalledProcessError as e:
print(f"[-] Error: {e}")
if e.stderr:
print(f"Details: {e.stderr}")
return False
def clean_build_dirs():
"""Clean previous build artifacts (but preserve packages)."""
# Only clean the build directory and PyInstaller artifacts
# Keep the packages from uv build
dirs_to_clean = ["build"]
for dir_name in dirs_to_clean:
dir_path = Path(dir_name)
if dir_path.exists():
print(f"[*] Cleaning {dir_name}/")
shutil.rmtree(dir_path)
# Remove only the executable from dist, not the packages
dist_path = Path("dist")
if dist_path.exists():
exe_path = dist_path / "trello-tools.exe"
if exe_path.exists():
print("[*] Removing old executable")
exe_path.unlink()
def build_executable():
"""Build a standalone executable using PyInstaller."""
# Clean previous builds
clean_build_dirs()
# Create a more comprehensive build command using uv run
cmd = [
"uv",
"run",
"pyinstaller",
"--onefile", # Single executable file
"--name",
"trello-tools", # Executable name
"--console", # Console application
"--clean", # Clean build cache
"--noconfirm", # Overwrite output directory
"--add-data",
"src;src", # Include source code
"--add-data",
"README.md;.", # Include README
"--hidden-import",
"pkg_resources.extern", # Fix potential import issues
"--hidden-import",
"trello_cli.database",
"--hidden-import",
"trello_cli.models",
"--hidden-import",
"trello_cli.trello",
"trello_tools_main.py", # Entry point
]
if not run_command(cmd, "Building standalone executable"):
return False
# Check if executable was created
exe_path = Path("dist/trello-tools.exe")
if exe_path.exists():
size_mb = exe_path.stat().st_size / (1024 * 1024)
print("[+] Executable built successfully!")
print(f"[*] Location: {exe_path.absolute()}")
print(f"[*] Size: {size_mb:.1f} MB")
return True
else:
print(f"[-] Executable not found at {exe_path}")
return False
def build_packages():
"""Build Python packages using uv."""
return run_command("uv build", "Building Python packages")
def main():
"""Main build function."""
import argparse
parser = argparse.ArgumentParser(
description="Build trello-tools executable and packages"
)
parser.add_argument("--exe-only", action="store_true", help="Build executable only")
parser.add_argument(
"--packages-only", action="store_true", help="Build packages only"
)
args = parser.parse_args()
success = True
if args.packages_only:
success = build_packages()
elif args.exe_only:
success = build_executable()
else:
# Build both by default
print("[*] Building trello-tools executable and packages...")
print()
# Build packages first
if not build_packages():
success = False
print()
# Build executable
if not build_executable():
success = False
if success:
print("\n[+] Build completed successfully!")
print("\n[*] Distribution files:")
dist_path = Path("dist")
if dist_path.exists():
for file in sorted(dist_path.iterdir()):
if file.is_file():
size_mb = file.stat().st_size / (1024 * 1024)
print(f" [*] {file.name} ({size_mb:.1f} MB)")
else:
print("\n[-] Build failed!")
return False
return True
if __name__ == "__main__":
success = main()
sys.exit(0 if success else 1)