From 3f4b5313de41ae576770f8dadaab314d1382939e Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Wed, 29 Oct 2025 16:51:21 -0600 Subject: [PATCH 1/7] Add preparedepends --- docs/recipe-format.md | 23 +++++ toltec/builder.py | 212 ++++++++++++++++++++++-------------------- toltec/recipe.py | 3 + 3 files changed, 136 insertions(+), 102 deletions(-) diff --git a/docs/recipe-format.md b/docs/recipe-format.md index daeb0de..032534e 100644 --- a/docs/recipe-format.md +++ b/docs/recipe-format.md @@ -209,6 +209,29 @@ For example, `build:autotools` and `libvncserver>=0.9.13` are valid dependency s **Dependencies declared in the `makedepends` field are only satisfied during the build process, not at install time** — see the [`installdepends`](#installdepends-field) below for declaring install-time dependencies. +#### `preparedepends` field + + + + + + + + + +
Required?No, defaults to () +
TypeArray of dependency specifications (strings)
+ +The list of Debian, Toltec or Entware packages that are needed to prepare this package. +Dependency specifications have the following format: `[host:|build:]package-name`. +For example, `build:autotools` and `libvncserver>=0.9.13` are valid dependency specifications. + +*Build-type dependencies* (prefixed with `build:`) are packages from Debian to install in the container’s root system before the recipe’s build script is executed. + +*Host-type dependencies* (prefixed with `host:`) are packages from Toltec or Entware to install in the container’s `$SYSROOT` before the recipe’s build script is executed. The packages are offline-installed (i.e., none of their [install scripts](#install-section) are executed). + +**Dependencies declared in the `preparedepends` field are only satisfied during the prepare process, not at install time** — see the [`installdepends`](#installdepends-field) below for declaring install-time dependencies. + #### `pkgnames` field diff --git a/toltec/builder.py b/toltec/builder.py index b49f6ab..dc9c187 100644 --- a/toltec/builder.py +++ b/toltec/builder.py @@ -15,7 +15,7 @@ from . import hooks from . import bash, util, ipk from .recipe import RecipeBundle, Recipe, Package -from .version import DependencyKind +from .version import Dependency, DependencyKind logger = logging.getLogger(__name__) @@ -112,9 +112,7 @@ def post_build(self, recipe: Recipe, src_dir: str) -> None: """ @util.hook - def post_package( - self, package: Package, src_dir: str, pkg_dir: str - ) -> None: + def post_package(self, package: Package, src_dir: str, pkg_dir: str) -> None: """ Triggered after part of the artifacts from a build have been copied in place to the packaging directory. @@ -195,9 +193,7 @@ def _make_arch( base_pkg_dir = os.path.join(build_dir, "pkg") os.makedirs(base_pkg_dir, exist_ok=True) - for package in ( - packages if packages is not None else recipe.packages.values() - ): + for package in packages if packages is not None else recipe.packages.values(): pkg_dir = os.path.join(base_pkg_dir, package.name) os.makedirs(pkg_dir, exist_ok=True) @@ -227,9 +223,7 @@ def _fetch_sources( if self.URL_REGEX.match(source.url) is None: # Get source file from the recipe’s directory - _ = shutil.copy2( - os.path.join(recipe.path, source.url), local_path - ) + _ = shutil.copy2(os.path.join(recipe.path, source.url), local_path) else: # Fetch source file from the network req = requests.get(source.url, timeout=(3.05, 300)) @@ -261,69 +255,28 @@ def _fetch_sources( local_path, ) - def _prepare(self, recipe: Recipe, src_dir: str) -> None: - """Prepare source files before building.""" - if not recipe.prepare: - logger.debug("Skipping prepare (nothing to do)") - return - - logger.info("Preparing source files") - mount_src = "/src" - uid = os.getuid() - gid = os.getgid() - logs = bash.run_script_in_container( - self.docker, - image=self.IMAGE_PREFIX + "toolchain:v4.0", - mounts=[ - docker.types.Mount( - type="bind", - source=os.path.abspath(src_dir), - target=mount_src, - ), - ], - variables={ - "srcdir": mount_src, - }, - script="\n".join( - [ - f'cd "{mount_src}"', - recipe.prepare, - f"chown -R {uid}:{gid} {mount_src}", - ] - ), - ) - bash.pipe_logs(logger, logs, "prepare()") - - # pylint: disable=too-many-locals - def _build(self, recipe: Recipe, src_dir: str) -> None: - """Build artifacts for a recipe.""" - if not recipe.build: - logger.debug("Skipping build (nothing to do)") - return - - logger.info("Building artifacts") - - # Set fixed atime and mtime for all the source files - epoch = int(recipe.timestamp.timestamp()) - - for filename in util.list_tree(src_dir): - os.utime(filename, (epoch, epoch)) - - mount_src = "/src" - repo_src = "/repo" - uid = os.getuid() - gid = os.getgid() + def _run_script( # pylint: disable=R0913, R0917, R0914 + self, + script: list[str], + arch: str = "rmall", + image: str = "toolchain:v4.0", + dependencies: set[Dependency] | None = None, + mounts: list[docker.types.Mount] | None = None, + variables: bash.Variables | None = None, + ) -> bash.LogGenerator: + """Run a recipe script in a container""" pre_script: list[str] = [] # Install required dependencies build_deps: list[str] = [] host_deps: list[str] = [] - for dep in recipe.makedepends: - if dep.kind == DependencyKind.BUILD: - build_deps.append(dep.package) - elif dep.kind == DependencyKind.HOST: - host_deps.append(dep.package) + if dependencies is not None: + for dep in dependencies: + if dep.kind == DependencyKind.BUILD: + build_deps.append(dep.package) + elif dep.kind == DependencyKind.HOST: + host_deps.append(dep.package) if build_deps: pre_script.extend( @@ -338,25 +291,16 @@ def _build(self, recipe: Recipe, src_dir: str) -> None: ) ) + is_aarch64 = arch.startswith("rmpp") if host_deps: opkg_conf_path = ( "$SYSROOT_AARCH64/etc/opkg/opkg.conf" - if recipe.arch.startswith("rmpp") + if is_aarch64 else "$SYSROOT/etc/opkg/opkg.conf" ) - opkg_exec = ( - "opkg-aarch64" if recipe.arch.startswith("rmpp") else "opkg" - ) - opkg_arch = ( - "aarch64-3.10" - if recipe.arch.startswith("rmpp") - else "armv7-3.2" - ) - opkg_src = ( - "aarch64-k3.10" - if recipe.arch.startswith("rmpp") - else "armv7sf-k3.2" - ) + opkg_exec = "opkg-aarch64" if is_aarch64 else "opkg" + opkg_arch = "aarch64-3.10" if is_aarch64 else "armv7-3.2" + opkg_src = "aarch64-k3.10" if is_aarch64 else "armv7sf-k3.2" pre_script.extend( ( @@ -370,11 +314,11 @@ def _build(self, recipe: Recipe, src_dir: str) -> None: ) ) - if recipe.arch != "rmall": + if arch != "rmall": pre_script.extend( ( - f'echo -n "arch {recipe.arch} 250', - f"src/gz toltec-{recipe.arch} file:///repo/{recipe.arch}", + f'echo -n "arch {arch} 250', + f"src/gz toltec-{arch} file:///repo/{arch}", f'" >> "{opkg_conf_path}"', ) ) @@ -388,12 +332,88 @@ def _build(self, recipe: Recipe, src_dir: str) -> None: ) ) - if recipe.arch.startswith("rmpp"): - pre_script.append(("source /opt/x-tools/switch-aarch64.sh")) + if is_aarch64: + pre_script.append( + ( + "if [ -f /opt/x-tools/switch-aarch64.sh ]; then" + + "source /opt/x-tools/switch-aarch64.sh;" + + "fi" + ) + ) - logs = bash.run_script_in_container( + return bash.run_script_in_container( self.docker, - image=self.IMAGE_PREFIX + recipe.image, + image=self.IMAGE_PREFIX + image, + mounts=mounts or [], + variables=variables or {}, + script="\n".join( + [ + *pre_script, + *script, + ] + ), + ) + + def _prepare(self, recipe: Recipe, src_dir: str) -> None: + """Prepare source files before building.""" + if not recipe.prepare: + logger.debug("Skipping prepare (nothing to do)") + return + + logger.info("Preparing source files") + mount_src = "/src" + uid = os.getuid() + gid = os.getgid() + logs = self._run_script( + [ + f'cd "{mount_src}"', + recipe.prepare, + f"chown -R {uid}:{gid} {mount_src}", + ], + image=recipe.image, + arch=recipe.arch, + dependencies=recipe.preparedepends, + mounts=[ + docker.types.Mount( + type="bind", + source=os.path.abspath(src_dir), + target=mount_src, + ), + ], + variables={ + "srcdir": mount_src, + }, + ) + bash.pipe_logs(logger, logs, "prepare()") + + # pylint: disable=too-many-locals + def _build(self, recipe: Recipe, src_dir: str) -> None: + """Build artifacts for a recipe.""" + if not recipe.build: + logger.debug("Skipping build (nothing to do)") + return + + logger.info("Building artifacts") + + # Set fixed atime and mtime for all the source files + epoch = int(recipe.timestamp.timestamp()) + + for filename in util.list_tree(src_dir): + os.utime(filename, (epoch, epoch)) + + mount_src = "/src" + repo_src = "/repo" + uid = os.getuid() + gid = os.getgid() + + logs = self._run_script( + [ + f'cd "{mount_src}"', + recipe.build, + f"chown -R {uid}:{gid} {mount_src} {repo_src}", + ], + arch=recipe.arch, + dependencies=recipe.makedepends, mounts=[ docker.types.Mount( type="bind", @@ -409,14 +429,6 @@ def _build(self, recipe: Recipe, src_dir: str) -> None: variables={ "srcdir": mount_src, }, - script="\n".join( - ( - *pre_script, - f'cd "{mount_src}"', - recipe.build, - f"chown -R {uid}:{gid} {mount_src} {repo_src}", - ) - ), ) bash.pipe_logs(logger, logs, "build()") @@ -437,9 +449,7 @@ def _package(self, package: Package, src_dir: str, pkg_dir: str) -> None: for filename in util.list_tree(pkg_dir): logger.debug( " - %s", - os.path.normpath( - os.path.join("/", os.path.relpath(filename, pkg_dir)) - ), + os.path.normpath(os.path.join("/", os.path.relpath(filename, pkg_dir))), ) @staticmethod @@ -484,9 +494,7 @@ def _archive(package: Package, pkg_dir: str, ar_path: str) -> None: ) for step in ("pre", "post"): - if getattr(package, step + "upgrade") or getattr( - package, step + "remove" - ): + if getattr(package, step + "upgrade") or getattr(package, step + "remove"): script = script_header for action in ("upgrade", "remove"): diff --git a/toltec/recipe.py b/toltec/recipe.py index 361feae..04c7aad 100644 --- a/toltec/recipe.py +++ b/toltec/recipe.py @@ -69,6 +69,9 @@ class Recipe: # pylint: disable=too-many-instance-attributes # Set of packages that are needed to build this recipe makedepends: set[Dependency] + # Set of packages that are needed to prepare this recipe + preparedepends: set[Dependency] + # Full name and email address of this recipe’s maintainer maintainer: str From c6ad40cb4cafe137c9d9df0698e13f6e498ce95f Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Wed, 29 Oct 2025 16:56:02 -0600 Subject: [PATCH 2/7] Increment version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e2cb85d..ff49e98 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "toltecmk" -version = "0.4.0" +version = "0.5.0" authors = [ { name="Mattéo Delabre", email="git.matteo@delab.re" }, { name="Eeems", email="eeems@eeems.email" }, From 91e3da0c360e39aaf4bd338b1aebb60aadeeb215 Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Wed, 29 Oct 2025 16:56:24 -0600 Subject: [PATCH 3/7] Formatting --- toltec/builder.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/toltec/builder.py b/toltec/builder.py index dc9c187..913f6cb 100644 --- a/toltec/builder.py +++ b/toltec/builder.py @@ -112,7 +112,9 @@ def post_build(self, recipe: Recipe, src_dir: str) -> None: """ @util.hook - def post_package(self, package: Package, src_dir: str, pkg_dir: str) -> None: + def post_package( + self, package: Package, src_dir: str, pkg_dir: str + ) -> None: """ Triggered after part of the artifacts from a build have been copied in place to the packaging directory. @@ -193,7 +195,9 @@ def _make_arch( base_pkg_dir = os.path.join(build_dir, "pkg") os.makedirs(base_pkg_dir, exist_ok=True) - for package in packages if packages is not None else recipe.packages.values(): + for package in ( + packages if packages is not None else recipe.packages.values() + ): pkg_dir = os.path.join(base_pkg_dir, package.name) os.makedirs(pkg_dir, exist_ok=True) @@ -223,7 +227,9 @@ def _fetch_sources( if self.URL_REGEX.match(source.url) is None: # Get source file from the recipe’s directory - _ = shutil.copy2(os.path.join(recipe.path, source.url), local_path) + _ = shutil.copy2( + os.path.join(recipe.path, source.url), local_path + ) else: # Fetch source file from the network req = requests.get(source.url, timeout=(3.05, 300)) @@ -449,7 +455,9 @@ def _package(self, package: Package, src_dir: str, pkg_dir: str) -> None: for filename in util.list_tree(pkg_dir): logger.debug( " - %s", - os.path.normpath(os.path.join("/", os.path.relpath(filename, pkg_dir))), + os.path.normpath( + os.path.join("/", os.path.relpath(filename, pkg_dir)) + ), ) @staticmethod @@ -494,7 +502,9 @@ def _archive(package: Package, pkg_dir: str, ar_path: str) -> None: ) for step in ("pre", "post"): - if getattr(package, step + "upgrade") or getattr(package, step + "remove"): + if getattr(package, step + "upgrade") or getattr( + package, step + "remove" + ): script = script_header for action in ("upgrade", "remove"): From d6ff5b958b05013c8b6dd19707a24d6f5477c90c Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Wed, 29 Oct 2025 16:57:47 -0600 Subject: [PATCH 4/7] Fix tests --- tests/recipe_parsers/test_parser.py | 20 ++++++++++++++++++++ tests/test_recipe.py | 1 + toltec/recipe_parsers/bash.py | 8 ++++++++ 3 files changed, 29 insertions(+) diff --git a/tests/recipe_parsers/test_parser.py b/tests/recipe_parsers/test_parser.py index 205eb14..d56c6a6 100644 --- a/tests/recipe_parsers/test_parser.py +++ b/tests/recipe_parsers/test_parser.py @@ -80,6 +80,7 @@ def test_basic_recipe(self) -> None: }, ) self.assertEqual(recipe.makedepends, set()) + self.assertEqual(recipe.preparedepends, set()) self.assertEqual(recipe.maintainer, "None ") self.assertEqual(recipe.image, "base:v2.1") self.assertEqual(recipe.arch, "rmall") @@ -94,6 +95,7 @@ def test_basic_recipe(self) -> None: declare -a sha256sums=([0]=SKIP) declare -a noextract=() declare -a makedepends=() +declare -a preparedepends=() declare -- maintainer='None ' declare -- image=base:v2.1 declare -- arch=rmall @@ -136,6 +138,7 @@ def test_basic_recipe(self) -> None: declare -a sha256sums=([0]=SKIP) declare -a noextract=() declare -a makedepends=() +declare -a preparedepends=() declare -- maintainer='None ' declare -- image=base:v2.1 declare -- arch=rmall @@ -383,6 +386,7 @@ def test_split_packages(self): self.assertEqual(recipe.path, rec_path) self.assertEqual(recipe.makedepends, set()) + self.assertEqual(recipe.preparedepends, set()) self.assertEqual(recipe.maintainer, "None ") self.assertEqual(recipe.image, "base:v2.1") self.assertEqual(recipe.arch, "rmall") @@ -396,6 +400,7 @@ def test_split_packages(self): declare -a sha256sums=([0]=SKIP) declare -a noextract=() declare -a makedepends=() +declare -a preparedepends=() declare -- maintainer='None ' declare -- image=base:v2.1 declare -- arch=rmall @@ -417,6 +422,7 @@ def test_split_packages(self): declare -a sha256sums=([0]=SKIP) declare -a noextract=() declare -a makedepends=() +declare -a preparedepends=() declare -- maintainer='None ' declare -- image=base:v2.1 declare -- arch=rmall @@ -457,6 +463,7 @@ def test_split_packages(self): declare -a sha256sums=([0]=SKIP) declare -a noextract=() declare -a makedepends=() +declare -a preparedepends=() declare -- maintainer='None ' declare -- image=base:v2.1 declare -- arch=rmall @@ -505,6 +512,7 @@ def test_split_packages(self): declare -a sha256sums=([0]=SKIP) declare -a noextract=() declare -a makedepends=() +declare -a preparedepends=() declare -- maintainer='None ' declare -- image=base:v2.1 declare -- arch=rmall @@ -554,6 +562,7 @@ def test_split_packages(self): declare -a sha256sums=([0]=SKIP) declare -a noextract=() declare -a makedepends=() +declare -a preparedepends=() declare -- maintainer='None ' declare -- image=base:v2.1 declare -- arch=rmall @@ -664,6 +673,7 @@ def test_split_archs(self): declare -a sha256sums=() declare -a noextract=() declare -a makedepends=() +declare -a preparedepends=() declare -- maintainer='Mattéo Delabre ' declare -- image=qt:v1.1 declare -- arch=rm1 @@ -718,6 +728,7 @@ def test_split_archs(self): declare -a sha256sums=() declare -a noextract=() declare -a makedepends=() +declare -a preparedepends=() declare -- maintainer='Mattéo Delabre ' declare -- image=qt:v1.1 declare -- arch=rm1 @@ -753,6 +764,7 @@ def test_split_archs(self): declare -a sha256sums=() declare -a noextract=() declare -a makedepends=() +declare -a preparedepends=() declare -- maintainer='Mattéo Delabre ' declare -- image=qt:v1.1 declare -- arch=rm1 @@ -810,6 +822,7 @@ def test_split_archs(self): declare -a sha256sums=() declare -a noextract=() declare -a makedepends=() +declare -a preparedepends=() declare -- maintainer='Mattéo Delabre ' declare -- image=qt:v1.1 declare -- arch=rm1 @@ -845,6 +858,7 @@ def test_split_archs(self): declare -a sha256sums=() declare -a noextract=() declare -a makedepends=() +declare -a preparedepends=() declare -- maintainer='Mattéo Delabre ' declare -- image=qt:v1.1 declare -- arch=rm1 @@ -883,6 +897,7 @@ def test_split_archs(self): ) self.assertEqual(rm2.sources, set()) self.assertEqual(rm2.makedepends, set()) + self.assertEqual(rm2.preparedepends, set()) self.assertEqual(rm2.maintainer, "None ") self.assertEqual(rm2.image, "qt:v1.3") self.assertEqual(rm2.arch, "rm2") @@ -897,6 +912,7 @@ def test_split_archs(self): declare -a sha256sums=() declare -a noextract=() declare -a makedepends=() +declare -a preparedepends=() declare -- maintainer='None ' declare -- image=qt:v1.3 declare -- arch=rm2 @@ -954,6 +970,7 @@ def test_split_archs(self): declare -a sha256sums=() declare -a noextract=() declare -a makedepends=() +declare -a preparedepends=() declare -- maintainer='None ' declare -- image=qt:v1.3 declare -- arch=rm2 @@ -989,6 +1006,7 @@ def test_split_archs(self): declare -a sha256sums=() declare -a noextract=() declare -a makedepends=() +declare -a preparedepends=() declare -- maintainer='None ' declare -- image=qt:v1.3 declare -- arch=rm2 @@ -1049,6 +1067,7 @@ def test_split_archs(self): declare -a sha256sums=() declare -a noextract=() declare -a makedepends=() +declare -a preparedepends=() declare -- maintainer='None ' declare -- image=qt:v1.3 declare -- arch=rm2 @@ -1084,6 +1103,7 @@ def test_split_archs(self): declare -a sha256sums=() declare -a noextract=() declare -a makedepends=() +declare -a preparedepends=() declare -- maintainer='None ' declare -- image=qt:v1.3 declare -- arch=rm2 diff --git a/tests/test_recipe.py b/tests/test_recipe.py index ff6d67a..fe2b79b 100644 --- a/tests/test_recipe.py +++ b/tests/test_recipe.py @@ -20,6 +20,7 @@ def test_derived_fields(self) -> None: timestamp=datetime.now(), sources=set(), makedepends=set(), + preparedepends=set(), maintainer="Test ", image="", arch="armv7-3.2", diff --git a/toltec/recipe_parsers/bash.py b/toltec/recipe_parsers/bash.py index 0547f0e..f8cde37 100644 --- a/toltec/recipe_parsers/bash.py +++ b/toltec/recipe_parsers/bash.py @@ -181,6 +181,14 @@ def _parse_recipe( # pylint: disable=too-many-locals, disable=too-many-statemen Dependency.parse(dep or "") for dep in makedepends_raw } + preparedepends_raw = _pop_field_indexed( + path, variables, "preparedepends", [] + ) + raw_vars["preparedepends"] = preparedepends_raw + attrs["preparedepends"] = { + Dependency.parse(dep or "") for dep in preparedepends_raw + } + attrs["maintainer"] = raw_vars["maintainer"] = _pop_field_string( path, variables, "maintainer" ) From e958443ef950492ca60a024d93a6fd4c35a88b92 Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Wed, 29 Oct 2025 17:05:37 -0600 Subject: [PATCH 5/7] Use correct image for build and prepare --- toltec/builder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toltec/builder.py b/toltec/builder.py index 913f6cb..909c8a3 100644 --- a/toltec/builder.py +++ b/toltec/builder.py @@ -376,7 +376,6 @@ def _prepare(self, recipe: Recipe, src_dir: str) -> None: recipe.prepare, f"chown -R {uid}:{gid} {mount_src}", ], - image=recipe.image, arch=recipe.arch, dependencies=recipe.preparedepends, mounts=[ @@ -418,6 +417,7 @@ def _build(self, recipe: Recipe, src_dir: str) -> None: recipe.build, f"chown -R {uid}:{gid} {mount_src} {repo_src}", ], + image=recipe.image, arch=recipe.arch, dependencies=recipe.makedepends, mounts=[ From 95a32046a21d4791964fe1fd4851fd692744e686 Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Wed, 29 Oct 2025 17:07:01 -0600 Subject: [PATCH 6/7] Fix switch script --- toltec/builder.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/toltec/builder.py b/toltec/builder.py index 909c8a3..5248d34 100644 --- a/toltec/builder.py +++ b/toltec/builder.py @@ -339,11 +339,11 @@ def _run_script( # pylint: disable=R0913, R0917, R0914 ) if is_aarch64: - pre_script.append( + pre_script.extend( ( - "if [ -f /opt/x-tools/switch-aarch64.sh ]; then" - + "source /opt/x-tools/switch-aarch64.sh;" - + "fi" + "if [ -f /opt/x-tools/switch-aarch64.sh ]; then", + " source /opt/x-tools/switch-aarch64.sh", + "fi", ) ) From 04d32331934743454477ab5f01e326ae09776373 Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Wed, 29 Oct 2025 17:18:09 -0600 Subject: [PATCH 7/7] Test that preparedepends runs --- tests/fixtures/rmkit/package | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/fixtures/rmkit/package b/tests/fixtures/rmkit/package index 50f297b..bfe5998 100644 --- a/tests/fixtures/rmkit/package +++ b/tests/fixtures/rmkit/package @@ -24,8 +24,10 @@ sha256sums=( SKIP SKIP ) +preparedepends=(build:rsync) prepare() { + rsync --help > /dev/null patch -p1 -d"$srcdir" < "$srcdir"/patch-remux-duplicate-xochitl.diff patch -p1 -d"$srcdir" < "$srcdir"/patch-remux-start-xochitl.diff rm "$srcdir"/*.diff