Previously we had the clean / cleansstate / cleanall commands only
implemented for the kas-container. This proved not sufficient under some
scenarios (like rootless), as the cleaning needs to happen from inside
the container. Anyways, it was an inconsitency between kas-container and
kas.
We now add support for these commands in kas as well.
container-entrypoint | 2 +-
docs/_man/kas-plugin-clean.rst | 22 ++++
docs/_man/kas-plugin-cleanall.rst | 22 ++++
docs/_man/kas-plugin-cleansstate.rst | 22 ++++
docs/conf.py | 7 ++
docs/userguide/plugins.rst | 37 +++++++
kas/plugins/__init__.py | 2 +
kas/plugins/clean.py | 150 +++++++++++++++++++++++++++
8 files changed, 263 insertions(+), 1 deletion(-)
create mode 100644 docs/_man/kas-plugin-clean.rst
create mode 100644 docs/_man/kas-plugin-cleanall.rst
create mode 100644 docs/_man/kas-plugin-cleansstate.rst
create mode 100644 kas/plugins/clean.py
diff --git a/container-entrypoint b/container-entrypoint
index 41ce89d8d..aad89d2fc 100755
--- a/container-entrypoint
+++ b/container-entrypoint
@@ -58,7 +58,7 @@ fi
if [ -n "$1" ]; then
case "$1" in
- build|checkout|dump|for-all-repos|lock|menu|shell|-*)
+ build|checkout|clean*|dump|for-all-repos|lock|menu|shell|-*)
# SC2086: Double quote to prevent globbing and word splitting.
# shellcheck disable=2086
exec $GOSU kas "$@"
diff --git a/docs/_man/kas-plugin-clean.rst b/docs/_man/kas-plugin-clean.rst
new file mode 100644
index 000000000..3a0d1c8ae
--- /dev/null
+++ b/docs/_man/kas-plugin-clean.rst
@@ -0,0 +1,22 @@
+:orphan:
+
+kas clean command
+=================
+
+.. argparse::
+ :module: kas.kas
+ :func: kas_get_argparser
+ :prog: kas
+ :path: clean
+ :manpage:
+
+ .. automodule:: kas.plugins.clean.Clean
+ :noindex:
+
+SEE ALSO
+--------
+
+:manpage:`kas-plugin-cleanall`,
+:manpage:`kas-plugin-cleansstate`
+
+.. include:: _kas-man-footer.inc
diff --git a/docs/_man/kas-plugin-cleanall.rst b/docs/_man/kas-plugin-cleanall.rst
new file mode 100644
index 000000000..e67e9b57c
--- /dev/null
+++ b/docs/_man/kas-plugin-cleanall.rst
@@ -0,0 +1,22 @@
+:orphan:
+
+kas cleanall command
+====================
+
+.. argparse::
+ :module: kas.kas
+ :func: kas_get_argparser
+ :prog: kas
+ :path: cleanall
+ :manpage:
+
+ .. automodule:: kas.plugins.clean.CleanAll
+ :noindex:
+
+SEE ALSO
+--------
+
+:manpage:`kas-plugin-clean`,
+:manpage:`kas-plugin-cleansstate`
+
+.. include:: _kas-man-footer.inc
diff --git a/docs/_man/kas-plugin-cleansstate.rst b/docs/_man/kas-plugin-cleansstate.rst
new file mode 100644
index 000000000..270a0d564
--- /dev/null
+++ b/docs/_man/kas-plugin-cleansstate.rst
@@ -0,0 +1,22 @@
+:orphan:
+
+kas cleansstate command
+=======================
+
+.. argparse::
+ :module: kas.kas
+ :func: kas_get_argparser
+ :prog: kas
+ :path: cleansstate
+ :manpage:
+
+ .. automodule:: kas.plugins.clean.CleanSstate
+ :noindex:
+
+SEE ALSO
+--------
+
+:manpage:`kas-plugin-clean`,
+:manpage:`kas-plugin-cleanall`
+
+.. include:: _kas-man-footer.inc
diff --git a/docs/conf.py b/docs/conf.py
index 5c87aade5..1806d3019 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -331,6 +331,13 @@ man_pages = [
[author], 1),
('_man/kas-plugin-checkout', 'kas-checkout', 'kas checkout plugin',
[author], 1),
+ ('_man/kas-plugin-clean', 'kas-clean', 'kas clean command',
+ [author], 1),
+ ('_man/kas-plugin-cleanall', 'kas-cleanall', 'kas cleanall command',
+ [author], 1),
+ ('_man/kas-plugin-cleansstate', 'kas-cleansstate',
+ 'kas cleansstate command',
+ [author], 1),
('_man/kas-plugin-dump', 'kas-dump', 'kas dump plugin',
[author], 1),
('_man/kas-plugin-for-all-repos',
diff --git a/docs/userguide/plugins.rst b/docs/userguide/plugins.rst
index 80b9e6e4f..892479d5c 100644
--- a/docs/userguide/plugins.rst
+++ b/docs/userguide/plugins.rst
@@ -26,6 +26,43 @@ typically provides a single command.
:prog: kas
:path: checkout
+``clean`` plugin
+----------------
+
+.. automodule:: kas.plugins.clean
+
+``clean`` command
+^^^^^^^^^^^^^^^^^
+
+.. automodule:: kas.plugins.clean.Clean
+
+.. argparse::
+ :module: kas.kas
+ :func: kas_get_argparser
+ :prog: kas
+ :path: clean
+
+``cleansstate`` command
+^^^^^^^^^^^^^^^^^^^^^^^
+
+.. automodule:: kas.plugins.clean.CleanSstate
+
+.. argparse::
+ :module: kas.kas
+ :func: kas_get_argparser
+ :prog: kas
+ :path: cleansstate
+
+``cleanall`` command
+^^^^^^^^^^^^^^^^^^^^
+
+.. automodule:: kas.plugins.clean.CleanAll
+
+.. argparse::
+ :module: kas.kas
+ :func: kas_get_argparser
+ :prog: kas
+ :path: cleanall
``dump`` plugin
---------------
diff --git a/kas/plugins/__init__.py b/kas/plugins/__init__.py
index 8c772a262..3d4c10ad2 100644
--- a/kas/plugins/__init__.py
+++ b/kas/plugins/__init__.py
@@ -41,6 +41,7 @@ def load():
from . import build
from . import for_all_repos
from . import checkout
+ from . import clean
from . import shell
from . import menu
from . import dump
@@ -48,6 +49,7 @@ def load():
register_plugins(build)
register_plugins(checkout)
+ register_plugins(clean)
register_plugins(dump)
register_plugins(for_all_repos)
register_plugins(lock)
diff --git a/kas/plugins/clean.py b/kas/plugins/clean.py
new file mode 100644
index 000000000..e96cd1cf5
--- /dev/null
+++ b/kas/plugins/clean.py
@@ -0,0 +1,150 @@
+# kas - setup tool for bitbake based projects
+#
+# Copyright (c) Siemens, 2025
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+"""
+ This plugin implements the ``kas {clean,cleansstate,cleanall}``
+ commands. In case a configuration file is provided, it will be used to
+ determine the build system and the files managed by kas.
+"""
+
+import os
+import shutil
+import logging
+import subprocess
+from pathlib import Path
+from kas.context import create_global_context, get_context
+from kas.config import Config, CONFIG_YAML_FILE
+from kas.libcmds import Macro
+
+__license__ = 'MIT'
+__copyright__ = 'Copyright (c) Siemens, 2025'
+
+
+class Clean():
+ """
+ Clean the build artifacts by removing the build artifacts
+ directory.
+ """
+
+ name = 'clean'
+ helpmsg = (
+ 'Clean build artifacts, keep sstate cache and downloads.'
+ )
+
+ @classmethod
+ def setup_parser(cls, parser):
+ parser.add_argument('--dry-run',
+ action='store_true',
+ default=False,
+ help='Do not remove anything, just print what '
+ 'would be removed')
+ parser.add_argument('--isar',
+ action='store_true',
+ default=False,
+ help='Use ISAR build directory layout')
+ parser.add_argument('config',
+ help='Config file(s), separated by colon. Using '
+ '.config.yaml in KAS_WORK_DIR if existing '
+ 'and none is specified.',
+ nargs='?')
+
+ def run(self, args):
+ ctx = create_global_context(args)
+ build_system = None
+ if args.config or (Path(ctx.kas_work_dir) / CONFIG_YAML_FILE).exists():
+ ctx.config = Config(ctx, args.config)
+ macro = Macro()
+ macro.run(ctx, ['setup_environ'])
+ build_system = ctx.config.get_build_system()
+ if args.isar:
+ build_system = 'isar'
+
+ logging.debug('Run clean in "%s" mode' % (build_system or 'default'))
+ if args.dry_run:
+ logging.warning('Dry run, not removing anything')
+ tmpdirs = Path(ctx.build_dir).glob('tmp*')
+ for tmpdir in tmpdirs:
+
logging.info(f'Removing {tmpdir}')
+ if args.dry_run:
+ continue
+ if build_system == 'isar':
+ clean_args = [
+ 'sudo', '--prompt', '[sudo] enter password for %U '
+ f'to clean ISAR artifacts in {tmpdir}',
+ 'rm', '-rf', str(tmpdir)]
+ subprocess.check_call(clean_args)
+ else:
+ shutil.rmtree(tmpdir)
+
+ @staticmethod
+ def clear_dir_content(directory):
+ """
+ Clear the contents of a directory without removing the dir itself.
+ """
+ for item in directory.iterdir():
+ if item.is_dir():
+ shutil.rmtree(item)
+ else:
+ item.unlink()
+
+
+class CleanSstate(Clean):
+ """
+ Removes the build artifacts and the empties the sstate cache.
+ """
+
+ name = 'cleansstate'
+ helpmsg = (
+ 'Clean build artifacts and sstate cache.'
+ )
+
+ def run(self, args):
+ super().run(args)
+ ctx = get_context()
+ sstate_dir = Path(os.environ.get('SSTATE_DIR',
+ Path(ctx.build_dir) / 'sstate-cache'))
+ if sstate_dir.exists():
+
logging.info(f'Removing {sstate_dir}/*')
+ if not args.dry_run:
+ self.clear_dir_content(sstate_dir)
+
+
+class CleanAll(CleanSstate):
+ """
+ Removes the build artifacts, empties the sstate cache and the downloads.
+ """
+
+ name = 'cleanall'
+ helpmsg = (
+ 'Clean build artifacts, sstate-cache and downloads.'
+ )
+
+ def run(self, args):
+ super().run(args)
+ ctx = get_context()
+ downloads_dir = Path(ctx.build_dir) / 'downloads'
+ if downloads_dir.exists():
+
logging.info(f'Removing {downloads_dir}/*')
+ if not args.dry_run:
+ self.clear_dir_content(downloads_dir)
+
+
+__KAS_PLUGINS__ = [Clean, CleanSstate, CleanAll]
--
2.47.2