@@ -376,7 +376,42 @@ class BaseConfig(object):
re.search('fitImage', p) or re.search('uImage', p):
self.kernel = p
elif os.path.isfile(p) and ('-image-' in os.path.basename(p) or '.rootfs.' in os.path.basename(p)):
- self.rootfs = p
+
+ # Decompress ZST image if needed
+ if p.endswith('.zst'):
+ # Get the real path to the image to avoid issues when a symbolic link is passed.
+ # This ensures we always operate on the actual file.
+ image_path = os.path.realpath(p)
+ # Extract target filename by removing .zst
+ image_dir = os.path.dirname(image_path)
+ uncompressed_name = os.path.basename(image_path).replace(".zst", "")
+ uncompressed_path = os.path.join(image_dir, uncompressed_name)
+
+ # If the decompressed image already exists (e.g., in the deploy directory),
+ # we use it directly to avoid overwriting artifacts generated by the build system.
+ # This prevents redundant decompression and preserves build outputs.
+ if os.path.exists(uncompressed_path):
+ logger.warning(f"Found existing decompressed image: {uncompressed_path}, Using it directly.")
+ else:
+ logger.info(f"Decompressing {p} to {uncompressed_path}")
+ # Ensure the 'zstd' tool is installed before attempting to decompress.
+ if not shutil.which('zstd'):
+ raise RunQemuError(f"'zstd' is required to decompress {p} but was not found in PATH")
+ try:
+ with open(uncompressed_path, 'wb') as out_file:
+ subprocess.check_call(['zstd', '-d', '-c', image_path], stdout=out_file)
+ except subprocess.CalledProcessError as e:
+ self.cleanup_files.append(uncompressed_path)
+ raise RunQemuError(f"Failed to decompress {p}: {e}")
+
+ # Mark for deletion at the end
+ self.cleanup_files.append(uncompressed_path)
+
+ # Use the decompressed image as the rootfs
+ self.rootfs = uncompressed_path
+
+ else:
+ self.rootfs = p
# Check filename against self.fstypes can handle <file>.cpio.gz,
# otherwise, its type would be "gz", which is incorrect.
fst = ""