Update install_rtorrent.py
This commit is contained in:
@@ -1,21 +1,25 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import argparse
|
import argparse
|
||||||
|
import itertools
|
||||||
import os
|
import os
|
||||||
import pwd
|
import pwd
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_USER = "rtorrent"
|
DEFAULT_USER = "rtorrent"
|
||||||
DEFAULT_GROUP = "rtorrent"
|
DEFAULT_GROUP = "rtorrent"
|
||||||
DEFAULT_HOME = "/home/rtorrent"
|
DEFAULT_HOME = "/home/rtorrent"
|
||||||
DEFAULT_BASE_DIR = "/opt/rtorrent_build"
|
DEFAULT_BASE_DIR = "/opt/rtorrent_build"
|
||||||
DEFAULT_LIBTORRENT_REF = "master"
|
DEFAULT_LIBTORRENT_REF = "v0.15.7"
|
||||||
DEFAULT_RTORRENT_REF = "master"
|
DEFAULT_RTORRENT_REF = "v0.15.7"
|
||||||
DEFAULT_XMLRPC_REF = "latest-stable"
|
DEFAULT_XMLRPC_REF = "latest-stable"
|
||||||
|
DEFAULT_CARES_REF = "1.34.6"
|
||||||
|
DEFAULT_CURL_REF = "8.19.0"
|
||||||
DEFAULT_SERVICE_PATH = "/etc/systemd/system/rtorrent@.service"
|
DEFAULT_SERVICE_PATH = "/etc/systemd/system/rtorrent@.service"
|
||||||
DEFAULT_SCGI_PORT = 5000
|
DEFAULT_SCGI_PORT = 5000
|
||||||
DEFAULT_TORRENT_PORT = 51300
|
DEFAULT_TORRENT_PORT = 51300
|
||||||
@@ -25,29 +29,61 @@ class InstallError(Exception):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def run(cmd, *, cwd=None, env=None, check=True):
|
class Spinner:
|
||||||
|
FRAMES = ["|", "/", "-", "\\"]
|
||||||
|
|
||||||
|
def __init__(self, message, enabled=True):
|
||||||
|
self.message = message
|
||||||
|
self.enabled = enabled and sys.stdout.isatty()
|
||||||
|
self._stop = threading.Event()
|
||||||
|
self._thread = None
|
||||||
|
self._start = None
|
||||||
|
|
||||||
|
def _run(self):
|
||||||
|
for frame in itertools.cycle(self.FRAMES):
|
||||||
|
if self._stop.is_set():
|
||||||
|
break
|
||||||
|
elapsed = time.time() - self._start
|
||||||
|
sys.stdout.write(f"\r[ {frame} ] {self.message} ({elapsed:.1f}s)")
|
||||||
|
sys.stdout.flush()
|
||||||
|
time.sleep(0.12)
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
self._start = time.time()
|
||||||
|
if self.enabled:
|
||||||
|
self._thread = threading.Thread(target=self._run, daemon=True)
|
||||||
|
self._thread.start()
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc, tb):
|
||||||
|
elapsed = time.time() - self._start
|
||||||
|
if self.enabled:
|
||||||
|
self._stop.set()
|
||||||
|
self._thread.join(timeout=0.5)
|
||||||
|
status = "ERR" if exc else "OK "
|
||||||
|
sys.stdout.write(f"\r[ {status} ] {self.message} ({elapsed:.1f}s)\n")
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
|
||||||
|
def run(cmd, *, cwd=None, env=None, check=True, debug=False, capture_output=False):
|
||||||
|
if debug:
|
||||||
print(f"\n>>> {' '.join(cmd)}")
|
print(f"\n>>> {' '.join(cmd)}")
|
||||||
result = subprocess.run(cmd, cwd=cwd, env=env, check=False)
|
stdout = subprocess.PIPE if capture_output else (None if debug else subprocess.DEVNULL)
|
||||||
|
stderr = subprocess.PIPE if capture_output else (None if debug else subprocess.DEVNULL)
|
||||||
|
result = subprocess.run(cmd, cwd=cwd, env=env, check=False, text=True, stdout=stdout, stderr=stderr)
|
||||||
if check and result.returncode != 0:
|
if check and result.returncode != 0:
|
||||||
raise InstallError(f"Command failed with exit code {result.returncode}: {' '.join(cmd)}")
|
stderr_text = ""
|
||||||
return result.returncode
|
if capture_output and result.stderr:
|
||||||
|
stderr_text = f"\n{result.stderr.strip()}"
|
||||||
|
raise InstallError(f"Command failed with exit code {result.returncode}: {' '.join(cmd)}{stderr_text}")
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
def capture(cmd, *, cwd=None, env=None, check=True):
|
def capture(cmd, **kwargs):
|
||||||
result = subprocess.run(
|
result = run(cmd, capture_output=True, **kwargs)
|
||||||
cmd,
|
out = (result.stdout or "").strip()
|
||||||
cwd=cwd,
|
err = (result.stderr or "").strip()
|
||||||
env=env,
|
return out if out else err
|
||||||
check=False,
|
|
||||||
text=True,
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.PIPE,
|
|
||||||
)
|
|
||||||
if check and result.returncode != 0:
|
|
||||||
raise InstallError(
|
|
||||||
f"Command failed with exit code {result.returncode}: {' '.join(cmd)}\n{result.stderr.strip()}"
|
|
||||||
)
|
|
||||||
return result.stdout.strip()
|
|
||||||
|
|
||||||
|
|
||||||
def require_root():
|
def require_root():
|
||||||
@@ -63,8 +99,8 @@ def detect_debian():
|
|||||||
data = {}
|
data = {}
|
||||||
for line in os_release.read_text().splitlines():
|
for line in os_release.read_text().splitlines():
|
||||||
if "=" in line:
|
if "=" in line:
|
||||||
key, value = line.split("=", 1)
|
k, v = line.split("=", 1)
|
||||||
data[key] = value.strip().strip('"')
|
data[k] = v.strip().strip('"')
|
||||||
|
|
||||||
distro_id = data.get("ID", "").lower()
|
distro_id = data.get("ID", "").lower()
|
||||||
distro_like = data.get("ID_LIKE", "").lower()
|
distro_like = data.get("ID_LIKE", "").lower()
|
||||||
@@ -99,11 +135,11 @@ def parse_version(version):
|
|||||||
return tuple(parts[:3]) if parts else (0,)
|
return tuple(parts[:3]) if parts else (0,)
|
||||||
|
|
||||||
|
|
||||||
def ensure_packages(packages):
|
def ensure_packages(packages, *, debug=False):
|
||||||
print("Updating APT metadata...")
|
print("Updating APT metadata...")
|
||||||
run(["apt-get", "update"])
|
run(["apt-get", "update"], debug=debug)
|
||||||
print("Installing build and runtime dependencies...")
|
print("Installing build and runtime dependencies...")
|
||||||
run(["apt-get", "install", "-y", *packages])
|
run(["apt-get", "install", "-y", *packages], debug=debug)
|
||||||
|
|
||||||
|
|
||||||
def ensure_dir(path, owner=None, group=None, mode=None):
|
def ensure_dir(path, owner=None, group=None, mode=None):
|
||||||
@@ -114,14 +150,14 @@ def ensure_dir(path, owner=None, group=None, mode=None):
|
|||||||
os.chmod(path, mode)
|
os.chmod(path, mode)
|
||||||
|
|
||||||
|
|
||||||
def create_system_user(user, group, home, assume_yes=False):
|
def create_system_user(user, group, home, assume_yes=False, debug=False):
|
||||||
try:
|
try:
|
||||||
pwd.getpwnam(user)
|
pwd.getpwnam(user)
|
||||||
print(f"User '{user}' already exists.")
|
print(f"User '{user}' already exists.")
|
||||||
except KeyError:
|
except KeyError:
|
||||||
if not prompt_yes_no(f"Create system user '{user}' with home '{home}'?", default=True, assume_yes=assume_yes):
|
if not prompt_yes_no(f"Create system user '{user}' with home '{home}'?", default=True, assume_yes=assume_yes):
|
||||||
raise InstallError("User creation declined.")
|
raise InstallError("User creation declined.")
|
||||||
run(["groupadd", "--system", group], check=False)
|
run(["groupadd", "--system", group], check=False, debug=debug)
|
||||||
run([
|
run([
|
||||||
"useradd",
|
"useradd",
|
||||||
"--system",
|
"--system",
|
||||||
@@ -130,24 +166,31 @@ def create_system_user(user, group, home, assume_yes=False):
|
|||||||
"--shell", "/usr/sbin/nologin",
|
"--shell", "/usr/sbin/nologin",
|
||||||
"--gid", group,
|
"--gid", group,
|
||||||
user,
|
user,
|
||||||
])
|
], debug=debug)
|
||||||
|
|
||||||
|
|
||||||
def clone_or_update_repo(repo_url, repo_dir, ref):
|
def clone_or_update_repo(repo_url, repo_dir, ref, *, debug=False):
|
||||||
repo_dir = Path(repo_dir)
|
repo_dir = Path(repo_dir)
|
||||||
if not repo_dir.exists():
|
if not repo_dir.exists():
|
||||||
run(["git", "clone", repo_url, str(repo_dir)])
|
with Spinner(f"Cloning {repo_dir.name}", enabled=not debug):
|
||||||
|
run(["git", "clone", repo_url, str(repo_dir)], debug=debug)
|
||||||
else:
|
else:
|
||||||
print(f"Repository already exists: {repo_dir}")
|
print(f"Repository already exists: {repo_dir}")
|
||||||
run(["git", "fetch", "--all", "--tags"], cwd=str(repo_dir))
|
with Spinner(f"Checking out {repo_dir.name} -> {ref}", enabled=not debug):
|
||||||
|
run(["git", "fetch", "--all", "--tags"], cwd=str(repo_dir), debug=debug)
|
||||||
run(["git", "fetch", "--all", "--tags"], cwd=str(repo_dir))
|
run(["git", "checkout", ref], cwd=str(repo_dir), debug=debug)
|
||||||
run(["git", "checkout", ref], cwd=str(repo_dir))
|
run(["git", "pull", "--ff-only"], cwd=str(repo_dir), check=False, debug=debug)
|
||||||
run(["git", "pull", "--ff-only"], cwd=str(repo_dir), check=False)
|
|
||||||
|
|
||||||
|
|
||||||
def download_file(url, destination):
|
def download_file(url, destination, *, debug=False):
|
||||||
run(["curl", "-fL", url, "-o", str(destination)])
|
run(["curl", "-fL", url, "-o", str(destination)], debug=debug)
|
||||||
|
|
||||||
|
|
||||||
|
def extract_tarball(tarball, destination, *, debug=False):
|
||||||
|
if destination.exists():
|
||||||
|
shutil.rmtree(destination)
|
||||||
|
destination.mkdir(parents=True, exist_ok=True)
|
||||||
|
run(["tar", "-xzf", str(tarball), "-C", str(destination), "--strip-components=1"], debug=debug)
|
||||||
|
|
||||||
|
|
||||||
def find_xmlrpc_config(base_dir, preferred_install=None):
|
def find_xmlrpc_config(base_dir, preferred_install=None):
|
||||||
@@ -180,20 +223,54 @@ def find_xmlrpc_config(base_dir, preferred_install=None):
|
|||||||
return unique[0] if unique else None
|
return unique[0] if unique else None
|
||||||
|
|
||||||
|
|
||||||
def verify_xmlrpc_environment(xmlrpc_config_path):
|
def verify_xmlrpc_environment(xmlrpc_config_path, *, debug=False):
|
||||||
tool = Path(xmlrpc_config_path)
|
tool = Path(xmlrpc_config_path)
|
||||||
if not tool.exists():
|
if not tool.exists():
|
||||||
raise InstallError(f"xmlrpc-c-config was not found: {tool}")
|
raise InstallError(f"xmlrpc-c-config was not found: {tool}")
|
||||||
|
version = capture([str(tool), "--version"], check=True, debug=debug)
|
||||||
version = capture([str(tool), "--version"], check=True)
|
|
||||||
if parse_version(version) < (1, 11):
|
if parse_version(version) < (1, 11):
|
||||||
raise InstallError(
|
raise InstallError(f"xmlrpc-c version is too old: {version}. Version 1.11 or newer is required.")
|
||||||
f"xmlrpc-c version is too old: {version}. Version 1.11 or newer is required."
|
|
||||||
)
|
|
||||||
print(f"Detected xmlrpc-c version: {version} ({tool})")
|
print(f"Detected xmlrpc-c version: {version} ({tool})")
|
||||||
|
return version
|
||||||
|
|
||||||
|
|
||||||
def build_xmlrpc_c(base_dir, xmlrpc_ref):
|
def build_env(*prefixes, extra_env=None):
|
||||||
|
env = os.environ.copy()
|
||||||
|
include_dirs = []
|
||||||
|
lib_dirs = []
|
||||||
|
pkg_dirs = []
|
||||||
|
bin_dirs = []
|
||||||
|
|
||||||
|
for prefix in prefixes:
|
||||||
|
if not prefix:
|
||||||
|
continue
|
||||||
|
prefix = str(prefix)
|
||||||
|
include_dirs.append(f"-I{prefix}/include")
|
||||||
|
lib_dirs.append(f"-L{prefix}/lib")
|
||||||
|
pkg_dirs.append(f"{prefix}/lib/pkgconfig")
|
||||||
|
bin_dirs.append(f"{prefix}/bin")
|
||||||
|
|
||||||
|
if include_dirs:
|
||||||
|
env["CPPFLAGS"] = " ".join(include_dirs + [env.get("CPPFLAGS", "")]).strip()
|
||||||
|
env["CFLAGS"] = " ".join(include_dirs + [env.get("CFLAGS", "")]).strip()
|
||||||
|
|
||||||
|
if lib_dirs:
|
||||||
|
rpaths = [f"-Wl,-rpath,{d[2:]}" for d in lib_dirs]
|
||||||
|
env["LDFLAGS"] = " ".join(lib_dirs + rpaths + [env.get("LDFLAGS", "")]).strip()
|
||||||
|
|
||||||
|
if pkg_dirs:
|
||||||
|
env["PKG_CONFIG_PATH"] = ":".join(pkg_dirs + ([env.get("PKG_CONFIG_PATH")] if env.get("PKG_CONFIG_PATH") else []))
|
||||||
|
|
||||||
|
if bin_dirs:
|
||||||
|
env["PATH"] = ":".join(bin_dirs + [env.get("PATH", "")])
|
||||||
|
|
||||||
|
if extra_env:
|
||||||
|
env.update(extra_env)
|
||||||
|
|
||||||
|
return env
|
||||||
|
|
||||||
|
|
||||||
|
def build_xmlrpc_c(base_dir, xmlrpc_ref, *, debug=False):
|
||||||
source_root = Path(base_dir) / "xmlrpc-c-src"
|
source_root = Path(base_dir) / "xmlrpc-c-src"
|
||||||
install_dir = Path(base_dir) / "xmlrpc-c_install"
|
install_dir = Path(base_dir) / "xmlrpc-c_install"
|
||||||
build_root = Path(base_dir) / "_sources"
|
build_root = Path(base_dir) / "_sources"
|
||||||
@@ -202,13 +279,10 @@ def build_xmlrpc_c(base_dir, xmlrpc_ref):
|
|||||||
existing_config = find_xmlrpc_config(base_dir, install_dir)
|
existing_config = find_xmlrpc_config(base_dir, install_dir)
|
||||||
if existing_config and str(existing_config).startswith(str(install_dir.resolve())):
|
if existing_config and str(existing_config).startswith(str(install_dir.resolve())):
|
||||||
print(f"Reusing existing xmlrpc-c installation: {existing_config}")
|
print(f"Reusing existing xmlrpc-c installation: {existing_config}")
|
||||||
verify_xmlrpc_environment(existing_config)
|
version = verify_xmlrpc_environment(existing_config, debug=debug)
|
||||||
return install_dir
|
return install_dir, version
|
||||||
|
|
||||||
ensure_dir(build_root)
|
ensure_dir(build_root)
|
||||||
if source_root.exists():
|
|
||||||
shutil.rmtree(source_root)
|
|
||||||
source_root.mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
if xmlrpc_ref == "latest-stable":
|
if xmlrpc_ref == "latest-stable":
|
||||||
url = "https://sourceforge.net/projects/xmlrpc-c/files/latest/download"
|
url = "https://sourceforge.net/projects/xmlrpc-c/files/latest/download"
|
||||||
@@ -220,89 +294,170 @@ def build_xmlrpc_c(base_dir, xmlrpc_ref):
|
|||||||
else:
|
else:
|
||||||
url = xmlrpc_ref
|
url = xmlrpc_ref
|
||||||
|
|
||||||
print(f"Downloading xmlrpc-c from: {url}")
|
with Spinner("Downloading xmlrpc-c", enabled=not debug):
|
||||||
download_file(url, tarball)
|
download_file(url, tarball, debug=debug)
|
||||||
run(["tar", "-xzf", str(tarball), "-C", str(source_root), "--strip-components=1"])
|
extract_tarball(tarball, source_root, debug=debug)
|
||||||
run(["./configure", f"--prefix={install_dir}"], cwd=str(source_root))
|
with Spinner("Configuring xmlrpc-c", enabled=not debug):
|
||||||
run(["make", "-j", str(os.cpu_count() or 1)], cwd=str(source_root))
|
run(["./configure", f"--prefix={install_dir}"], cwd=str(source_root), debug=debug)
|
||||||
run(["make", "install"], cwd=str(source_root))
|
with Spinner("Building xmlrpc-c", enabled=not debug):
|
||||||
|
run(["make", "-j", str(os.cpu_count() or 1)], cwd=str(source_root), debug=debug)
|
||||||
|
with Spinner("Installing xmlrpc-c", enabled=not debug):
|
||||||
|
run(["make", "install"], cwd=str(source_root), debug=debug)
|
||||||
|
|
||||||
xmlrpc_config = find_xmlrpc_config(base_dir, install_dir)
|
xmlrpc_config = find_xmlrpc_config(base_dir, install_dir)
|
||||||
if not xmlrpc_config or not str(xmlrpc_config).startswith(str(install_dir.resolve())):
|
if not xmlrpc_config or not str(xmlrpc_config).startswith(str(install_dir.resolve())):
|
||||||
raise InstallError(
|
raise InstallError(f"Custom xmlrpc-c build finished, but xmlrpc-c-config was not found under {install_dir}.")
|
||||||
f"Custom xmlrpc-c build finished, but xmlrpc-c-config was not found under {install_dir}."
|
version = verify_xmlrpc_environment(xmlrpc_config, debug=debug)
|
||||||
)
|
return install_dir, version
|
||||||
|
|
||||||
verify_xmlrpc_environment(xmlrpc_config)
|
|
||||||
return install_dir
|
|
||||||
|
|
||||||
|
|
||||||
def build_libtorrent(base_dir, libtorrent_ref):
|
def build_cares(base_dir, cares_version, *, debug=False):
|
||||||
source_dir = Path(base_dir) / "libtorrent"
|
source_root = Path(base_dir) / "c-ares-src"
|
||||||
install_dir = Path(base_dir) / "libtorrent_install"
|
install_dir = Path(base_dir) / "c-ares_install"
|
||||||
|
build_root = Path(base_dir) / "_sources"
|
||||||
|
tarball = build_root / f"c-ares-{cares_version}.tar.gz"
|
||||||
|
url = f"https://github.com/c-ares/c-ares/releases/download/v{cares_version}/c-ares-{cares_version}.tar.gz"
|
||||||
|
|
||||||
clone_or_update_repo("https://github.com/rakshasa/libtorrent.git", source_dir, libtorrent_ref)
|
ensure_dir(build_root)
|
||||||
run(["autoreconf", "-i"], cwd=str(source_dir))
|
with Spinner("Downloading c-ares", enabled=not debug):
|
||||||
run(["./configure", f"--prefix={install_dir}"], cwd=str(source_dir))
|
download_file(url, tarball, debug=debug)
|
||||||
run(["make", "-j", str(os.cpu_count() or 1)], cwd=str(source_dir))
|
extract_tarball(tarball, source_root, debug=debug)
|
||||||
run(["make", "install"], cwd=str(source_dir))
|
with Spinner("Configuring c-ares", enabled=not debug):
|
||||||
return install_dir
|
run([
|
||||||
|
"cmake",
|
||||||
|
"-S", str(source_root),
|
||||||
|
"-B", str(source_root / "build"),
|
||||||
|
f"-DCMAKE_INSTALL_PREFIX={install_dir}",
|
||||||
|
"-DCARES_SHARED=ON",
|
||||||
|
"-DCARES_STATIC=OFF",
|
||||||
|
"-DCMAKE_BUILD_TYPE=Release",
|
||||||
|
], debug=debug)
|
||||||
|
with Spinner("Building c-ares", enabled=not debug):
|
||||||
|
run(["cmake", "--build", str(source_root / "build"), "--parallel", str(os.cpu_count() or 1)], debug=debug)
|
||||||
|
with Spinner("Installing c-ares", enabled=not debug):
|
||||||
|
run(["cmake", "--install", str(source_root / "build")], debug=debug)
|
||||||
|
return install_dir, cares_version
|
||||||
|
|
||||||
|
|
||||||
def build_rtorrent(base_dir, rtorrent_ref, libtorrent_install, xmlrpc_install):
|
def build_curl(base_dir, curl_version, cares_install, *, debug=False):
|
||||||
source_dir = Path(base_dir) / "rtorrent"
|
source_root = Path(base_dir) / "curl-src"
|
||||||
install_dir = Path(base_dir) / "rtorrent_install"
|
install_dir = Path(base_dir) / "curl_install"
|
||||||
|
build_root = Path(base_dir) / "_sources"
|
||||||
|
tarball = build_root / f"curl-{curl_version}.tar.gz"
|
||||||
|
url = f"https://curl.se/download/curl-{curl_version}.tar.gz"
|
||||||
|
|
||||||
clone_or_update_repo("https://github.com/rakshasa/rtorrent.git", source_dir, rtorrent_ref)
|
ensure_dir(build_root)
|
||||||
|
with Spinner("Downloading curl", enabled=not debug):
|
||||||
|
download_file(url, tarball, debug=debug)
|
||||||
|
extract_tarball(tarball, source_root, debug=debug)
|
||||||
|
|
||||||
xmlrpc_config = find_xmlrpc_config(base_dir, xmlrpc_install)
|
env = build_env(cares_install)
|
||||||
if not xmlrpc_config:
|
buildconf_script = source_root / "buildconf"
|
||||||
raise InstallError(
|
with Spinner("Preparing curl build system", enabled=not debug):
|
||||||
f"Could not find custom xmlrpc-c-config under {base_dir}. "
|
if buildconf_script.exists():
|
||||||
"Build xmlrpc-c first or remove the broken installation and retry."
|
run(["./buildconf"], cwd=str(source_root), env=env, debug=debug)
|
||||||
)
|
run(["make", "distclean"], cwd=str(source_root), env=env, check=False, debug=debug)
|
||||||
if not str(xmlrpc_config).startswith(str(Path(xmlrpc_install).resolve())):
|
with Spinner("Configuring curl with c-ares", enabled=not debug):
|
||||||
raise InstallError(
|
|
||||||
f"Wrong xmlrpc-c-config selected: {xmlrpc_config}. Expected one under: {xmlrpc_install}"
|
|
||||||
)
|
|
||||||
|
|
||||||
verify_xmlrpc_environment(xmlrpc_config)
|
|
||||||
|
|
||||||
env = os.environ.copy()
|
|
||||||
include_flags = f"-I{libtorrent_install}/include -I{xmlrpc_install}/include"
|
|
||||||
ld_flags = f"-L{libtorrent_install}/lib -L{xmlrpc_install}/lib"
|
|
||||||
existing_cflags = env.get("CFLAGS", "")
|
|
||||||
existing_cppflags = env.get("CPPFLAGS", "")
|
|
||||||
existing_ldflags = env.get("LDFLAGS", "")
|
|
||||||
env["CFLAGS"] = include_flags + (f" {existing_cflags}" if existing_cflags else "")
|
|
||||||
env["CPPFLAGS"] = include_flags + (f" {existing_cppflags}" if existing_cppflags else "")
|
|
||||||
env["LDFLAGS"] = ld_flags + (f" {existing_ldflags}" if existing_ldflags else "")
|
|
||||||
existing_pkg = env.get("PKG_CONFIG_PATH", "")
|
|
||||||
pkg_paths = [f"{libtorrent_install}/lib/pkgconfig", f"{xmlrpc_install}/lib/pkgconfig"]
|
|
||||||
env["PKG_CONFIG_PATH"] = ":".join(pkg_paths + ([existing_pkg] if existing_pkg else []))
|
|
||||||
env["PATH"] = f"{xmlrpc_config.parent}:" + env.get("PATH", "")
|
|
||||||
env["XMLRPC_C_CONFIG"] = str(xmlrpc_config)
|
|
||||||
|
|
||||||
resolved_xmlrpc_config = capture(["sh", "-c", "command -v xmlrpc-c-config"], env=env, check=False)
|
|
||||||
print(f"Resolved xmlrpc-c-config for build: {resolved_xmlrpc_config or 'not found'}")
|
|
||||||
if resolved_xmlrpc_config != str(xmlrpc_config):
|
|
||||||
raise InstallError(
|
|
||||||
f"Wrong xmlrpc-c-config selected: {resolved_xmlrpc_config or 'not found'}. Expected: {xmlrpc_config}"
|
|
||||||
)
|
|
||||||
|
|
||||||
run(["autoreconf", "-i"], cwd=str(source_dir), env=env)
|
|
||||||
run(["make", "distclean"], cwd=str(source_dir), env=env, check=False)
|
|
||||||
run([
|
run([
|
||||||
"./configure",
|
"./configure",
|
||||||
f"--prefix={install_dir}",
|
f"--prefix={install_dir}",
|
||||||
"--with-xmlrpc-c",
|
"--with-openssl",
|
||||||
], cwd=str(source_dir), env=env)
|
f"--enable-ares={cares_install}",
|
||||||
run(["make", "-j", str(os.cpu_count() or 1)], cwd=str(source_dir), env=env)
|
"--disable-static",
|
||||||
run(["make", "install"], cwd=str(source_dir), env=env)
|
"--enable-shared",
|
||||||
return install_dir
|
], cwd=str(source_root), env=env, debug=debug)
|
||||||
|
with Spinner("Building curl", enabled=not debug):
|
||||||
|
run(["make", "-j", str(os.cpu_count() or 1)], cwd=str(source_root), env=env, debug=debug)
|
||||||
|
with Spinner("Installing curl", enabled=not debug):
|
||||||
|
run(["make", "install"], cwd=str(source_root), env=env, debug=debug)
|
||||||
|
|
||||||
|
version = capture([str(install_dir / "bin" / "curl"), "--version"], env=build_env(install_dir, cares_install), debug=debug)
|
||||||
|
return install_dir, version
|
||||||
|
|
||||||
|
|
||||||
def install_symlinks(rtorrent_install, libtorrent_install, xmlrpc_install):
|
def build_libtorrent(base_dir, libtorrent_ref, curl_install=None, cares_install=None, *, debug=False):
|
||||||
|
source_dir = Path(base_dir) / "libtorrent"
|
||||||
|
install_dir = Path(base_dir) / "libtorrent_install"
|
||||||
|
clone_or_update_repo("https://github.com/rakshasa/libtorrent.git", source_dir, libtorrent_ref, debug=debug)
|
||||||
|
|
||||||
|
prefixes = []
|
||||||
|
if curl_install:
|
||||||
|
prefixes.append(curl_install)
|
||||||
|
if cares_install:
|
||||||
|
prefixes.append(cares_install)
|
||||||
|
env = build_env(*prefixes)
|
||||||
|
configure_cmd = ["./configure", f"--prefix={install_dir}"]
|
||||||
|
|
||||||
|
if curl_install:
|
||||||
|
curl_config = str(Path(curl_install) / "bin" / "curl-config")
|
||||||
|
env["CURL_CONFIG"] = curl_config
|
||||||
|
if Path(curl_config).exists():
|
||||||
|
configure_cmd.append(f"--with-curl={curl_config}")
|
||||||
|
env["LIBS"] = f"-L{Path(curl_install) / 'lib'} -lcurl " + env.get("LIBS", "")
|
||||||
|
if cares_install:
|
||||||
|
env["LIBS"] = f"-L{Path(cares_install) / 'lib'} -lcares " + env.get("LIBS", "")
|
||||||
|
|
||||||
|
with Spinner("Preparing libtorrent build system", enabled=not debug):
|
||||||
|
run(["autoreconf", "-i"], cwd=str(source_dir), env=env, debug=debug)
|
||||||
|
run(["make", "distclean"], cwd=str(source_dir), env=env, check=False, debug=debug)
|
||||||
|
with Spinner("Configuring libtorrent", enabled=not debug):
|
||||||
|
run(configure_cmd, cwd=str(source_dir), env=env, debug=debug)
|
||||||
|
with Spinner("Building libtorrent", enabled=not debug):
|
||||||
|
run(["make", "-j", str(os.cpu_count() or 1)], cwd=str(source_dir), env=env, debug=debug)
|
||||||
|
with Spinner("Installing libtorrent", enabled=not debug):
|
||||||
|
run(["make", "install"], cwd=str(source_dir), env=env, debug=debug)
|
||||||
|
|
||||||
|
version = capture(["git", "describe", "--tags", "--always"], cwd=str(source_dir), debug=debug)
|
||||||
|
return install_dir, version
|
||||||
|
|
||||||
|
|
||||||
|
def build_rtorrent(base_dir, rtorrent_ref, libtorrent_install, xmlrpc_install, curl_install=None, cares_install=None, *, debug=False):
|
||||||
|
source_dir = Path(base_dir) / "rtorrent"
|
||||||
|
install_dir = Path(base_dir) / "rtorrent_install"
|
||||||
|
|
||||||
|
clone_or_update_repo("https://github.com/rakshasa/rtorrent.git", source_dir, rtorrent_ref, debug=debug)
|
||||||
|
|
||||||
|
xmlrpc_config = find_xmlrpc_config(base_dir, xmlrpc_install)
|
||||||
|
if not xmlrpc_config:
|
||||||
|
raise InstallError(f"Could not find custom xmlrpc-c-config under {base_dir}.")
|
||||||
|
if not str(xmlrpc_config).startswith(str(Path(xmlrpc_install).resolve())):
|
||||||
|
raise InstallError(f"Wrong xmlrpc-c-config selected: {xmlrpc_config}. Expected one under: {xmlrpc_install}")
|
||||||
|
|
||||||
|
verify_xmlrpc_environment(xmlrpc_config, debug=debug)
|
||||||
|
|
||||||
|
prefixes = [libtorrent_install, xmlrpc_install]
|
||||||
|
if curl_install:
|
||||||
|
prefixes.append(curl_install)
|
||||||
|
if cares_install:
|
||||||
|
prefixes.append(cares_install)
|
||||||
|
env = build_env(*prefixes)
|
||||||
|
env["PATH"] = f"{xmlrpc_config.parent}:" + env.get("PATH", "")
|
||||||
|
env["XMLRPC_C_CONFIG"] = str(xmlrpc_config)
|
||||||
|
|
||||||
|
with Spinner("Preparing rTorrent build system", enabled=not debug):
|
||||||
|
run(["autoreconf", "-i"], cwd=str(source_dir), env=env, debug=debug)
|
||||||
|
run(["make", "distclean"], cwd=str(source_dir), env=env, check=False, debug=debug)
|
||||||
|
|
||||||
|
configure_cmd = ["./configure", f"--prefix={install_dir}", "--with-xmlrpc-c"]
|
||||||
|
with Spinner("Configuring rTorrent", enabled=not debug):
|
||||||
|
run(configure_cmd, cwd=str(source_dir), env=env, debug=debug)
|
||||||
|
with Spinner("Building rTorrent", enabled=not debug):
|
||||||
|
run(["make", "-j", str(os.cpu_count() or 1)], cwd=str(source_dir), env=env, debug=debug)
|
||||||
|
with Spinner("Installing rTorrent", enabled=not debug):
|
||||||
|
run(["make", "install"], cwd=str(source_dir), env=env, debug=debug)
|
||||||
|
|
||||||
|
runtime_prefixes = [libtorrent_install, xmlrpc_install]
|
||||||
|
if curl_install:
|
||||||
|
runtime_prefixes.append(curl_install)
|
||||||
|
if cares_install:
|
||||||
|
runtime_prefixes.append(cares_install)
|
||||||
|
runtime_env = build_env(*runtime_prefixes)
|
||||||
|
runtime_env["LD_LIBRARY_PATH"] = ":".join([f"{p}/lib" for p in runtime_prefixes])
|
||||||
|
version = capture([str(install_dir / "bin" / "rtorrent"), "-h"], env=runtime_env, check=False, debug=debug)
|
||||||
|
return install_dir, version
|
||||||
|
|
||||||
|
|
||||||
|
def install_symlinks(rtorrent_install, libtorrent_install, xmlrpc_install, curl_install=None, cares_install=None, *, debug=False):
|
||||||
rtorrent_bin = Path(rtorrent_install) / "bin" / "rtorrent"
|
rtorrent_bin = Path(rtorrent_install) / "bin" / "rtorrent"
|
||||||
if not rtorrent_bin.exists():
|
if not rtorrent_bin.exists():
|
||||||
raise InstallError(f"Compiled rtorrent binary not found: {rtorrent_bin}")
|
raise InstallError(f"Compiled rtorrent binary not found: {rtorrent_bin}")
|
||||||
@@ -313,9 +468,14 @@ def install_symlinks(rtorrent_install, libtorrent_install, xmlrpc_install):
|
|||||||
usr_local_bin.symlink_to(rtorrent_bin)
|
usr_local_bin.symlink_to(rtorrent_bin)
|
||||||
print(f"Symlinked {usr_local_bin} -> {rtorrent_bin}")
|
print(f"Symlinked {usr_local_bin} -> {rtorrent_bin}")
|
||||||
|
|
||||||
|
lib_dirs = [f"{libtorrent_install}/lib", f"{xmlrpc_install}/lib"]
|
||||||
|
if curl_install:
|
||||||
|
lib_dirs.append(f"{curl_install}/lib")
|
||||||
|
if cares_install:
|
||||||
|
lib_dirs.append(f"{cares_install}/lib")
|
||||||
ld_conf = Path("/etc/ld.so.conf.d/rtorrent-custom-libs.conf")
|
ld_conf = Path("/etc/ld.so.conf.d/rtorrent-custom-libs.conf")
|
||||||
ld_conf.write_text(f"{libtorrent_install}/lib\n{xmlrpc_install}/lib\n")
|
ld_conf.write_text("\n".join(lib_dirs) + "\n")
|
||||||
run(["ldconfig"])
|
run(["ldconfig"], debug=debug)
|
||||||
|
|
||||||
|
|
||||||
def write_service(service_path, binary_path, runtime_lib_dirs):
|
def write_service(service_path, binary_path, runtime_lib_dirs):
|
||||||
@@ -358,6 +518,7 @@ network.port_range.set = {DEFAULT_TORRENT_PORT}-{DEFAULT_TORRENT_PORT}
|
|||||||
network.port_random.set = no
|
network.port_random.set = no
|
||||||
network.bind_address.set = 0.0.0.0
|
network.bind_address.set = 0.0.0.0
|
||||||
system.file.allocate.set = 1
|
system.file.allocate.set = 1
|
||||||
|
system.umask.set = 0022
|
||||||
dht.mode.set = disable
|
dht.mode.set = disable
|
||||||
protocol.pex.set = no
|
protocol.pex.set = no
|
||||||
trackers.use_udp.set = no
|
trackers.use_udp.set = no
|
||||||
@@ -370,58 +531,91 @@ schedule2 = session_save,1200,43200,((session.save))
|
|||||||
|
|
||||||
|
|
||||||
def prepare_user_dirs(user_home, username):
|
def prepare_user_dirs(user_home, username):
|
||||||
dirs = [
|
for d in [Path(user_home) / "downloads", Path(user_home) / ".session", Path(user_home) / "watch"]:
|
||||||
Path(user_home) / "downloads",
|
|
||||||
Path(user_home) / ".session",
|
|
||||||
Path(user_home) / "watch",
|
|
||||||
]
|
|
||||||
for d in dirs:
|
|
||||||
ensure_dir(d, owner=username, group=username, mode=0o755)
|
ensure_dir(d, owner=username, group=username, mode=0o755)
|
||||||
home_path = Path(user_home)
|
shutil.chown(Path(user_home), user=username, group=username)
|
||||||
shutil.chown(home_path, user=username, group=username)
|
|
||||||
|
|
||||||
|
|
||||||
def enable_service(user):
|
def enable_service(user, *, debug=False):
|
||||||
unit_name = f"rtorrent@{user}.service"
|
unit_name = f"rtorrent@{user}.service"
|
||||||
run(["systemctl", "enable", "--now", unit_name])
|
run(["systemctl", "enable", "--now", unit_name], debug=debug)
|
||||||
print(f"Enabled and started {unit_name}")
|
print(f"Enabled and started {unit_name}")
|
||||||
|
|
||||||
|
|
||||||
def verify_install(rtorrent_install, libtorrent_install, xmlrpc_install):
|
def print_link_lines(title, lines):
|
||||||
|
print(title)
|
||||||
|
for line in lines:
|
||||||
|
print(line)
|
||||||
|
|
||||||
|
|
||||||
|
def verify_libtorrent_curl_integration(base_dir, libtorrent_install, curl_install, cares_install, *, debug=False):
|
||||||
|
libtorrent_so = next((p for p in sorted((Path(libtorrent_install) / "lib").glob("libtorrent.so*")) if p.is_file() and not p.is_symlink()), None)
|
||||||
|
if not libtorrent_so:
|
||||||
|
raise InstallError("Could not find compiled libtorrent shared object for verification.")
|
||||||
|
|
||||||
|
libtorrent_linked = capture(["ldd", str(libtorrent_so)], check=True, debug=debug)
|
||||||
|
curl_lines = [line for line in libtorrent_linked.splitlines() if "libcurl" in line.lower()]
|
||||||
|
print_link_lines("Linked libcurl lines (from libtorrent):", curl_lines)
|
||||||
|
|
||||||
|
expected_curl = str(Path(curl_install) / "lib")
|
||||||
|
if curl_lines:
|
||||||
|
if not any(expected_curl in line for line in curl_lines):
|
||||||
|
raise InstallError(f"libtorrent does not appear to be linked against the compiled libcurl from {expected_curl}.")
|
||||||
|
else:
|
||||||
|
config_log = Path(base_dir) / "libtorrent" / "config.log"
|
||||||
|
config_text = config_log.read_text(errors="ignore") if config_log.exists() else ""
|
||||||
|
curl_config = str(Path(curl_install) / "bin" / "curl-config")
|
||||||
|
if curl_config not in config_text and expected_curl not in config_text:
|
||||||
|
raise InstallError(
|
||||||
|
"libtorrent does not expose libcurl in ldd, and config.log does not show the custom curl path either. "
|
||||||
|
"The build likely used the system curl or no curl integration."
|
||||||
|
)
|
||||||
|
print("libtorrent does not show libcurl in ldd; accepting config.log evidence of custom curl usage.")
|
||||||
|
|
||||||
|
custom_curl = Path(curl_install) / "bin" / "curl"
|
||||||
|
curl_version = capture([str(custom_curl), "--version"], env=build_env(curl_install, cares_install), check=True, debug=debug)
|
||||||
|
print("Custom curl version:")
|
||||||
|
print(curl_version.splitlines()[0])
|
||||||
|
lower = curl_version.lower()
|
||||||
|
if "asynchdns" not in lower:
|
||||||
|
raise InstallError("Custom curl does not report AsynchDNS support.")
|
||||||
|
if "c-ares" not in lower and "ares" not in lower:
|
||||||
|
print("Warning: curl --version does not explicitly show c-ares. Continuing because AsynchDNS is present.")
|
||||||
|
|
||||||
|
if cares_install:
|
||||||
|
cares_lines = [line for line in libtorrent_linked.splitlines() if "cares" in line.lower()]
|
||||||
|
print_link_lines("Linked c-ares lines (from libtorrent):", cares_lines)
|
||||||
|
if not cares_lines:
|
||||||
|
print("c-ares is not visible in libtorrent ldd; this can still be valid when libcurl is resolved differently.")
|
||||||
|
|
||||||
|
|
||||||
|
def verify_install(base_dir, rtorrent_install, libtorrent_install, xmlrpc_install, curl_install=None, cares_install=None, *, debug=False):
|
||||||
rtorrent_bin = Path(rtorrent_install) / "bin" / "rtorrent"
|
rtorrent_bin = Path(rtorrent_install) / "bin" / "rtorrent"
|
||||||
which_rtorrent = capture(["which", "rtorrent"], check=False) or "not found in PATH"
|
which_rtorrent = capture(["which", "rtorrent"], check=False, debug=debug) or "not found in PATH"
|
||||||
print(f"Resolved rtorrent from PATH: {which_rtorrent}")
|
print(f"Resolved rtorrent from PATH: {which_rtorrent}")
|
||||||
|
|
||||||
linked = capture(["ldd", str(rtorrent_bin)], check=True)
|
linked = capture(["ldd", str(rtorrent_bin)], check=True, debug=debug)
|
||||||
libtorrent_lines = [line for line in linked.splitlines() if "libtorrent" in line]
|
for libname, expected in [("libtorrent", str(Path(libtorrent_install) / "lib")), ("xmlrpc", str(Path(xmlrpc_install) / "lib"))]:
|
||||||
print("Linked libtorrent lines:")
|
lines = [line for line in linked.splitlines() if libname in line]
|
||||||
for line in libtorrent_lines:
|
print_link_lines(f"Linked {libname} lines:", lines)
|
||||||
print(line)
|
if not any(expected in line for line in lines):
|
||||||
expected_libtorrent = str(Path(libtorrent_install) / "lib")
|
raise InstallError(f"rtorrent does not appear to be linked against the compiled {libname} from {expected}.")
|
||||||
if not any(expected_libtorrent in line for line in libtorrent_lines):
|
|
||||||
raise InstallError("rtorrent does not appear to be linked against the compiled libtorrent from /opt.")
|
|
||||||
|
|
||||||
xmlrpc_lines = [line for line in linked.splitlines() if "xmlrpc" in line]
|
if curl_install:
|
||||||
print("Linked xmlrpc-c lines:")
|
verify_libtorrent_curl_integration(base_dir, libtorrent_install, curl_install, cares_install, debug=debug)
|
||||||
for line in xmlrpc_lines:
|
|
||||||
print(line)
|
|
||||||
expected_xmlrpc = str(Path(xmlrpc_install) / "lib")
|
|
||||||
if not any(expected_xmlrpc in line for line in xmlrpc_lines):
|
|
||||||
raise InstallError("rtorrent does not appear to be linked against the compiled xmlrpc-c from /opt.")
|
|
||||||
|
|
||||||
env = os.environ.copy()
|
env = build_env(libtorrent_install, xmlrpc_install, curl_install, cares_install)
|
||||||
env["LANG"] = "C"
|
env["LANG"] = "C"
|
||||||
env["LC_ALL"] = "C"
|
env["LC_ALL"] = "C"
|
||||||
env["TERM"] = env.get("TERM", "xterm")
|
env["TERM"] = env.get("TERM", "xterm")
|
||||||
env["LD_LIBRARY_PATH"] = f"{Path(libtorrent_install) / 'lib'}:{Path(xmlrpc_install) / 'lib'}"
|
ld_paths = [str(Path(libtorrent_install) / "lib"), str(Path(xmlrpc_install) / "lib")]
|
||||||
probe = subprocess.run(
|
if curl_install:
|
||||||
[str(rtorrent_bin), "-h"],
|
ld_paths.append(str(Path(curl_install) / "lib"))
|
||||||
env=env,
|
if cares_install:
|
||||||
check=False,
|
ld_paths.append(str(Path(cares_install) / "lib"))
|
||||||
text=True,
|
env["LD_LIBRARY_PATH"] = ":".join(ld_paths)
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.PIPE,
|
probe = subprocess.run([str(rtorrent_bin), "-h"], env=env, check=False, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
)
|
|
||||||
help_output = ((probe.stdout or "") + "\n" + (probe.stderr or "")).lower()
|
help_output = ((probe.stdout or "") + "\n" + (probe.stderr or "")).lower()
|
||||||
if "xmlrpc-c" in help_output and "i8" in help_output:
|
if "xmlrpc-c" in help_output and "i8" in help_output:
|
||||||
raise InstallError(
|
raise InstallError(
|
||||||
@@ -431,18 +625,21 @@ def verify_install(rtorrent_install, libtorrent_install, xmlrpc_install):
|
|||||||
|
|
||||||
|
|
||||||
def build_parser():
|
def build_parser():
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(description="Debian installer for xmlrpc-c + libtorrent + rTorrent under /opt with optional c-ares/custom curl support.")
|
||||||
description="Interactive Debian installer for xmlrpc-c + libtorrent + rTorrent under /opt with optional systemd setup."
|
|
||||||
)
|
|
||||||
parser.add_argument("--base-dir", default=DEFAULT_BASE_DIR, help=f"Base build/install directory (default: {DEFAULT_BASE_DIR})")
|
parser.add_argument("--base-dir", default=DEFAULT_BASE_DIR, help=f"Base build/install directory (default: {DEFAULT_BASE_DIR})")
|
||||||
parser.add_argument("--libtorrent-ref", default=DEFAULT_LIBTORRENT_REF, help="Git branch, tag or commit for libtorrent (default: master)")
|
parser.add_argument("--libtorrent-ref", default=DEFAULT_LIBTORRENT_REF, help=f"Git branch, tag or commit for libtorrent (default: {DEFAULT_LIBTORRENT_REF})")
|
||||||
parser.add_argument("--rtorrent-ref", default=DEFAULT_RTORRENT_REF, help="Git branch, tag or commit for rtorrent (default: master)")
|
parser.add_argument("--rtorrent-ref", default=DEFAULT_RTORRENT_REF, help=f"Git branch, tag or commit for rtorrent (default: {DEFAULT_RTORRENT_REF})")
|
||||||
parser.add_argument("--xmlrpc-ref", default=DEFAULT_XMLRPC_REF, help="xmlrpc-c source version or URL (default: latest-stable)")
|
parser.add_argument("--xmlrpc-ref", default=DEFAULT_XMLRPC_REF, help="xmlrpc-c source version or URL (default: latest-stable)")
|
||||||
|
parser.add_argument("--cares-ref", default=DEFAULT_CARES_REF, help=f"c-ares release version (default: {DEFAULT_CARES_REF})")
|
||||||
|
parser.add_argument("--curl-ref", default=DEFAULT_CURL_REF, help=f"curl release version (default: {DEFAULT_CURL_REF})")
|
||||||
parser.add_argument("--user", default=DEFAULT_USER, help=f"System user for the service (default: {DEFAULT_USER})")
|
parser.add_argument("--user", default=DEFAULT_USER, help=f"System user for the service (default: {DEFAULT_USER})")
|
||||||
parser.add_argument("--group", default=DEFAULT_GROUP, help=f"System group for the service (default: {DEFAULT_GROUP})")
|
parser.add_argument("--group", default=DEFAULT_GROUP, help=f"System group for the service (default: {DEFAULT_GROUP})")
|
||||||
parser.add_argument("--home", default=DEFAULT_HOME, help=f"Home directory for the service user (default: {DEFAULT_HOME})")
|
parser.add_argument("--home", default=DEFAULT_HOME, help=f"Home directory for the service user (default: {DEFAULT_HOME})")
|
||||||
parser.add_argument("--only-build", action="store_true", help="Only build and install libtorrent/rTorrent under /opt. Skip user, config and systemd.")
|
parser.add_argument("--only-build", action="store_true", help="Only build and install libtorrent/rTorrent under /opt. Skip user, config and systemd.")
|
||||||
parser.add_argument("--yes", action="store_true", help="Assume yes for interactive prompts.")
|
parser.add_argument("--yes", action="store_true", help="Assume yes for interactive prompts.")
|
||||||
|
parser.add_argument("--debug", action="store_true", help="Show full command output during build steps.")
|
||||||
|
parser.add_argument("--without-cares", dest="use_cares", action="store_false", help="Skip building c-ares and custom curl.")
|
||||||
|
parser.set_defaults(use_cares=True)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
|
||||||
@@ -454,26 +651,20 @@ def main():
|
|||||||
detect_debian()
|
detect_debian()
|
||||||
|
|
||||||
packages = [
|
packages = [
|
||||||
"build-essential",
|
"build-essential", "pkg-config", "libtool", "autoconf", "automake", "git", "ca-certificates",
|
||||||
"pkg-config",
|
"libssl-dev", "libncurses5-dev", "libncursesw5-dev", "libexpat1-dev", "curl", "tar", "cmake",
|
||||||
"libtool",
|
"libpsl-dev", "zlib1g-dev", "libbrotli-dev", "libzstd-dev"
|
||||||
"autoconf",
|
|
||||||
"automake",
|
|
||||||
"git",
|
|
||||||
"ca-certificates",
|
|
||||||
"libssl-dev",
|
|
||||||
"libcurl4-openssl-dev",
|
|
||||||
"libncurses5-dev",
|
|
||||||
"libncursesw5-dev",
|
|
||||||
"libexpat1-dev",
|
|
||||||
"curl",
|
|
||||||
"tar",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
print("This script will:")
|
print("This script will:")
|
||||||
print(f" - build xmlrpc-c from '{args.xmlrpc_ref}'")
|
print(f" - build xmlrpc-c from '{args.xmlrpc_ref}'")
|
||||||
print(f" - build libtorrent from '{args.libtorrent_ref}'")
|
print(f" - build libtorrent from '{args.libtorrent_ref}'")
|
||||||
print(f" - build rtorrent from '{args.rtorrent_ref}'")
|
print(f" - build rtorrent from '{args.rtorrent_ref}'")
|
||||||
|
if args.use_cares:
|
||||||
|
print(f" - build c-ares from '{args.cares_ref}'")
|
||||||
|
print(f" - build curl from '{args.curl_ref}' with c-ares")
|
||||||
|
else:
|
||||||
|
print(" - skip c-ares/custom curl and use system curl")
|
||||||
print(f" - install everything under '{args.base_dir}'")
|
print(f" - install everything under '{args.base_dir}'")
|
||||||
if args.only_build:
|
if args.only_build:
|
||||||
print(" - skip service user, config and systemd setup")
|
print(" - skip service user, config and systemd setup")
|
||||||
@@ -484,28 +675,58 @@ def main():
|
|||||||
print("Aborted by user.")
|
print("Aborted by user.")
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
ensure_packages(packages)
|
ensure_packages(packages, debug=args.debug)
|
||||||
ensure_dir(args.base_dir)
|
ensure_dir(args.base_dir)
|
||||||
|
|
||||||
xmlrpc_install = build_xmlrpc_c(args.base_dir, args.xmlrpc_ref)
|
xmlrpc_install, xmlrpc_version = build_xmlrpc_c(args.base_dir, args.xmlrpc_ref, debug=args.debug)
|
||||||
libtorrent_install = build_libtorrent(args.base_dir, args.libtorrent_ref)
|
|
||||||
rtorrent_install = build_rtorrent(args.base_dir, args.rtorrent_ref, libtorrent_install, xmlrpc_install)
|
cares_install = None
|
||||||
install_symlinks(rtorrent_install, libtorrent_install, xmlrpc_install)
|
cares_version = None
|
||||||
verify_install(rtorrent_install, libtorrent_install, xmlrpc_install)
|
curl_install = None
|
||||||
|
curl_version = None
|
||||||
|
|
||||||
|
if args.use_cares:
|
||||||
|
cares_install, cares_version = build_cares(args.base_dir, args.cares_ref, debug=args.debug)
|
||||||
|
curl_install, curl_version = build_curl(args.base_dir, args.curl_ref, cares_install, debug=args.debug)
|
||||||
|
|
||||||
|
libtorrent_install, libtorrent_version = build_libtorrent(
|
||||||
|
args.base_dir, args.libtorrent_ref, curl_install=curl_install, cares_install=cares_install, debug=args.debug
|
||||||
|
)
|
||||||
|
rtorrent_install, rtorrent_version = build_rtorrent(
|
||||||
|
args.base_dir, args.rtorrent_ref, libtorrent_install, xmlrpc_install, curl_install=curl_install,
|
||||||
|
cares_install=cares_install, debug=args.debug
|
||||||
|
)
|
||||||
|
|
||||||
|
install_symlinks(rtorrent_install, libtorrent_install, xmlrpc_install, curl_install=curl_install, cares_install=cares_install, debug=args.debug)
|
||||||
|
verify_install(args.base_dir, rtorrent_install, libtorrent_install, xmlrpc_install, curl_install=curl_install, cares_install=cares_install, debug=args.debug)
|
||||||
|
|
||||||
if not args.only_build:
|
if not args.only_build:
|
||||||
create_system_user(args.user, args.group, args.home, assume_yes=args.yes)
|
create_system_user(args.user, args.group, args.home, assume_yes=args.yes, debug=args.debug)
|
||||||
prepare_user_dirs(args.home, args.user)
|
prepare_user_dirs(args.home, args.user)
|
||||||
write_rtorrent_config(args.home, args.user)
|
write_rtorrent_config(args.home, args.user)
|
||||||
runtime_lib_dirs = f"{libtorrent_install / 'lib'}:{xmlrpc_install / 'lib'}"
|
runtime_lib_dirs = [f"{libtorrent_install}/lib", f"{xmlrpc_install}/lib"]
|
||||||
write_service(DEFAULT_SERVICE_PATH, "/usr/local/bin/rtorrent", runtime_lib_dirs)
|
if curl_install:
|
||||||
enable_service(args.user)
|
runtime_lib_dirs.append(f"{curl_install}/lib")
|
||||||
|
if cares_install:
|
||||||
|
runtime_lib_dirs.append(f"{cares_install}/lib")
|
||||||
|
write_service(DEFAULT_SERVICE_PATH, "/usr/local/bin/rtorrent", ":".join(runtime_lib_dirs))
|
||||||
|
enable_service(args.user, debug=args.debug)
|
||||||
print(f"\nService status hint: systemctl status rtorrent@{args.user}.service")
|
print(f"\nService status hint: systemctl status rtorrent@{args.user}.service")
|
||||||
|
|
||||||
|
print("\nBuild summary")
|
||||||
|
print("-------------")
|
||||||
|
print(f"xmlrpc-c: {xmlrpc_version}")
|
||||||
|
print(f"libtorrent: {libtorrent_version}")
|
||||||
|
print(f"rtorrent: {rtorrent_version.splitlines()[0] if rtorrent_version else args.rtorrent_ref}")
|
||||||
|
if args.use_cares:
|
||||||
|
print(f"c-ares: {cares_version}")
|
||||||
|
print(f"curl: {curl_version.splitlines()[0] if curl_version else args.curl_ref}")
|
||||||
|
else:
|
||||||
|
print("c-ares: disabled")
|
||||||
|
print("curl: system")
|
||||||
|
print("binary: /usr/local/bin/rtorrent")
|
||||||
|
print(f"base dir: {args.base_dir}")
|
||||||
print("\nDone.")
|
print("\nDone.")
|
||||||
print("rtorrent binary: /usr/local/bin/rtorrent")
|
|
||||||
print(f"libtorrent path: {libtorrent_install / 'lib'}")
|
|
||||||
print(f"xmlrpc-c path: {xmlrpc_install / 'lib'}")
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user