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.
@@ -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.
@@ -0,0 +1,3 @@
1
+ # lovepack
2
+
3
+ I'll do the README later on.
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"