# Class for generating OpenCore Configurations tailored for Macs # Copyright (C) 2020-2023, Dhinak G, Mykola Grymalyuk import copy import pickle import plistlib import shutil import zipfile import logging from pathlib import Path from datetime import date from resources import constants, utilities from resources.build import bluetooth, firmware, graphics_audio, support, storage, smbios, security, misc from resources.build.networking import wired, wireless def rmtree_handler(func, path, exc_info) -> None: if exc_info[0] == FileNotFoundError: return raise # pylint: disable=misplaced-bare-raise class BuildOpenCore: """ Core Build Library for generating and validating OpenCore EFI Configurations compatible with genuine Macs """ def __init__(self, model: str, global_constants: constants.Constants) -> None: self.model: str = model self.config: dict = None self.constants: constants.Constants = global_constants self._build_opencore() def _build_efi(self) -> None: """ Build EFI folder """ utilities.cls() logging.info(f"Building Configuration {'for external' if self.constants.custom_model else 'on model'}: {self.model}") self._generate_base() self._set_revision() # Set Lilu and co. support.BuildSupport(self.model, self.constants, self.config).enable_kext("Lilu.kext", self.constants.lilu_version, self.constants.lilu_path) self.config["Kernel"]["Quirks"]["DisableLinkeditJettison"] = True # Call support functions for function in [ firmware.BuildFirmware, wired.BuildWiredNetworking, wireless.BuildWirelessNetworking, graphics_audio.BuildGraphicsAudio, bluetooth.BuildBluetooth, storage.BuildStorage, smbios.BuildSMBIOS, security.BuildSecurity, misc.BuildMiscellaneous ]: function(self.model, self.constants, self.config) # Work-around ocvalidate if self.constants.validate is False: logging.info("- Adding bootmgfw.efi BlessOverride") self.config["Misc"]["BlessOverride"] += ["\\EFI\\Microsoft\\Boot\\bootmgfw.efi"] def _generate_base(self) -> None: """ Generate OpenCore base folder and config """ if not Path(self.constants.build_path).exists(): logging.info("Creating build folder") Path(self.constants.build_path).mkdir() else: logging.info("Build folder already present, skipping") if Path(self.constants.opencore_zip_copied).exists(): logging.info("Deleting old copy of OpenCore zip") Path(self.constants.opencore_zip_copied).unlink() if Path(self.constants.opencore_release_folder).exists(): logging.info("Deleting old copy of OpenCore folder") shutil.rmtree(self.constants.opencore_release_folder, onerror=rmtree_handler, ignore_errors=True) logging.info("") logging.info(f"- Adding OpenCore v{self.constants.opencore_version} {self.constants.opencore_build}") shutil.copy(self.constants.opencore_zip_source, self.constants.build_path) zipfile.ZipFile(self.constants.opencore_zip_copied).extractall(self.constants.build_path) # Setup config.plist for editing logging.info("- Adding config.plist for OpenCore") shutil.copy(self.constants.plist_template, self.constants.oc_folder) self.config = plistlib.load(Path(self.constants.plist_path).open("rb")) def _set_revision(self) -> None: """ Set revision information in config.plist """ self.config["#Revision"]["Build-Version"] = f"{self.constants.patcher_version} - {date.today()}" if not self.constants.custom_model: self.config["#Revision"]["Build-Type"] = "OpenCore Built on Target Machine" computer_copy = copy.copy(self.constants.computer) computer_copy.ioregistry = None self.config["#Revision"]["Hardware-Probe"] = pickle.dumps(computer_copy) else: self.config["#Revision"]["Build-Type"] = "OpenCore Built for External Machine" self.config["#Revision"]["OpenCore-Version"] = f"{self.constants.opencore_version} - {self.constants.opencore_build}" self.config["#Revision"]["Original-Model"] = self.model self.config["NVRAM"]["Add"]["4D1FDA02-38C7-4A6A-9CC6-4BCCA8B30102"]["OCLP-Version"] = f"{self.constants.patcher_version}" self.config["NVRAM"]["Add"]["4D1FDA02-38C7-4A6A-9CC6-4BCCA8B30102"]["OCLP-Model"] = self.model def _save_config(self) -> None: """ Save config.plist to disk """ plistlib.dump(self.config, Path(self.constants.plist_path).open("wb"), sort_keys=True) def _build_opencore(self) -> None: """ Kick off the build process This is the main function: - Generates the OpenCore configuration - Cleans working directory - Signs files - Validates generated EFI """ # Generate OpenCore Configuration self._build_efi() if self.constants.allow_oc_everywhere is False or self.constants.allow_native_spoofs is True or (self.constants.custom_serial_number != "" and self.constants.custom_board_serial_number != ""): smbios.BuildSMBIOS(self.model, self.constants, self.config).set_smbios() support.BuildSupport(self.model, self.constants, self.config).cleanup() self._save_config() # Post-build handling support.BuildSupport(self.model, self.constants, self.config).sign_files() support.BuildSupport(self.model, self.constants, self.config).validate_pathing() logging.info("") logging.info(f"Your OpenCore EFI for {self.model} has been built at:") logging.info(f" {self.constants.opencore_release_folder}") logging.info("")