From 09eb845f99bdeba63120d00d7875cd4bb2a05927 Mon Sep 17 00:00:00 2001 From: no92 Date: Fri, 20 May 2022 23:18:20 +0200 Subject: [PATCH] xbstrap: add `discard` command for nuking packages --- xbstrap/__init__.py | 21 +++++++++++ xbstrap/base.py | 90 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 99 insertions(+), 12 deletions(-) diff --git a/xbstrap/__init__.py b/xbstrap/__init__.py index da75f26..846dcd7 100755 --- a/xbstrap/__init__.py +++ b/xbstrap/__init__.py @@ -915,6 +915,25 @@ def resolve_host_paths(x): # ---------------------------------------------------------------------------------------- +def do_discard(args): + cfg = config_for_args(args) + sel = select_pkgs(cfg, args) + plan = xbstrap.base.Plan(cfg) + handle_plan_args(cfg, plan, args) + plan.wanted.update([(xbstrap.base.Action.DISCARD_PKG, pkg) for pkg in sel]) + plan.run_plan() + + +do_discard.parser = main_subparsers.add_parser( + "discard", + parents=[handle_plan_args.parser, select_pkgs.parser], + formatter_class=argparse.RawDescriptionHelpFormatter, + description="Discard a package from your tree", +) + +# ---------------------------------------------------------------------------------------- + + def do_execute_manifest(args): if args.c is not None: manifest = yaml.load(args.c, Loader=xbstrap.base.global_yaml_loader) @@ -990,6 +1009,8 @@ def main(): do_run_task(args) elif args.command == "lsp": do_lsp(args) + elif args.command == "discard": + do_discard(args) else: assert not "Unexpected command" except ( diff --git a/xbstrap/base.py b/xbstrap/base.py index 2b8e525..7865bce 100644 --- a/xbstrap/base.py +++ b/xbstrap/base.py @@ -14,6 +14,7 @@ import tempfile import urllib.request import zipfile +from contextlib import suppress from enum import Enum import colorama @@ -951,8 +952,11 @@ def check_if_mirrord(self, settings): return ItemState() - def mark_as_fetched(self): - touch(os.path.join(self.source_dir, "fetched.xbstrap")) + def mark_as_fetched(self, mark=True): + if mark: + touch(os.path.join(self.source_dir, "fetched.xbstrap")) + else: + os.unlink(os.path.join(self.source_dir, "fetched.xbstrap")) def check_if_checkedout(self, settings): path = os.path.join(self.source_dir, "checkedout.xbstrap") @@ -962,8 +966,11 @@ def check_if_checkedout(self, settings): return ItemState(missing=True) return ItemState(timestamp=stat.st_mtime) - def mark_as_checkedout(self): - touch(os.path.join(self.source_dir, "checkedout.xbstrap")) + def mark_as_checkedout(self, mark=True): + if mark: + touch(os.path.join(self.source_dir, "checkedout.xbstrap")) + else: + os.unlink(os.path.join(self.source_dir, "checkedout.xbstrap")) def check_if_patched(self, settings): path = os.path.join(self.source_dir, "patched.xbstrap") @@ -973,8 +980,11 @@ def check_if_patched(self, settings): return ItemState(missing=True) return ItemState(timestamp=stat.st_mtime) - def mark_as_patched(self): - touch(os.path.join(self.source_dir, "patched.xbstrap")) + def mark_as_patched(self, mark=True): + if mark: + touch(os.path.join(self.source_dir, "patched.xbstrap")) + else: + os.unlink(os.path.join(self.source_dir, "patched.xbstrap")) def check_if_regenerated(self, settings): path = os.path.join(self.source_dir, "regenerated.xbstrap") @@ -984,8 +994,11 @@ def check_if_regenerated(self, settings): return ItemState(missing=True) return ItemState(timestamp=stat.st_mtime) - def mark_as_regenerated(self): - touch(os.path.join(self.source_dir, "regenerated.xbstrap")) + def mark_as_regenerated(self, mark=True): + if mark: + touch(os.path.join(self.source_dir, "regenerated.xbstrap")) + else: + os.unlink(os.path.join(self.source_dir, "regenerated.xbstrap")) class HostStage(RequirementsMixin): @@ -1060,7 +1073,7 @@ def check_if_installed(self, settings): return ItemState(missing=True) return ItemState(timestamp=stat.st_mtime) - def mark_as_installed(self): + def mark_as_installed(self, mark=True): stage_spec = "" if not self._inherited: stage_spec = "@" + self.stage_name @@ -1069,7 +1082,10 @@ def mark_as_installed(self): path = os.path.join( self._pkg.prefix_dir, "etc", "xbstrap", self._pkg.name + stage_spec + ".installed" ) - touch(path) + if mark: + touch(path) + else: + os.unlink(path) class HostPackage(RequirementsMixin): @@ -1448,11 +1464,14 @@ def check_if_installed(self, settings): return ItemState(missing=True) return ItemState() - def mark_as_installed(self): + def mark_as_installed(self, mark=True): _util.try_mkdir(os.path.join(self._cfg.sysroot_dir, "etc")) _util.try_mkdir(os.path.join(self._cfg.sysroot_dir, "etc", "xbstrap")) path = os.path.join(self._cfg.sysroot_dir, "etc", "xbstrap", self.name + ".installed") - touch(path) + if mark: + touch(path) + else: + os.unlink(path) class PackageRunTask(RequirementsMixin): @@ -2610,6 +2629,36 @@ def run_tool_task(cfg, task): ) +def discard_src(cfg, src): + with suppress(Exception): + src.mark_as_fetched(mark=False) + src.mark_as_checkedout(mark=False) + src.mark_as_patched(mark=False) + src.mark_as_regenerated(mark=False) + with suppress(Exception): + shutil.rmtree(os.path.join(cfg.source_root, src.source_subdir)) + + +def discard_pkg(cfg, pkg): + with suppress(Exception): + pkg.mark_as_configured(mark=False) + pkg.mark_as_installed(mark=False) + with suppress(Exception): + shutil.rmtree(os.path.join(cfg.build_root, pkg.build_subdir)) + shutil.rmtree(os.path.join(cfg.build_root, pkg.collect_subdir)) + + output = subprocess.DEVNULL + if verbosity: + output = None + effective_arch = pkg.architecture + environ = os.environ.copy() + _util.build_environ_paths(environ, "PATH", prepend=[os.path.join(_util.find_home(), "bin")]) + environ["XBPS_ARCH"] = effective_arch + args = ["xbps-remove", "-Fy", "-r", cfg.sysroot_dir, pkg.name] + _util.log_info("Running {}".format(args)) + subprocess.call(args, env=environ, stdout=output) + + # --------------------------------------------------------------------------------------- # Build planning. # --------------------------------------------------------------------------------------- @@ -2662,6 +2711,8 @@ class Action(Enum): WANT_PKG = 21 # xbstrap-mirror functionality. MIRROR_SRC = 22 + DISCARD_SRC = 23 + DISCARD_PKG = 24 Action.strings = { @@ -2687,6 +2738,8 @@ class Action(Enum): Action.WANT_TOOL: "want-tool", Action.WANT_PKG: "want-pkg", Action.MIRROR_SRC: "mirror", + Action.DISCARD_SRC: "discard-source", + Action.DISCARD_PKG: "discard-package", } @@ -2766,6 +2819,8 @@ def _determine_state(self): Action.WANT_TOOL: lambda s, c: s.check_if_fully_installed(c), Action.WANT_PKG: lambda s, c: s.check_staging(c), Action.MIRROR_SRC: lambda s, c: s.check_if_mirrord(c), + Action.DISCARD_SRC: lambda s, c: ItemState(missing=True), + Action.DISCARD_PKG: lambda s, c: ItemState(missing=True), } self._state = visitors[self.action](self.subject, self.settings) @@ -2965,6 +3020,13 @@ def add_task_dependencies(s): add_pkg_dependencies(subject) add_task_dependencies(subject) + elif action == Action.DISCARD_SRC: + pass + + elif action == Action.DISCARD_PKG: + src = self._cfg.get_source(subject.source) + item.build_edges.add((action.DISCARD_SRC, src)) + return item def _do_materialization_visit(self, edges): @@ -3308,6 +3370,10 @@ def emit_progress(status): raise ExecutionFailureError(action, subject) elif action == Action.MIRROR_SRC: mirror_src(self._cfg, subject) + elif action == Action.DISCARD_SRC: + discard_src(self._cfg, subject) + elif action == Action.DISCARD_PKG: + discard_pkg(self._cfg, subject) else: raise AssertionError("Unexpected action") item.exec_status = ExecutionStatus.SUCCESS