## @file
# Copyright (c) 2023, The OCE Build Authors. All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause
##
"""Methods for retrieving and handling config.plist files and patches."""
from functools import partial
from typing import Dict, List, Optional, Tuple, Union
from ocebuild.parsers.dict import merge_dict, nested_del, nested_get, nested_set
from ocebuild.parsers.plist import parse_plist
from ocebuild.parsers.schema import parse_schema
from ocebuild.parsers.yaml import parse_yaml
from ocebuild.sources import request
from ocebuild.sources.github import github_file_url
#NOTE: This import was remapped from 'third_party' to 'ocebuild.third_party'.
from ocebuild.third_party.cpython.pathlib import Path
[docs]def read_config(filepath: str,
frontmatter: bool=False,
flags: Optional[List[str]]=None
) -> Tuple[dict, Union[dict, None]]:
"""Reads a configuration file.
Args:
filepath: The path to the configuration file.
frontmatter: Whether to include the file's frontmatter.
flags: The flags to apply to the configuration file.
Raises:
ValueError: If the file extension is not supported.
Returns:
The configuration file.
If `frontmatter` is `True`, a tuple containing:
- The configuration file.
- The frontmatter of the configuration file.
"""
if not flags: flags = []
with open(filepath, 'r', encoding='UTF-8') as f:
file_ext = Path(filepath).suffix
if file_ext in ('.plist'):
file = parse_plist(f)
if frontmatter:
return file, None
elif file_ext in ('.yml', '.yaml'):
if frontmatter:
file, frontmatter = parse_yaml(f, flags=flags, frontmatter=True)
return file, frontmatter
else:
file = parse_yaml(f, flags=flags)
else:
raise ValueError(f"Unsupported file extension: {file_ext}")
return file
[docs]def merge_configs(base: Union[str, Path],
*patches: Union[str, Path],
flags: Optional[List[str]]=None
) -> Dict:
"""Merges a set of plist or yaml config files into a single config.
Args:
base: The base config file.
*patches: The patch config files.
flags: The flags to apply to the configuration file.
Returns:
The merged config.
Raises:
ValueError: If a patch file is not a plist or yaml file.
Example:
>>> merge_configs('base.plist', 'patch1.yml', 'patch2.plist', 'patch2.yaml')
{...}
"""
base_config, _ = read_config(base)
if not flags: flags = []
# Parse config patches
for filepath in patches:
patch, frontmatter = read_config(filepath, flags=flags, frontmatter=True)
if isinstance(frontmatter, dict):
flags += nested_get(frontmatter, ['flags'], default=[])
if tags := nested_get(frontmatter, ['tags']):
apply_preprocessor_tags(base_config, patch, tags)
base_config = merge_dict(base_config, patch)
return base_config
[docs]def get_configuration_schema(repository: str='acidanthera/OpenCorePkg',
branch: str = 'master',
tag: Union[str, None] = None,
commit: Union[str, None] = None,
get_sample: bool=False,
**kwargs
) -> Union[dict, Tuple[dict, dict]]:
"""Reads the Sample.plist schema from a OpenCorePkg version."""
# Resolve file urls for the given repository parameters.
file_url = partial(github_file_url,
repository=repository,
branch=branch,
tag=tag,
commit=commit,
raw=True)
# Get the reference configuration and sample plist urls
configuration_url = file_url(path='Docs/Configuration.tex')
sample_plist_url = file_url(path='Docs/Sample.plist')
sample_plist = parse_plist(request(url=sample_plist_url).text())
with request(url=configuration_url).text() as file:
schema = parse_schema(file, sample_plist, **kwargs)
if get_sample: return schema, sample_plist
return schema
#TODO: Sort kexts by dependency order, including support for:
# - Including existing entry properties (pointing to an existing `BundlePath`)
# - Disable entries on:
# - Duplicate CFBundleIdentifiers with overlapping Max/MinKernel ranges
# - Missing CFBundleIdentifiers (i.e. unresolved dependencies)
# - Missing CFBundleIdentifiers/CFBundleExecutable fields in Info.plist
__all__ = [
# Functions (4)
"read_config",
"apply_preprocessor_tags",
"merge_configs",
"get_configuration_schema"
]