- We now make sure git can be executed from mkosi scripts without
running into permission errors
- `WithDocs=` is now enabled by default.
+- `Presets` were renamed to `Images`. `mkosi.images/` is now used
+ instead of `mkosi.presets/`, the `Presets=` setting was renamed
+ to `Images=` and the `Presets` section was merged into the `Config`
+ section. The old names can still be used for backwards
+ compatibility.
## v18
def check_inputs(config: MkosiConfig) -> None:
"""
- Make sure all the inputs that aren't checked during config parsing because they might be created by an
- earlier preset exist.
+ Make sure all the inputs exist that aren't checked during config parsing because they might be created by an
+ earlier build.
"""
for base in config.base_trees:
if not base.exists():
]
# Underscores are not allowed in machine names so replace them with hyphens.
- cmdline += ["--machine", (config.image_id or config.preset or config.output).replace("_", "-")]
+ cmdline += ["--machine", (config.image_id or config.image or config.output).replace("_", "-")]
for k, v in config.credentials.items():
cmdline += [f"--set-credential={k}:{v}"]
os.environ["PATH"] = ":".join(olds)
-def finalize_tools(args: MkosiArgs, presets: Sequence[MkosiConfig]) -> Sequence[MkosiConfig]:
+def finalize_tools(args: MkosiArgs, images: Sequence[MkosiConfig]) -> Sequence[MkosiConfig]:
new = []
- for p in presets:
+ for p in images:
if not p.tools_tree or p.tools_tree.name != "default":
new.append(p)
continue
]
_, [config] = parse_config(cmdline)
- config = dataclasses.replace(config, preset=f"{distribution}-tools")
+ config = dataclasses.replace(config, image=f"{distribution}-tools")
if config not in new:
new.append(config)
yield
-def run_verb(args: MkosiArgs, presets: Sequence[MkosiConfig]) -> None:
+def run_verb(args: MkosiArgs, images: Sequence[MkosiConfig]) -> None:
if args.verb.needs_root() and os.getuid() != 0:
die(f"Must be root to run the {args.verb} command")
if args.verb == Verb.genkey:
return generate_key_cert_pair(args)
- if all(p == MkosiConfig.default() for p in presets):
+ if all(config == MkosiConfig.default() for config in images):
die("No configuration found",
hint="Make sure you're running mkosi from a directory with configuration files")
if args.verb == Verb.summary:
if args.json:
text = json.dumps(
- {"Presets": [p.to_dict() for p in presets]},
+ {"Images": [config.to_dict() for config in images]},
cls=MkosiJsonEncoder,
indent=4,
sort_keys=True
)
else:
- text = "\n".join(summary(p) for p in presets)
+ text = "\n".join(summary(config) for config in images)
page(text, args.pager)
return
- presets = finalize_tools(args, presets)
- last = presets[-1]
+ images = finalize_tools(args, images)
+ last = images[-1]
if args.verb in (Verb.shell, Verb.boot):
opname = "acquire shell in" if args.verb == Verb.shell else "boot"
if last.compress_output:
die(f"Sorry, can't {opname} a compressed image.")
- for config in presets:
+ for config in images:
if args.verb == Verb.build and not args.force:
check_outputs(config)
# execution are forcibly loaded early here.
try_import("importlib.readers")
try_import("importlib.resources.readers")
- for config in presets:
+ for config in images:
try_import(f"mkosi.distributions.{config.distribution}")
# After we unshare the user namespace, we might not have access to /dev/kvm or related device nodes anymore as
if Path(d).exists():
run(["mount", "--rbind", d, d, "--options", "ro"])
- # First, process all directory removals because otherwise if different presets share directories a later
- # preset could end up output generated by an earlier preset.
+ # First, process all directory removals because otherwise if different images share directories a later
+ # image build could end up deleting the output generated by an earlier image build.
- for config in presets:
+ for config in images:
if not needs_build(args, config) and args.verb != Verb.clean:
continue
build = False
- for config in presets:
+ for config in images:
check_inputs(config)
if not needs_build(args, config):
continue
with (
- complete_step(f"Building {config.preset or 'default'} image"),\
+ complete_step(f"Building {config.image or 'default'} image"),\
mount_tools(config.tools_tree),\
mount_passwd(),\
prepend_to_environ_path(config)\
@propagate_failed_return()
def main() -> None:
log_setup()
- args, presets = parse_config(sys.argv[1:])
+ args, images = parse_config(sys.argv[1:])
if args.debug:
faulthandler.enable()
try:
- run_verb(args, presets)
+ run_verb(args, images)
finally:
if sys.stderr.isatty() and shutil.which("tput"):
run(["tput", "cnorm"], check=False)
profile: Optional[str]
include: tuple[str, ...]
- presets: tuple[str, ...]
+ images: tuple[str, ...]
dependencies: tuple[str, ...]
distribution: Distribution
qemu_kernel: Optional[Path]
qemu_args: list[str]
- preset: Optional[str]
+ image: Optional[str]
def output_dir_or_cwd(self) -> Path:
return self.output_dir or Path.cwd()
match=config_make_string_matcher(),
),
MkosiConfigSetting(
- dest="presets",
- long="--preset",
- section="Preset",
+ dest="images",
+ compat_names=("Presets",),
+ long="--image",
+ section="Config",
parse=config_make_list_parser(delimiter=","),
- help="Specify which presets to build",
+ help="Specify which images to build",
),
MkosiConfigSetting(
dest="dependencies",
long="--dependency",
- section="Preset",
+ section="Config",
parse=config_make_list_parser(delimiter=","),
- help="Specify other presets that this preset depends on",
+ help="Specify other images that this image depends on",
),
MkosiConfigSetting(
dest="distribution",
return parser
-def resolve_deps(presets: Sequence[MkosiConfig], include: Sequence[str]) -> list[MkosiConfig]:
- graph = {p.preset: p.dependencies for p in presets}
+def resolve_deps(images: Sequence[MkosiConfig], include: Sequence[str]) -> list[MkosiConfig]:
+ graph = {config.image: config.dependencies for config in images}
if include:
- if any((missing := p) not in graph for p in include):
- die(f"No preset found with name {missing}")
+ if any((missing := i) not in graph for i in include):
+ die(f"No image found with name {missing}")
deps = set()
queue = [*include]
while queue:
- if (preset := queue.pop(0)) not in deps:
- deps.add(preset)
- queue.extend(graph[preset])
+ if (image := queue.pop(0)) not in deps:
+ deps.add(image)
+ queue.extend(graph[image])
- presets = [p for p in presets if p.preset in deps]
+ images = [config for config in images if config.image in deps]
- graph = {p.preset: p.dependencies for p in presets}
+ graph = {config.image: config.dependencies for config in images}
try:
order = list(graphlib.TopologicalSorter(graph).static_order())
except graphlib.CycleError as e:
- die(f"Preset dependency cycle detected: {' => '.join(e.args[1])}")
+ die(f"Image dependency cycle detected: {' => '.join(e.args[1])}")
- return sorted(presets, key=lambda p: order.index(p.preset))
+ return sorted(images, key=lambda i: order.index(i.image))
def parse_config(argv: Sequence[str] = ()) -> tuple[MkosiArgs, tuple[MkosiConfig, ...]]:
if path.exists():
logging.debug(f"Including configuration file {Path.cwd() / path}")
- for section, k, v in parse_ini(path, only_sections={s.section for s in SETTINGS}):
+ for section, k, v in parse_ini(path, only_sections={s.section for s in SETTINGS} | {"Preset"}):
name = k.removeprefix("@")
ns = namespace if k == name else defaults
for s in SETTINGS:
finalize_default(s, namespace, defaults)
- presets = []
+ images = []
namespace = argparse.Namespace()
defaults = argparse.Namespace()
if args.directory is not None:
parse_config(Path("."), namespace, defaults, profiles=True)
- finalize_default(SETTINGS_LOOKUP_BY_DEST["presets"], namespace, defaults)
- include = getattr(namespace, "presets")
- immutable_settings.add("Presets")
+ finalize_default(SETTINGS_LOOKUP_BY_DEST["images"], namespace, defaults)
+ include = getattr(namespace, "images")
+ immutable_settings.add("Images")
- if Path("mkosi.presets").exists():
- for p in Path("mkosi.presets").iterdir():
+ d: Optional[Path]
+ for d in (Path("mkosi.images"), Path("mkosi.presets")):
+ if Path(d).exists():
+ break
+ else:
+ d = None
+
+ if d:
+ for p in d.iterdir():
if not p.is_dir() and not p.suffix == ".conf":
continue
name = p.name.removesuffix(".conf")
if not name:
- die(f"{p} is not a valid preset name")
+ die(f"{p} is not a valid image name")
ns_copy = copy.deepcopy(namespace)
defaults_copy = copy.deepcopy(defaults)
- setattr(ns_copy, "preset", name)
+ setattr(ns_copy, "image", name)
with chdir(p if p.is_dir() else Path.cwd()):
if not parse_config(p if p.is_file() else Path("."), ns_copy, defaults_copy):
continue
finalize_defaults(ns_copy, defaults_copy)
- presets += [ns_copy]
+ images += [ns_copy]
- if not presets:
- setattr(namespace, "preset", None)
+ if not images:
+ setattr(namespace, "image", None)
finalize_defaults(namespace, defaults)
- presets = [namespace]
+ images = [namespace]
- if not presets:
- die("No presets defined in mkosi.presets/")
+ if not images:
+ die("No images defined in mkosi.images/")
- presets = [load_config(ns) for ns in presets]
- presets = resolve_deps(presets, include)
+ images = [load_config(ns) for ns in images]
+ images = resolve_deps(images, include)
- return args, tuple(presets)
+ return args, tuple(images)
def load_credentials(args: argparse.Namespace) -> dict[str, str]:
args.checksum = True
if args.output is None:
- args.output = args.image_id or args.preset or "image"
+ args.output = args.image_id or args.image or "image"
args.credentials = load_credentials(args)
args.kernel_command_line_extra = load_kernel_command_line_extra(args)
env = [f"{k}={v}" for k, v in config.environment.items()]
summary = f"""\
-{bold(f"PRESET: {config.preset or 'default'}")}
+{bold(f"IMAGE: {config.image or 'default'}")}
{bold("CONFIG")}:
Profile: {none_to_none(config.profile)}
Include: {line_join_list(config.include)}
-
- {bold("PRESET")}:
- Presets: {line_join_list(config.presets)}
+ Images: {line_join_list(config.images)}
Dependencies: {line_join_list(config.dependencies)}
{bold("DISTRIBUTION")}:
: Note that each path containing extra configuration is only parsed
once, even if included more than once with `Include=`.
-### [Preset] Section
+`Images=`, `--image=`
-`Presets=`, `--preset=`
-
-: If specified, only build the given presets. Can be specified multiple
- times to build multiple presets. All the given presets and their
- dependencies are built. If not specified, all presets are built. See
- the **Presets** section for more information.
+: If specified, only build the given image. Can be specified multiple
+ times to build multiple images. All the given images and their
+ dependencies are built. If not specified, all images are built. See
+ the **Building multiple images** section for more information.
: Note that this section only takes effect when specified in the global
- configuration files. It has no effect if specified as a preset
+ configuration files. It has no effect if specified as an image
specific setting.
`Dependencies=`, `--dependency=`
-: The presets that this preset depends on specified as a comma-separated
- list. All presets configured in this option will be built before this
- preset and will be pulled in as dependencies of this preset when
- `Presets=` is used.
+: The images that this image depends on specified as a comma-separated
+ list. All images configured in this option will be built before this
+ image and will be pulled in as dependencies of this image when
+ `Images=` is used.
### [Distribution] Section
given tools tree.
: If set to `default`, mkosi will automatically add an extra tools tree
- preset and use it as the tools tree. The following table shows for
+ image and use it as the tools tree. The following table shows for
which distributions default tools tree packages are defined and which
packages are included in those default tools trees:
- `/media`
- `/mnt`
-Then, for each preset, we execute the following steps:
+Then, for each image, we execute the following steps:
6. Copy package manager trees into the workspace
7. Copy base trees (`--base-tree=`) into the image
complete image builds are minimal, as only changed source files need to
be recompiled.
-# PRESETS
+# Building multiple images
-Presets allow building more than one image with mkosi. Presets are
-loaded from the `mkosi.presets/` directory. Presets can be either
-directories containing mkosi configuration files or regular files with
-the `.conf` extension.
+If the `mkosi.images/` directory exists, mkosi will load individual
+image configurations from it and build each of them. Image
+configurations can be either directories containing mkosi configuration
+files or regular files with the `.conf` extension.
-When presets are found in `mkosi.presets/`, mkosi will build the
-configured preset and its dependencies (or all of them if no presets
-were explicitly configured using `Presets=`). To add dependencies
-between presets, the `Dependencies=` setting can be used.
+When image configurations are found in `mkosi.images/`, mkosi will build
+the configured images and all of their dependencies (or all of them if
+no images were explicitly configured using `Images=`). To add
+dependencies between images, the `Dependencies=` setting can be used.
-When presets are defined, mkosi will first read the global configuration
-(configuration outside of the `mkosi.presets/` directory), followed by
-the preset specific configuration. This means that global configuration
-takes precedence over preset specific configuration.
+When images are defined, mkosi will first read the global configuration
+(configuration outside of the `mkosi.images/` directory), followed by
+the image specific configuration. This means that global configuration
+takes precedence over image specific configuration.
-Presets can refer to outputs of presets they depend on. Specifically,
+Images can refer to outputs of images they depend on. Specifically,
for the following options, mkosi will only check whether the inputs
-exist just before building the preset:
+exist just before building the image:
- `BaseTrees=`
- `PackageManagerTrees=`
- `ToolsTree=`
- `Initrds=`
-To refer to outputs of a preset's dependencies, simply configure any of
+To refer to outputs of a image's dependencies, simply configure any of
these options with a relative path to the output to use in the output
-directory of the dependency.
+directory of the dependency. Or use the `%O` specifier to refer to the
+output directory.
-A good example on how to use presets can be found in the [systemd
-repository](https://github.com/systemd/systemd/tree/main/mkosi.presets).
+A good example on how to build multiple images can be found in the
+[systemd](https://github.com/systemd/systemd/tree/main/mkosi.presets)
+repository.
# ENVIRONMENT VARIABLES
"FinalizeScripts": [],
"Format": "uki",
"Hostname": null,
+ "Image": "default",
"ImageId": "myimage",
"ImageVersion": "5",
+ "Images": [
+ "default",
+ "initrd"
+ ],
"Include": [],
"Incremental": false,
"InitrdPackages": [
"PrepareScripts": [
"/run/foo"
],
- "Preset": "default",
- "Presets": [
- "default",
- "initrd"
- ],
"Profile": "profile",
"QemuArgs": [],
"QemuCdrom": false,
extra_trees = [],
finalize_scripts = [],
hostname = None,
+ image = "default",
image_id = "myimage",
image_version = "5",
+ images = ("default", "initrd"),
include = tuple(),
incremental = False,
initrd_packages = ["clevis"],
passphrase = None,
postinst_scripts = [Path("/bar/qux")],
prepare_scripts = [Path("/run/foo")],
- preset = "default",
- presets = ("default", "initrd"),
profile = "profile",
qemu_args = [],
qemu_cdrom = False,