From 9f526facf471a30afc6369f6d70b3976c772f3cd Mon Sep 17 00:00:00 2001
From: Patrick Lenihan
Date: Mon, 3 Feb 2025 10:21:36 +0000
Subject: [PATCH 1/2] Added script to copy rust binaries to the Python scripts
directory (venv/bin)
---
Makefile | 2 ++
scripts/copy_rust_binaries.py | 63 +++++++++++++++++++++++++++++++++++
2 files changed, 65 insertions(+)
create mode 100644 scripts/copy_rust_binaries.py
diff --git a/Makefile b/Makefile
index 32ee5881..f1af0d0b 100644
--- a/Makefile
+++ b/Makefile
@@ -2,6 +2,7 @@
all: build
build: aw-server aw-sync
+ python scripts/copy_rust_binaries.py target/$(targetdir)
DESTDIR :=
ifeq ($(SUDO_USER),)
@@ -107,4 +108,5 @@ install:
install -m 644 aw-server.service $(DESTDIR)$(PREFIX)/lib/systemd/user/aw-server.service
clean:
+ python scripts/copy_rust_binaries.py --clean target/$(targetdir)
cargo clean
diff --git a/scripts/copy_rust_binaries.py b/scripts/copy_rust_binaries.py
new file mode 100644
index 00000000..cf81efeb
--- /dev/null
+++ b/scripts/copy_rust_binaries.py
@@ -0,0 +1,63 @@
+import argparse
+import sysconfig
+import shutil
+import os
+import sys
+
+def add_rust_prefix(p):
+ base, ext = os.path.splitext(p)
+ return f"{base}-rust{ext}"
+
+def is_executable(p):
+ """ Cross-platform check for executable files. """
+ if os.path.isfile(p) and os.access(p, os.X_OK):
+ return True
+ return os.name == "nt" and p.lower().endswith((".exe", ".bat", ".cmd", ".com"))
+
+def build(target_dir, python_bin_dir):
+
+ if not os.path.exists(target_dir):
+ print(f"Error: {target_dir} does not exist. Did you run `cargo build --release`?", file=sys.stderr)
+ sys.exit(1)
+
+ os.makedirs(python_bin_dir, exist_ok=True)
+
+ for file_name in os.listdir(target_dir):
+ src_file = os.path.join(target_dir, file_name)
+ dst_file = add_rust_prefix(os.path.join(python_bin_dir, file_name))
+
+ if is_executable(src_file):
+ shutil.copy(src_file, dst_file)
+
+def clean(target_dir, python_bin_dir):
+ if not os.path.exists(python_bin_dir) or not os.path.exists(target_dir):
+ return
+
+ for file_name in os.listdir(target_dir):
+ dst_file = add_rust_prefix(os.path.join(python_bin_dir, file_name))
+ if is_executable(dst_file):
+ os.remove(dst_file)
+
+def main(args):
+
+ python_bin_dir = sysconfig.get_path("scripts")
+
+ if not python_bin_dir:
+ python_bin_dir = os.path.dirname(sys.executable)
+
+ if args.clean:
+ clean(args.target_dir, python_bin_dir)
+ else:
+ build(args.target_dir, python_bin_dir)
+
+def parse_args():
+ parser = argparse.ArgumentParser()
+
+ parser.add_argument("--clean", action='store_true', default=False)
+ parser.add_argument("target_dir", type=str, default=os.path.join("target", "release"), nargs='?')
+
+ return parser.parse_args()
+
+if __name__ == "__main__":
+ args = parse_args()
+ main(args)
From 691a4ab250f147868ebae88254f9ad1dfd4d9f4a Mon Sep 17 00:00:00 2001
From: Patrick Lenihan
Date: Mon, 3 Feb 2025 17:16:44 +0000
Subject: [PATCH 2/2] Switched to using Poetry script
---
Makefile | 4 +--
python/aw_server_rust.py | 31 +++++++++++++++++
python/poetry.lock | 7 ++++
python/pyproject.toml | 18 ++++++++++
scripts/copy_rust_binaries.py | 63 -----------------------------------
5 files changed, 58 insertions(+), 65 deletions(-)
create mode 100644 python/aw_server_rust.py
create mode 100644 python/poetry.lock
create mode 100644 python/pyproject.toml
delete mode 100644 scripts/copy_rust_binaries.py
diff --git a/Makefile b/Makefile
index f1af0d0b..e66478d7 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,6 @@
all: build
build: aw-server aw-sync
- python scripts/copy_rust_binaries.py target/$(targetdir)
DESTDIR :=
ifeq ($(SUDO_USER),)
@@ -23,6 +22,7 @@ endif
aw-server: set-version aw-webui
cargo build $(cargoflag) --bin aw-server
+ poetry install --directory python
aw-sync: set-version
cargo build $(cargoflag) --bin aw-sync
@@ -108,5 +108,5 @@ install:
install -m 644 aw-server.service $(DESTDIR)$(PREFIX)/lib/systemd/user/aw-server.service
clean:
- python scripts/copy_rust_binaries.py --clean target/$(targetdir)
+ rm -rf __pycache__ python/__pycache__
cargo clean
diff --git a/python/aw_server_rust.py b/python/aw_server_rust.py
new file mode 100644
index 00000000..e5e7e0cc
--- /dev/null
+++ b/python/aw_server_rust.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python3
+
+import os
+import sys
+import subprocess
+
+def find_rust_binary():
+ """Finds the Rust binary in the target directory."""
+
+ repo_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+ target = "debug" if os.getenv("RELEASE", "") == "false" else "release"
+
+ rust_binary = os.path.join(repo_root, "target", target, "aw-server")
+
+ if os.path.exists(rust_binary):
+ return rust_binary
+
+ print(f"Error: Rust binary '{rust_binary}' not found. Did you run `cargo build --release`?", file=sys.stderr)
+ sys.exit(1)
+
+def main():
+ """Executes the Rust binary and forwards all arguments."""
+ rust_binary = find_rust_binary()
+ if os.name == "posix":
+ # Replace current Python process with rust binary
+ os.execvp(rust_binary, (rust_binary, *sys.argv[1:]))
+ else:
+ subprocess.run([rust_binary] + sys.argv[1:])
+
+if __name__ == "__main__":
+ main()
diff --git a/python/poetry.lock b/python/poetry.lock
new file mode 100644
index 00000000..d196c8d4
--- /dev/null
+++ b/python/poetry.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand.
+package = []
+
+[metadata]
+lock-version = "2.1"
+python-versions = "*"
+content-hash = "1be3afc87beb00627d70ba9087279825e941f05a562df12e972061262422d79a"
diff --git a/python/pyproject.toml b/python/pyproject.toml
new file mode 100644
index 00000000..ca30a954
--- /dev/null
+++ b/python/pyproject.toml
@@ -0,0 +1,18 @@
+[tool.poetry]
+name = "aw-server-rust"
+version = "0.1.0"
+description = "Server for ActivityWatch"
+authors = ["Erik Bjäreholt "]
+license = "MPL-2.0"
+packages = [
+ { include = "aw_server_rust.py" }
+]
+
+[tool.poetry.scripts]
+aw-server-rust = "aw_server_rust:main"
+
+[tool.poetry.dependencies]
+
+[build-system]
+requires = ["poetry-core"]
+build-backend = "poetry.core.masonry.api"
diff --git a/scripts/copy_rust_binaries.py b/scripts/copy_rust_binaries.py
deleted file mode 100644
index cf81efeb..00000000
--- a/scripts/copy_rust_binaries.py
+++ /dev/null
@@ -1,63 +0,0 @@
-import argparse
-import sysconfig
-import shutil
-import os
-import sys
-
-def add_rust_prefix(p):
- base, ext = os.path.splitext(p)
- return f"{base}-rust{ext}"
-
-def is_executable(p):
- """ Cross-platform check for executable files. """
- if os.path.isfile(p) and os.access(p, os.X_OK):
- return True
- return os.name == "nt" and p.lower().endswith((".exe", ".bat", ".cmd", ".com"))
-
-def build(target_dir, python_bin_dir):
-
- if not os.path.exists(target_dir):
- print(f"Error: {target_dir} does not exist. Did you run `cargo build --release`?", file=sys.stderr)
- sys.exit(1)
-
- os.makedirs(python_bin_dir, exist_ok=True)
-
- for file_name in os.listdir(target_dir):
- src_file = os.path.join(target_dir, file_name)
- dst_file = add_rust_prefix(os.path.join(python_bin_dir, file_name))
-
- if is_executable(src_file):
- shutil.copy(src_file, dst_file)
-
-def clean(target_dir, python_bin_dir):
- if not os.path.exists(python_bin_dir) or not os.path.exists(target_dir):
- return
-
- for file_name in os.listdir(target_dir):
- dst_file = add_rust_prefix(os.path.join(python_bin_dir, file_name))
- if is_executable(dst_file):
- os.remove(dst_file)
-
-def main(args):
-
- python_bin_dir = sysconfig.get_path("scripts")
-
- if not python_bin_dir:
- python_bin_dir = os.path.dirname(sys.executable)
-
- if args.clean:
- clean(args.target_dir, python_bin_dir)
- else:
- build(args.target_dir, python_bin_dir)
-
-def parse_args():
- parser = argparse.ArgumentParser()
-
- parser.add_argument("--clean", action='store_true', default=False)
- parser.add_argument("target_dir", type=str, default=os.path.join("target", "release"), nargs='?')
-
- return parser.parse_args()
-
-if __name__ == "__main__":
- args = parse_args()
- main(args)