lovepack 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lovepack-0.1.0/PKG-INFO +29 -0
- lovepack-0.1.0/README.md +3 -0
- lovepack-0.1.0/lovepack/__init__.py +0 -0
- lovepack-0.1.0/lovepack/cli.py +262 -0
- lovepack-0.1.0/pyproject.toml +32 -0
lovepack-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lovepack
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Universal LÖVE (Love2D) build engine and binary distribution manager.
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: love2d,gamedev,cli,build-tool,packaging
|
|
7
|
+
Author: OmgRod
|
|
8
|
+
Author-email: rod@omgrod.me
|
|
9
|
+
Requires-Python: >=3.11,<4.0
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
20
|
+
Classifier: Topic :: Games/Entertainment
|
|
21
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
22
|
+
Requires-Dist: urllib3 (>=2.0.0,<3.0.0)
|
|
23
|
+
Project-URL: Homepage, https://github.com/OmgRod/lovepack
|
|
24
|
+
Project-URL: Repository, https://github.com/OmgRod/lovepack
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# lovepack
|
|
28
|
+
|
|
29
|
+
I'll do the README later on.
|
lovepack-0.1.0/README.md
ADDED
|
File without changes
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import fnmatch
|
|
3
|
+
import os
|
|
4
|
+
import platform
|
|
5
|
+
import shutil
|
|
6
|
+
import sys
|
|
7
|
+
import tarfile
|
|
8
|
+
import tomllib
|
|
9
|
+
import urllib.request
|
|
10
|
+
import zipfile
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
DEFAULT_PROJECT_TOML = """[game]
|
|
14
|
+
name = "MyLoveGame"
|
|
15
|
+
version = "0.1.0"
|
|
16
|
+
author = "Developer"
|
|
17
|
+
|
|
18
|
+
[love]
|
|
19
|
+
version = "11.5"
|
|
20
|
+
|
|
21
|
+
[build]
|
|
22
|
+
output_dir = "build"
|
|
23
|
+
love_filename = "game.love"
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
DEFAULT_LOVEIGNORE = """# Git & Dev Files
|
|
27
|
+
.git*
|
|
28
|
+
.vscode/
|
|
29
|
+
.idea/
|
|
30
|
+
*.md
|
|
31
|
+
project.toml
|
|
32
|
+
.loveignore
|
|
33
|
+
|
|
34
|
+
# Build Artifacts
|
|
35
|
+
build/
|
|
36
|
+
dist/
|
|
37
|
+
*.love
|
|
38
|
+
*.exe
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
CACHE_DIR = Path.home() / ".cache" / "lovepack"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def get_platform_info():
|
|
45
|
+
system = platform.system().lower()
|
|
46
|
+
machine = platform.machine().lower()
|
|
47
|
+
|
|
48
|
+
if system == "windows":
|
|
49
|
+
os_name = "win"
|
|
50
|
+
arch = "x64" if machine in ["amd64", "x86_64"] else "x86"
|
|
51
|
+
elif system == "darwin":
|
|
52
|
+
os_name = "macos"
|
|
53
|
+
arch = "universal"
|
|
54
|
+
elif system == "linux":
|
|
55
|
+
os_name = "linux"
|
|
56
|
+
arch = "x86_64" if machine in ["amd64", "x86_64"] else "i686"
|
|
57
|
+
else:
|
|
58
|
+
raise RuntimeError(f"Unsupported operating system: {system}")
|
|
59
|
+
|
|
60
|
+
return os_name, arch
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def load_config(root_dir: Path) -> dict:
|
|
64
|
+
config_path = root_dir / "project.toml"
|
|
65
|
+
if not config_path.exists():
|
|
66
|
+
print("No project.toml found. Initializing defaults...")
|
|
67
|
+
return {
|
|
68
|
+
"game": {"name": root_dir.name, "version": "0.1.0"},
|
|
69
|
+
"love": {"version": "11.5"},
|
|
70
|
+
"build": {"output_dir": "build", "love_filename": f"{root_dir.name}.love"}
|
|
71
|
+
}
|
|
72
|
+
with open(config_path, "rb") as f:
|
|
73
|
+
return tomllib.load(f)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def load_ignore_patterns(root_dir: Path) -> list[str]:
|
|
77
|
+
ignore_path = root_dir / ".loveignore"
|
|
78
|
+
patterns = [".git*", "build/*", ".loveignore"]
|
|
79
|
+
if ignore_path.exists():
|
|
80
|
+
with open(ignore_path, "r", encoding="utf-8") as f:
|
|
81
|
+
for line in f:
|
|
82
|
+
line = line.strip()
|
|
83
|
+
if line and not line.startswith("#"):
|
|
84
|
+
patterns.append(line)
|
|
85
|
+
return patterns
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def is_ignored(rel_path: Path, patterns: list[str]) -> bool:
|
|
89
|
+
path_str = str(rel_path).replace("\\", "/")
|
|
90
|
+
for pattern in patterns:
|
|
91
|
+
if fnmatch.fnmatch(path_str, pattern) or fnmatch.fnmatch(rel_path.name, pattern):
|
|
92
|
+
return True
|
|
93
|
+
if pattern.endswith("/") and fnmatch.fnmatch(path_str + "/", pattern):
|
|
94
|
+
return True
|
|
95
|
+
return False
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def build_love_package(root_dir: Path, config: dict) -> Path:
|
|
99
|
+
build_dir = root_dir / config.get("build", {}).get("output_dir", "build")
|
|
100
|
+
build_dir.mkdir(parents=True, exist_ok=True)
|
|
101
|
+
|
|
102
|
+
game_name = config.get("game", {}).get("name", root_dir.name)
|
|
103
|
+
love_filename = config.get("build", {}).get("love_filename", f"{game_name}.love")
|
|
104
|
+
out_love_path = build_dir / love_filename
|
|
105
|
+
|
|
106
|
+
patterns = load_ignore_patterns(root_dir)
|
|
107
|
+
print(f"Packaging '{game_name}' into {out_love_path.relative_to(root_dir)}...")
|
|
108
|
+
|
|
109
|
+
packed_count = 0
|
|
110
|
+
with zipfile.ZipFile(out_love_path, "w", zipfile.ZIP_DEFLATED) as love_zip:
|
|
111
|
+
for file_path in root_dir.rglob("*"):
|
|
112
|
+
if file_path.is_file():
|
|
113
|
+
rel_path = file_path.relative_to(root_dir)
|
|
114
|
+
if build_dir in file_path.parents or file_path == out_love_path:
|
|
115
|
+
continue
|
|
116
|
+
if not is_ignored(rel_path, patterns):
|
|
117
|
+
love_zip.write(file_path, rel_path)
|
|
118
|
+
packed_count += 1
|
|
119
|
+
|
|
120
|
+
print(f"Success! Packed {packed_count} files.")
|
|
121
|
+
return out_love_path
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def download_love_binary(version: str) -> Path:
|
|
125
|
+
os_name, arch = get_platform_info()
|
|
126
|
+
CACHE_DIR.mkdir(parents=True, exist_ok=True)
|
|
127
|
+
|
|
128
|
+
if os_name == "win":
|
|
129
|
+
asset_name = f"love-{version}-win{64 if arch == 'x64' else 32}.zip"
|
|
130
|
+
elif os_name == "macos":
|
|
131
|
+
asset_name = f"love-{version}-macos.zip"
|
|
132
|
+
elif os_name == "linux":
|
|
133
|
+
asset_name = f"love-{version}-x86_64.tar.gz"
|
|
134
|
+
|
|
135
|
+
url = f"https://github.com/love2d/love/releases/download/{version}/{asset_name}"
|
|
136
|
+
target_extract = CACHE_DIR / f"love-{version}-{os_name}-{arch}"
|
|
137
|
+
|
|
138
|
+
if target_extract.exists():
|
|
139
|
+
return target_extract
|
|
140
|
+
|
|
141
|
+
print(f"Downloading LÖVE binaries ({version}) from GitHub Releases...")
|
|
142
|
+
print(f"URL: {url}")
|
|
143
|
+
|
|
144
|
+
archive_path = CACHE_DIR / asset_name
|
|
145
|
+
try:
|
|
146
|
+
urllib.request.urlretrieve(url, archive_path)
|
|
147
|
+
except Exception as e:
|
|
148
|
+
print(f"Failed to download LÖVE binaries: {e}")
|
|
149
|
+
sys.exit(1)
|
|
150
|
+
|
|
151
|
+
print("Extracting binary runtime...")
|
|
152
|
+
target_extract.mkdir(parents=True, exist_ok=True)
|
|
153
|
+
|
|
154
|
+
if asset_name.endswith(".zip"):
|
|
155
|
+
with zipfile.ZipFile(archive_path, "r") as zip_ref:
|
|
156
|
+
zip_ref.extractall(target_extract)
|
|
157
|
+
elif asset_name.endswith(".tar.gz"):
|
|
158
|
+
with tarfile.open(archive_path, "r:gz") as tar_ref:
|
|
159
|
+
tar_ref.extractall(target_extract)
|
|
160
|
+
|
|
161
|
+
archive_path.unlink()
|
|
162
|
+
return target_extract
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def build_executable(root_dir: Path, config: dict, love_file: Path):
|
|
166
|
+
version = config.get("love", {}).get("version", "11.5")
|
|
167
|
+
binary_dir = download_love_binary(version)
|
|
168
|
+
os_name, _ = get_platform_info()
|
|
169
|
+
|
|
170
|
+
build_dir = root_dir / config.get("build", {}).get("output_dir", "build")
|
|
171
|
+
game_name = config.get("game", {}).get("name", "Game")
|
|
172
|
+
dist_dir = build_dir / "dist"
|
|
173
|
+
dist_dir.mkdir(parents=True, exist_ok=True)
|
|
174
|
+
|
|
175
|
+
print(f"Fusing standalone executable for {os_name.upper()}...")
|
|
176
|
+
|
|
177
|
+
if os_name == "win":
|
|
178
|
+
love_exe = next(binary_dir.rglob("love.exe"), None)
|
|
179
|
+
if not love_exe:
|
|
180
|
+
raise FileNotFoundError("Could not locate love.exe in downloaded binary cache.")
|
|
181
|
+
|
|
182
|
+
exe_out_dir = dist_dir / f"{game_name}-win"
|
|
183
|
+
if exe_out_dir.exists():
|
|
184
|
+
shutil.rmtree(exe_out_dir)
|
|
185
|
+
|
|
186
|
+
shutil.copytree(love_exe.parent, exe_out_dir)
|
|
187
|
+
target_exe = exe_out_dir / f"{game_name}.exe"
|
|
188
|
+
|
|
189
|
+
with open(target_exe, "wb") as out_f:
|
|
190
|
+
with open(love_exe, "rb") as in_exe:
|
|
191
|
+
out_f.write(in_exe.read())
|
|
192
|
+
with open(love_file, "rb") as in_love:
|
|
193
|
+
out_f.write(in_love.read())
|
|
194
|
+
|
|
195
|
+
(exe_out_dir / "love.exe").unlink(missing_ok=True)
|
|
196
|
+
print(f"Standalone Windows distribution created: {exe_out_dir}")
|
|
197
|
+
|
|
198
|
+
elif os_name == "linux":
|
|
199
|
+
love_bin = next(binary_dir.rglob("love"), None)
|
|
200
|
+
if not love_bin:
|
|
201
|
+
raise FileNotFoundError("Could not locate love binary in downloaded cache.")
|
|
202
|
+
|
|
203
|
+
target_bin = dist_dir / game_name
|
|
204
|
+
with open(target_bin, "wb") as out_f:
|
|
205
|
+
with open(love_bin, "rb") as in_bin:
|
|
206
|
+
out_f.write(in_bin.read())
|
|
207
|
+
with open(love_file, "rb") as in_love:
|
|
208
|
+
out_f.write(in_love.read())
|
|
209
|
+
|
|
210
|
+
target_bin.chmod(0o755)
|
|
211
|
+
print(f"Standalone Linux executable created: {target_bin}")
|
|
212
|
+
|
|
213
|
+
elif os_name == "macos":
|
|
214
|
+
love_app = next(binary_dir.rglob("love.app"), None)
|
|
215
|
+
if not love_app:
|
|
216
|
+
raise FileNotFoundError("Could not locate love.app in downloaded cache.")
|
|
217
|
+
|
|
218
|
+
target_app = dist_dir / f"{game_name}.app"
|
|
219
|
+
if target_app.exists():
|
|
220
|
+
shutil.rmtree(target_app)
|
|
221
|
+
|
|
222
|
+
shutil.copytree(love_app, target_app)
|
|
223
|
+
resources_dir = target_app / "Contents" / "Resources"
|
|
224
|
+
shutil.copy(love_file, resources_dir / "game.love")
|
|
225
|
+
print(f"Standalone macOS Application Bundle created: {target_app}")
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def init_project(root_dir: Path):
|
|
229
|
+
toml_path = root_dir / "project.toml"
|
|
230
|
+
ignore_path = root_dir / ".loveignore"
|
|
231
|
+
|
|
232
|
+
if not toml_path.exists():
|
|
233
|
+
toml_path.write_text(DEFAULT_PROJECT_TOML, encoding="utf-8")
|
|
234
|
+
print("Created project.toml")
|
|
235
|
+
|
|
236
|
+
if not ignore_path.exists():
|
|
237
|
+
ignore_path.write_text(DEFAULT_LOVEIGNORE, encoding="utf-8")
|
|
238
|
+
print("Created .loveignore")
|
|
239
|
+
|
|
240
|
+
print("Initialized lovepack environment!")
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def main():
|
|
244
|
+
parser = argparse.ArgumentParser(description="lovepack: LÖVE engine packager & binary fusion utility")
|
|
245
|
+
parser.add_argument("command", choices=["init", "pack", "build"], help="Command to run")
|
|
246
|
+
args = parser.parse_args()
|
|
247
|
+
|
|
248
|
+
root_dir = Path.cwd()
|
|
249
|
+
|
|
250
|
+
if args.command == "init":
|
|
251
|
+
init_project(root_dir)
|
|
252
|
+
elif args.command == "pack":
|
|
253
|
+
config = load_config(root_dir)
|
|
254
|
+
build_love_package(root_dir, config)
|
|
255
|
+
elif args.command == "build":
|
|
256
|
+
config = load_config(root_dir)
|
|
257
|
+
love_file = build_love_package(root_dir, config)
|
|
258
|
+
build_executable(root_dir, config, love_file)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
if __name__ == "__main__":
|
|
262
|
+
main()
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "lovepack"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Universal LÖVE (Love2D) build engine and binary distribution manager."
|
|
5
|
+
authors = ["OmgRod <rod@omgrod.me>"]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
homepage = "https://github.com/OmgRod/lovepack"
|
|
9
|
+
repository = "https://github.com/OmgRod/lovepack"
|
|
10
|
+
keywords = ["love2d", "gamedev", "cli", "build-tool", "packaging"]
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 4 - Beta",
|
|
13
|
+
"Environment :: Console",
|
|
14
|
+
"Intended Audience :: Developers",
|
|
15
|
+
"License :: OSI Approved :: MIT License",
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Programming Language :: Python :: 3.11",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Topic :: Software Development :: Build Tools",
|
|
20
|
+
"Topic :: Games/Entertainment"
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[tool.poetry.dependencies]
|
|
24
|
+
python = "^3.11"
|
|
25
|
+
urllib3 = "^2.0.0"
|
|
26
|
+
|
|
27
|
+
[tool.poetry.scripts]
|
|
28
|
+
lovepack = "lovepack.cli:main"
|
|
29
|
+
|
|
30
|
+
[build-system]
|
|
31
|
+
requires = ["poetry-core>=1.0.0"]
|
|
32
|
+
build-backend = "poetry.core.masonry.api"
|