From: Daan De Meyer Date: Fri, 17 Mar 2023 14:07:06 +0000 (+0100) Subject: Add source target support to extra and skeleton trees X-Git-Tag: v15~286 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=80b496787c016b46c7d68a8a23a6d3f2a5605abb;p=thirdparty%2Fmkosi.git Add source target support to extra and skeleton trees --- diff --git a/mkosi.md b/mkosi.md index d34ff4416..9e008fbfa 100644 --- a/mkosi.md +++ b/mkosi.md @@ -544,12 +544,16 @@ a boolean argument: either "1", "yes", or "true" to enable, or "0", `SkeletonTree=`, `--skeleton-tree=` -: Takes a path to a directory to copy into the OS tree before invoking - the package manager. Use this to insert files and directories into - the OS tree before the package manager installs any packages. If - this option is not used, but the `mkosi.skeleton/` directory is - found in the local directory it is automatically used for this - purpose (also see the "Files" section below). +: Takes a colon separated pair of paths. The first path refers to a + directory to copy into the OS tree before invoking the package + manager. The second path refers to the target directory inside the + image. If the second path is not provided, the directory is copied + on top of the root directory of the image. Use this to insert files + and directories into the OS tree before the package manager installs + any packages. If this option is not used, but the `mkosi.skeleton/` + directory is found in the local directory it is automatically used + for this purpose with the root directory as target (also see the + "Files" section below). : Instead of a directory, a tar file may be provided. In this case it is unpacked into the OS tree before the package manager is @@ -562,12 +566,15 @@ a boolean argument: either "1", "yes", or "true" to enable, or "0", `ExtraTree=`, `--extra-tree=` -: Takes a path to a directory to copy on top of the OS tree the - package manager generated. Use this to override any default - configuration files shipped with the distribution. If this option is - not used, but the `mkosi.extra/` directory is found in the local - directory it is automatically used for this purpose (also see the - "Files" section below). +: Takes a colon separated pair of paths. The first path refers to a + directory to copy from the host into the image. The second path refers + to the target directory inside the image. If the second path is not + provided, the directory is copied on top of the root directory of the + image. Use this to override any default configuration files shipped + with the distribution. If this option is not used, but the + `mkosi.extra/` directory is found in the local directory it is + automatically used for this purpose with the root directory as target. + (also see the "Files" section below). : As with the skeleton tree logic above, instead of a directory, a tar file may be provided too. `mkosi.skeleton.tar` will be automatically diff --git a/mkosi/__init__.py b/mkosi/__init__.py index 429f16984..cc9916ef6 100644 --- a/mkosi/__init__.py +++ b/mkosi/__init__.py @@ -674,13 +674,19 @@ def install_extra_trees(state: MkosiState) -> None: return with complete_step("Copying in extra file trees…"): - for tree in state.config.extra_trees: - if tree.is_dir(): - copy_path(tree, state.root, preserve_owner=False) + for source, target in state.config.extra_trees: + t = state.root + if target: + t = state.root / target.relative_to("/") + + t.mkdir(mode=0o755, parents=True, exist_ok=True) + + if source.is_dir(): + copy_path(source, t, preserve_owner=False) else: # unpack_archive() groks Paths, but mypy doesn't know this. # Pretend that tree is a str. - shutil.unpack_archive(tree, state.root) + shutil.unpack_archive(source, t) def install_build_dest(state: MkosiState) -> None: @@ -1428,6 +1434,14 @@ USAGE = """ mkosi --version """.format(b=MkosiPrinter.bold, e=MkosiPrinter.reset) + +def parse_source_target_paths(value: str) -> tuple[Path, Optional[Path]]: + src, _, target = value.partition(':') + if target and not Path(target).absolute(): + die("Target path must be absolute") + return Path(src), Path(target) if target else None + + def create_parser() -> ArgumentParserMkosi: parser = ArgumentParserMkosi( prog="mkosi", @@ -1713,7 +1727,7 @@ def create_parser() -> ArgumentParserMkosi: dest="extra_trees", default=[], help="Copy an extra tree on top of image", - type=Path, + type=parse_source_target_paths, metavar="PATH", ) group.add_argument( @@ -1722,7 +1736,7 @@ def create_parser() -> ArgumentParserMkosi: dest="skeleton_trees", default=[], help="Use a skeleton tree to bootstrap the image before installing anything", - type=Path, + type=parse_source_target_paths, metavar="PATH", ) group.add_argument( @@ -2193,9 +2207,9 @@ def find_extra(args: argparse.Namespace) -> None: return if os.path.isdir("mkosi.extra"): - args.extra_trees.append(Path("mkosi.extra")) + args.extra_trees.append((Path("mkosi.extra"), None)) if os.path.isfile("mkosi.extra.tar"): - args.extra_trees.append(Path("mkosi.extra.tar")) + args.extra_trees.append((Path("mkosi.extra.tar"), None)) def find_skeleton(args: argparse.Namespace) -> None: @@ -2204,9 +2218,9 @@ def find_skeleton(args: argparse.Namespace) -> None: return if os.path.isdir("mkosi.skeleton"): - args.skeleton_trees.append(Path("mkosi.skeleton")) + args.skeleton_trees.append((Path("mkosi.skeleton"), None)) if os.path.isfile("mkosi.skeleton.tar"): - args.skeleton_trees.append(Path("mkosi.skeleton.tar")) + args.skeleton_trees.append((Path("mkosi.skeleton.tar"), None)) def args_find_path(args: argparse.Namespace, name: str, path: str, *, as_list: bool = False) -> None: @@ -2506,11 +2520,13 @@ def load_args(args: argparse.Namespace) -> MkosiConfig: if args.extra_trees: for i in range(len(args.extra_trees)): - args.extra_trees[i] = args.extra_trees[i].absolute() + source, target = args.extra_trees[i] + args.extra_trees[i] = (source.absolute(), target) if args.skeleton_trees is not None: for i in range(len(args.skeleton_trees)): - args.skeleton_trees[i] = args.skeleton_trees[i].absolute() + source, target = args.skeleton_trees[i] + args.skeleton_trees[i] = (source.absolute(), target) if args.secure_boot_key is not None: args.secure_boot_key = args.secure_boot_key.absolute() @@ -2605,6 +2621,11 @@ def check_tree_input(path: Optional[Path]) -> None: os.open(path, os.R_OK) +def check_source_target_input(tree: tuple[Path, Optional[Path]]) -> None: + source, _ = tree + os.open(source, os.R_OK) + + def check_script_input(path: Optional[Path]) -> None: if not path: return @@ -2624,7 +2645,7 @@ def check_inputs(config: MkosiConfig) -> None: for tree in (config.skeleton_trees, config.extra_trees): for item in tree: - check_tree_input(item) + check_source_target_input(item) for path in (config.build_script, config.prepare_script, @@ -2687,6 +2708,14 @@ def line_join_list( return "\n ".join(items) +def line_join_source_target_list(array: Sequence[tuple[Path, Optional[Path]]]) -> str: + if not array: + return "none" + + items = [f"{source}:{target}" if target else f"{source}" for source, target in array] + return "\n ".join(items) + + def print_summary(config: MkosiConfig) -> None: print("COMMANDS:") @@ -2772,8 +2801,8 @@ def print_summary(config: MkosiConfig) -> None: print(" With Documentation:", yes_no(config.with_docs)) print(" Package Cache:", none_to_none(config.cache_path)) - print(" Extra Trees:", line_join_list(config.extra_trees, check_tree_input)) - print(" Skeleton Trees:", line_join_list(config.skeleton_trees, check_tree_input)) + print(" Extra Trees:", line_join_source_target_list(config.extra_trees)) + print(" Skeleton Trees:", line_join_source_target_list(config.skeleton_trees)) print(" CleanPackageMetadata:", yes_no_or(config.clean_package_metadata)) if config.remove_files: diff --git a/mkosi/backend.py b/mkosi/backend.py index 964b9b54f..02be103b1 100644 --- a/mkosi/backend.py +++ b/mkosi/backend.py @@ -271,8 +271,8 @@ class MkosiConfig: with_docs: bool with_tests: bool cache_path: Path - extra_trees: list[Path] - skeleton_trees: list[Path] + extra_trees: list[tuple[Path, Optional[Path]]] + skeleton_trees: list[tuple[Path, Optional[Path]]] clean_package_metadata: Union[bool, str] remove_files: list[str] environment: dict[str, str] diff --git a/mkosi/install.py b/mkosi/install.py index 62feb6b01..d681115fb 100644 --- a/mkosi/install.py +++ b/mkosi/install.py @@ -82,10 +82,15 @@ def install_skeleton_trees(state: MkosiState, cached: bool, *, late: bool=False) return with complete_step("Copying in skeleton file trees…"): - for tree in state.config.skeleton_trees: - if tree.is_dir(): - copy_path(tree, state.root, preserve_owner=False) + for source, target in state.config.skeleton_trees: + t = state.root + if target: + t = state.root / target.relative_to("/") + + t.mkdir(mode=0o755, parents=True, exist_ok=True) + if source.is_dir(): + copy_path(source, t, preserve_owner=False) else: # unpack_archive() groks Paths, but mypy doesn't know this. # Pretend that tree is a str. - shutil.unpack_archive(tree, state.root) + shutil.unpack_archive(source, t)