Skip to content
This repository was archived by the owner on Oct 12, 2026. It is now read-only.

Commit 8d5ea3e

Browse files
committed
BUILD: Add AppImage packaging support for Linux releases.
Closes #2
1 parent e10164e commit 8d5ea3e

7 files changed

Lines changed: 150 additions & 7 deletions

File tree

‎.gitignore‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
.idea/
2+
build/
3+
release/
24
cmake-build-debug**/
35
venv/
46
**__pycache__/

‎README.md‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,19 @@ SUBCOMMANDS:
117117

118118
1. [GNU Emacs](./editor-plugins/emacs)
119119

120+
## Linux Releases
121+
122+
Prebuilt Linux releases are distributed as **AppImages**, allowing the interpreter to run on most modern Linux distributions without installation.
123+
124+
After downloading an AppImage, make it executable and run it:
125+
126+
```shell
127+
chmod +x Lambda_Discipline-*.AppImage
128+
./Lambda_Discipline-*.AppImage --help
129+
```
130+
131+
> You can also integrate the AppImage with your desktop environment using tools such as `AppImageLauncher`, or simply keep it as a portable executable.
132+
120133
## Dependencies
121134

122135
1. [fmt](https://github.com/fmtlib/fmt.git): String formatting.

‎make.py‎

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ def subcommand_setup():
3535
build_dir.mkdir(parents=True, exist_ok=True)
3636

3737
subprocess.run([sys.executable, "-m", "venv", str(venv_dir)], check=True)
38-
subprocess.run([python(), "-m", "pip", "install", "-r", "requirements.dev.txt"], check=True)
38+
subprocess.run(
39+
[python(), "-m", "pip", "install", "-r", "requirements.dev.txt"], check=True
40+
)
3941

4042
try:
4143
subprocess.run(["cmake", "-S", ".", "-B", str(build_dir)], check=True)
@@ -49,17 +51,32 @@ def subcommand_build():
4951

5052

5153
def subcommand_test():
52-
subprocess.run([python(), "-m", "pytest", f'--build-dir={BUILD_DIR}', "-v"], cwd=ROOT, check=True)
54+
subprocess.run(
55+
[python(), "-m", "pytest", f"--build-dir={BUILD_DIR}", "-v"],
56+
cwd=ROOT,
57+
check=True,
58+
)
5359

5460

5561
if __name__ == "__main__":
56-
parser = argparse.ArgumentParser(prog="make.py", description="Lambda Discipline build tool")
57-
parser.add_argument("--build-dir", default="cmake-build-debug", help="CMake build directory (default: %(default)s)")
58-
parser.add_argument("--venv-dir", default="venv",
59-
help="Python virtual environment directory (default: %(default)s)")
62+
parser = argparse.ArgumentParser(
63+
prog="make.py", description="Lambda Discipline build tool"
64+
)
65+
parser.add_argument(
66+
"--build-dir",
67+
default="cmake-build-debug",
68+
help="CMake build directory (default: %(default)s)",
69+
)
70+
parser.add_argument(
71+
"--venv-dir",
72+
default="venv",
73+
help="Python virtual environment directory (default: %(default)s)",
74+
)
6075

6176
subparsers = parser.add_subparsers(dest="command", required=True)
62-
subparsers.add_parser("setup", help="Create venv, install dependencies, and configure CMake")
77+
subparsers.add_parser(
78+
"setup", help="Create venv, install dependencies, and configure CMake"
79+
)
6380
subparsers.add_parser("build", help="Build the project")
6481
subparsers.add_parser("test", help="Run tests")
6582

‎packaging/appimage/AppRun‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/usr/bin/env bash
2+
3+
HERE="$(dirname "$(readlink -f "$0")")"
4+
exec "$HERE/lbd" "$@"
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[Desktop Entry]
2+
Type=Application
3+
Version=1.0
4+
Name=Lambda Discipline
5+
Comment=Functional programming language based on Lambda Calculus
6+
Exec=lbd
7+
Icon=fun.ninth.lambda-discipline
8+
Terminal=true
9+
Categories=Development;
10+
StartupNotify=true
523 KB
Loading

‎scripts/build-appimage.py‎

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#!/usr/bin/env python3
2+
3+
from __future__ import annotations
4+
import argparse
5+
import shutil
6+
import subprocess
7+
from pathlib import Path
8+
import sys
9+
10+
APP_ID = "fun.ninth.lambda-discipline"
11+
EXECUTABLE = "lbd"
12+
13+
14+
def run(*args: str) -> None:
15+
subprocess.run(args, check=True)
16+
17+
18+
def copy_file(src: Path, dst: Path) -> None:
19+
if not src.exists():
20+
sys.exit(f"[ERROR] Path '{src}' does not exist")
21+
22+
shutil.copy2(src, dst)
23+
24+
25+
def get_appimagetool() -> str:
26+
appimagetool = shutil.which("appimagetool")
27+
28+
if appimagetool is None:
29+
appimagetool = shutil.which("appimagetool-x86_64.AppImage")
30+
31+
if appimagetool is None:
32+
sys.exit(
33+
"[ERROR] Could not find 'appimagetool' or 'appimagetool-x86_64.AppImage' in PATH."
34+
)
35+
36+
return appimagetool
37+
38+
39+
def main() -> int:
40+
parser = argparse.ArgumentParser(
41+
description="Create an AppImage from an existing CMake build.",
42+
epilog=(
43+
"Example:\n build-appimage.py build\n build-appimage.py build-release"
44+
),
45+
formatter_class=argparse.RawDescriptionHelpFormatter,
46+
)
47+
48+
parser.add_argument(
49+
"build_dir",
50+
type=Path,
51+
metavar="BUILD_DIR",
52+
help="Path to the CMake build directory.",
53+
)
54+
55+
parser.add_argument(
56+
"release_dir",
57+
type=Path,
58+
metavar="RELEASE_DIR",
59+
help="Directory where the AppDir and AppImage will be created.",
60+
)
61+
62+
args = parser.parse_args()
63+
64+
build_dir = args.build_dir.resolve()
65+
release_dir = args.release_dir.resolve()
66+
67+
executable = build_dir / EXECUTABLE
68+
appdir = release_dir / "AppDir"
69+
70+
if not executable.exists():
71+
sys.exit(f"[ERROR] Executable '{executable}' not found")
72+
73+
if appdir.exists():
74+
shutil.rmtree(appdir)
75+
appdir.mkdir(parents=True, exist_ok=True)
76+
77+
packaging = Path("packaging/appimage")
78+
79+
copy_file(packaging / "AppRun", appdir / "AppRun")
80+
copy_file(packaging / f"{APP_ID}.desktop", appdir / f"{APP_ID}.desktop")
81+
copy_file(packaging / f"{APP_ID}.png", appdir / f"{APP_ID}.png")
82+
copy_file(executable, appdir / EXECUTABLE)
83+
84+
(appdir / "AppRun").chmod(0o755)
85+
(appdir / EXECUTABLE).chmod(0o755)
86+
87+
run(
88+
get_appimagetool(),
89+
str(appdir),
90+
str(release_dir / "Lambda_Discipline-x86_64.AppImage")
91+
)
92+
93+
return 0
94+
95+
96+
if __name__ == "__main__":
97+
raise SystemExit(main())

0 commit comments

Comments
 (0)