# Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
import os
-import random
-import string
+import shutil
import subprocess
-def sqfs_get_random_letters(size):
- letters = []
- for i in range(0, size):
- letters.append(random.choice(string.ascii_letters))
+""" standard test images table: Each table item is a key:value pair
+representing the output image name and its respective mksquashfs options.
+This table should be modified only when adding support for new compression
+algorithms. The 'default' case takes no options but the input and output
+names, so it must be assigned with an empty string.
+"""
+STANDARD_TABLE = {
+ 'default' : '',
+ 'lzo_comp_frag' : '',
+ 'lzo_frag' : '',
+ 'lzo_no_frag' : '',
+ 'zstd_comp_frag' : '',
+ 'zstd_frag' : '',
+ 'zstd_no_frag' : '',
+ 'gzip_comp_frag' : '',
+ 'gzip_frag' : '',
+ 'gzip_no_frag' : ''
+}
- return ''.join(letters)
+""" EXTRA_TABLE: Set this table's keys and values if you want to make squashfs
+images with your own customized options.
+"""
+EXTRA_TABLE = {}
-def sqfs_generate_file(path, size):
- content = sqfs_get_random_letters(size)
- file = open(path, "w")
+# path to source directory used to make squashfs test images
+SQFS_SRC_DIR = 'sqfs_src_dir'
+
+def get_opts_list():
+ """ Combines fragmentation and compression options into a list of strings.
+
+ opts_list's firts item is an empty string as STANDARD_TABLE's first item is
+ the 'default' case.
+
+ Returns:
+ A list of strings whose items are formed by a compression and a
+ fragmentation option joined by a whitespace.
+ """
+ # supported compression options only
+ comp_opts = ['-comp lzo', '-comp zstd', '-comp gzip']
+ # file fragmentation options
+ frag_opts = ['-always-use-fragments', '-always-use-fragments -noF', '-no-fragments']
+
+ opts_list = [' ']
+ for comp_opt in comp_opts:
+ for frag_opt in frag_opts:
+ opts_list.append(' '.join([comp_opt, frag_opt]))
+
+ return opts_list
+
+def init_standard_table():
+ """ Initializes STANDARD_TABLE values.
+
+ STANDARD_TABLE's keys are pre-defined, and init_standard_table() assigns
+ the right value for each one of them.
+ """
+ opts_list = get_opts_list()
+
+ for key, value in zip(STANDARD_TABLE.keys(), opts_list):
+ STANDARD_TABLE[key] = value
+
+def generate_file(file_name, file_size):
+ """ Generates a file filled with 'x'.
+
+ Args:
+ file_name: the file's name.
+ file_size: the content's length and therefore the file size.
+ """
+ content = 'x' * file_size
+
+ file = open(file_name, 'w')
file.write(content)
file.close()
-class Compression:
- def __init__(self, name, files, sizes, block_size = 4096):
- self.name = name
- self.files = files
- self.sizes = sizes
- self.mksquashfs_opts = " -b " + str(block_size) + " -comp " + self.name
-
- def add_opt(self, opt):
- self.mksquashfs_opts += " " + opt
-
- def gen_image(self, build_dir):
- src = os.path.join(build_dir, "sqfs_src/")
- os.mkdir(src)
- for (f, s) in zip(self.files, self.sizes):
- sqfs_generate_file(src + f, s)
-
- # the symbolic link always targets the first file
- os.symlink(self.files[0], src + "sym")
-
- sqfs_img = os.path.join(build_dir, "sqfs-" + self.name)
- i_o = src + " " + sqfs_img
- opts = self.mksquashfs_opts
- try:
- subprocess.run(["mksquashfs " + i_o + opts], shell = True, check = True)
- except:
- print("mksquashfs error. Compression type: " + self.name)
- raise RuntimeError
-
- def clean_source(self, build_dir):
- src = os.path.join(build_dir, "sqfs_src/")
- for f in self.files:
- os.remove(src + f)
- os.remove(src + "sym")
- os.rmdir(src)
-
- def cleanup(self, build_dir):
- self.clean_source(build_dir)
- sqfs_img = os.path.join(build_dir, "sqfs-" + self.name)
- os.remove(sqfs_img)
-
-files = ["blks_only", "blks_frag", "frag_only"]
-sizes = [4096, 5100, 100]
-gzip = Compression("gzip", files, sizes)
-zstd = Compression("zstd", files, sizes)
-lzo = Compression("lzo", files, sizes)
-
-# use fragment blocks for files larger than block_size
-gzip.add_opt("-always-use-fragments")
-zstd.add_opt("-always-use-fragments")
-
-# avoid fragments if lzo is used
-lzo.add_opt("-no-fragments")
-
-comp_opts = [gzip, zstd, lzo]
+def generate_sqfs_src_dir(build_dir):
+ """ Generates the source directory used to make the SquashFS images.
+
+ The source directory is generated at build_dir, and it has the following
+ structure:
+ sqfs_src_dir/
+ ├── empty-dir/
+ ├── f1000
+ ├── f4096
+ ├── f5096
+ ├── subdir/
+ │ └── subdir-file
+ └── sym -> subdir
+
+ 3 directories, 4 files
+
+ The files in the root dir. are prefixed with an 'f' followed by its size.
+
+ Args:
+ build_dir: u-boot's build-sandbox directory.
+ """
+
+ root = os.path.join(build_dir, SQFS_SRC_DIR)
+ # make root directory
+ os.makedirs(root)
+
+ # 4096: minimum block size
+ file_name = 'f4096'
+ generate_file(os.path.join(root, file_name), 4096)
+
+ # 5096: minimum block size + 1000 chars (fragment)
+ file_name = 'f5096'
+ generate_file(os.path.join(root, file_name), 5096)
+
+ # 1000: less than minimum block size (fragment only)
+ file_name = 'f1000'
+ generate_file(os.path.join(root, file_name), 1000)
+
+ # sub-directory with a single file inside
+ subdir_path = os.path.join(root, 'subdir')
+ os.makedirs(subdir_path)
+ generate_file(os.path.join(subdir_path, 'subdir-file'), 100)
+
+ # symlink (target: sub-directory)
+ os.symlink('subdir', os.path.join(root, 'sym'))
+
+ # empty directory
+ os.makedirs(os.path.join(root, 'empty-dir'))
+
+def mksquashfs(args):
+ """ Runs mksquashfs command.
+
+ Args:
+ args: mksquashfs options (e.g.: compression and fragmentation).
+ """
+ subprocess.run(['mksquashfs ' + args], shell=True, check=True,
+ stdout=subprocess.DEVNULL)
+
+def get_mksquashfs_version():
+ """ Parses the output of mksquashfs -version.
+
+ Returns:
+ mksquashfs's version as a float.
+ """
+ out = subprocess.run(['mksquashfs -version'], shell=True, check=True,
+ capture_output=True, text=True)
+ # 'out' is: mksquashfs version X (yyyy/mm/dd) ...
+ return float(out.stdout.split()[2])
+
+def check_mksquashfs_version():
+ """ Checks if mksquashfs meets the required version. """
+
+ required_version = 4.4
+ if get_mksquashfs_version() < required_version:
+ print('Error: mksquashfs is too old.')
+ print('Required version: {}'.format(required_version))
+ raise AssertionError
+
+def make_all_images(build_dir):
+ """ Makes the SquashFS images used in the test suite.
+
+ The image names and respective mksquashfs options are defined in STANDARD_TABLE
+ and EXTRA_TABLE. The destination is defined by 'build_dir'.
+
+ Args:
+ build_dir: u-boot's build-sandbox directory.
+ """
+
+ init_standard_table()
+ input_path = os.path.join(build_dir, SQFS_SRC_DIR)
+
+ # make squashfs images according to STANDARD_TABLE
+ for out, opts in zip(STANDARD_TABLE.keys(), STANDARD_TABLE.values()):
+ output_path = os.path.join(build_dir, out)
+ mksquashfs(' '.join([input_path, output_path, opts]))
+
+ # make squashfs images according to EXTRA_TABLE
+ for out, opts in zip(EXTRA_TABLE.keys(), EXTRA_TABLE.values()):
+ output_path = os.path.join(build_dir, out)
+ mksquashfs(' '.join([input_path, output_path, opts]))
+
+def clean_all_images(build_dir):
+ """ Deletes the SquashFS images at build_dir.
+
+ Args:
+ build_dir: u-boot's build-sandbox directory.
+ """
+
+ for image_name in STANDARD_TABLE:
+ image_path = os.path.join(build_dir, image_name)
+ os.remove(image_path)
+
+ for image_name in EXTRA_TABLE:
+ image_path = os.path.join(build_dir, image_name)
+ os.remove(image_path)
+
+def clean_sqfs_src_dir(build_dir):
+ """ Deletes the source directory at build_dir.
+
+ Args:
+ build_dir: u-boot's build-sandbox directory.
+ """
+ path = os.path.join(build_dir, SQFS_SRC_DIR)
+ shutil.rmtree(path)