mirror of
https://github.com/dortania/OpenCore-Legacy-Patcher.git
synced 2026-04-13 20:28:21 +10:00
265 lines
10 KiB
Bash
Executable File
265 lines
10 KiB
Bash
Executable File
#!/usr/bin/env python3
|
|
|
|
# This script's main purpose is to handle the following:
|
|
# - Download PatcherSupportPkg resources
|
|
# - Convert payloads directory into DMG (GUI only)
|
|
# - Build Binary via Pyinstaller
|
|
# - Add Launcher.sh (TUI only)
|
|
# - Patch 'LC_VERSION_MIN_MACOSX' to OS X 10.10
|
|
# - Add commit data to Info.plist
|
|
|
|
# Copyright (C) 2022 - Mykola Grymalyuk
|
|
|
|
from pathlib import Path
|
|
import time
|
|
import argparse
|
|
import os
|
|
import subprocess
|
|
import plistlib
|
|
import time
|
|
import sys
|
|
|
|
from resources import constants
|
|
|
|
class create_binary:
|
|
|
|
def __init__(self):
|
|
start = time.time()
|
|
print("- Starting build script")
|
|
self.set_cwd()
|
|
self.args = self.parse_arguments()
|
|
|
|
self.preflight_processes()
|
|
self.build_binary()
|
|
self.postflight_processes()
|
|
print(f"- Build script completed in {str(round(time.time() - start, 2))} seconds")
|
|
|
|
def set_cwd(self):
|
|
os.chdir(Path(__file__).resolve().parent)
|
|
print(f"- Current Working Directory: \n\t{os.getcwd()}")
|
|
|
|
def parse_arguments(self):
|
|
parser = argparse.ArgumentParser(description='Builds OpenCore-Patcher binary')
|
|
parser.add_argument('--build_tui', action='store_true', help='Builds TUI binary, if ommited GUI binary is built')
|
|
parser.add_argument('--branch', type=str, help='Git branch name')
|
|
parser.add_argument('--commit', type=str, help='Git commit URL')
|
|
parser.add_argument('--commit_date', type=str, help='Git commit date')
|
|
parser.add_argument('--reset_binaries', action='store_true', help='Force redownload and imaging of payloads')
|
|
args = parser.parse_args()
|
|
return args
|
|
|
|
def setup_pathing(self):
|
|
python_path = sys.executable
|
|
python_binary = python_path.split("/")[-1]
|
|
python_bin_dir = python_path.strip(python_binary)
|
|
|
|
# macOS (using Python installed by homebrew (e.g. brew))
|
|
if f"/usr/local/opt/python@3." in sys.executable:
|
|
print(f"\t* NOTE: home(brew) python3 detected; using (sys.exec_prefix, python_path) ==> {sys.exec_prefix, python_path}")
|
|
# - under brew, pip3 will install pyinstall at:
|
|
# /usr/local/lib/python3.9/site-packages/pyinstaller
|
|
# and /usr/local/bin/pyinstaller stub to load and run.
|
|
|
|
pyinstaller_path = f"/usr/local/bin/pyinstaller"
|
|
else:
|
|
pyinstaller_path = f"{python_bin_dir}pyinstaller"
|
|
|
|
if not Path(pyinstaller_path).exists():
|
|
print(f" - pyinstaller not found:\n\t{pyinstaller_path}")
|
|
raise Exception("pyinstaller not found")
|
|
|
|
self.pyinstaller_path = pyinstaller_path
|
|
|
|
def preflight_processes(self):
|
|
print("- Starting preflight processes")
|
|
self.setup_pathing()
|
|
self.delete_extra_binaries()
|
|
self.download_resources()
|
|
if not self.args.build_tui:
|
|
# payloads.dmg is only needed for GUI builds
|
|
self.generate_paylods_dmg()
|
|
|
|
def postflight_processes(self):
|
|
print("- Starting postflight processes")
|
|
if self.args.build_tui:
|
|
self.move_launcher()
|
|
self.patch_load_command()
|
|
self.add_commit_data()
|
|
|
|
def build_binary(self):
|
|
if Path(f"./dist/OpenCore-Patcher.app").exists():
|
|
print("- Found OpenCore-Patcher.app, removing...")
|
|
rm_output = subprocess.run(
|
|
["rm", "-rf", "./dist/OpenCore-Patcher.app"],
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
|
)
|
|
if rm_output.returncode != 0:
|
|
print("- Remove failed")
|
|
print(rm_output.stderr.decode('utf-8'))
|
|
raise Exception("Remove failed")
|
|
|
|
|
|
if self.args.build_tui:
|
|
print("- Building TUI binary...")
|
|
build_args = [self.pyinstaller_path, "./OpenCore-Patcher.spec", "--noconfirm"]
|
|
else:
|
|
print("- Building GUI binary...")
|
|
build_args = [self.pyinstaller_path, "./OpenCore-Patcher-GUI.spec", "--noconfirm"]
|
|
|
|
build_result = subprocess.run(build_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
if build_result.returncode != 0:
|
|
print("- Build failed")
|
|
print(build_result.stderr.decode('utf-8'))
|
|
raise Exception("Build failed")
|
|
|
|
def delete_extra_binaries(self):
|
|
delete_files = [
|
|
"AutoPkg-Assets.pkg",
|
|
"AutoPkg-Assets.pkg.zip",
|
|
"InstallAssistant.pkg",
|
|
]
|
|
print("- Deleting extra binaries...")
|
|
for file in Path("payloads").glob(pattern="*"):
|
|
if file.name in delete_files:
|
|
print(f" - Deleting {file.name}")
|
|
file.unlink()
|
|
elif (Path(file) / Path("Contents/Resources/createinstallmedia")).exists():
|
|
print(f" - Deleting {file}")
|
|
subprocess.run(["rm", "-rf", file])
|
|
|
|
def download_resources(self):
|
|
patcher_support_pkg_version = constants.Constants().patcher_support_pkg_version
|
|
required_resources = [
|
|
"Universal-Binaries.zip"
|
|
]
|
|
|
|
print("- Downloading required resources...")
|
|
for resource in required_resources:
|
|
if Path(f"./payloads/{resource}").exists():
|
|
if self.args.reset_binaries:
|
|
print(f" - Removing old {resource}")
|
|
rm_output = subprocess.run(
|
|
["rm", "-rf", f"./payloads/{resource}"],
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
|
)
|
|
if rm_output.returncode != 0:
|
|
print("- Remove failed")
|
|
print(rm_output.stderr.decode('utf-8'))
|
|
raise Exception("Remove failed")
|
|
else:
|
|
print(f" - {resource} already exists, skipping download")
|
|
continue
|
|
print(f" - Downloading {resource}...")
|
|
|
|
download_result = subprocess.run(
|
|
[
|
|
"curl", "-LO",
|
|
f"https://github.com/dortania/PatcherSupportPkg/releases/download/{patcher_support_pkg_version}/{resource}"
|
|
],
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
|
)
|
|
|
|
if download_result.returncode != 0:
|
|
print(" - Download failed")
|
|
print(download_result.stderr.decode('utf-8'))
|
|
raise Exception("Download failed")
|
|
if not Path(f"./{resource}").exists():
|
|
print(f" - {resource} not found")
|
|
raise Exception(f"{resource} not found")
|
|
|
|
print(" - Moving into payloads")
|
|
mv_output = subprocess.run(["mv", resource, "./payloads/"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
if mv_output.returncode != 0:
|
|
print(" - Move failed")
|
|
print(mv_output.stderr.decode('utf-8'))
|
|
raise Exception("Move failed")
|
|
|
|
def generate_paylods_dmg(self):
|
|
if Path("./payloads.dmg").exists():
|
|
if self.args.reset_binaries:
|
|
print(" - Removing old payloads.dmg")
|
|
rm_output = subprocess.run(
|
|
["rm", "-rf", "./payloads.dmg"],
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
|
)
|
|
if rm_output.returncode != 0:
|
|
print("- Remove failed")
|
|
print(rm_output.stderr.decode('utf-8'))
|
|
raise Exception("Remove failed")
|
|
else:
|
|
print(" - payloads.dmg already exists, skipping creation")
|
|
return
|
|
print(" - Generating DMG...")
|
|
dmg_output = subprocess.run([
|
|
'hdiutil', 'create', './payloads.dmg',
|
|
'-megabytes', '32000',
|
|
'-format', 'UDZO', '-ov',
|
|
'-volname', 'payloads',
|
|
'-fs', 'HFS+',
|
|
'-srcfolder', './payloads',
|
|
'-passphrase', 'password', '-encryption'
|
|
], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
if dmg_output.returncode != 0:
|
|
print(" - DMG generation failed")
|
|
print(dmg_output.stderr.decode('utf-8'))
|
|
raise Exception("DMG generation failed")
|
|
|
|
print(" - DMG generation complete")
|
|
|
|
def add_commit_data(self):
|
|
if not self.args.branch and not self.args.commit and not self.args.commit_date:
|
|
print(" - No commit data provided, adding source info")
|
|
branch = "Built from source"
|
|
commit_url = ""
|
|
commit_date = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
|
|
else:
|
|
branch = self.args.branch
|
|
commit_url = self.args.commit
|
|
commit_date = self.args.commit_date
|
|
print(" - Adding commit data to Info.plist")
|
|
plist_path = Path("./dist/OpenCore-Patcher.app/Contents/Info.plist")
|
|
plist = plistlib.load(Path(plist_path).open("rb"))
|
|
plist["Github"] = {
|
|
"Branch": branch,
|
|
"Commit URL": commit_url,
|
|
"Commit Date": commit_date,
|
|
}
|
|
plistlib.dump(plist, Path(plist_path).open("wb"), sort_keys=True)
|
|
|
|
def patch_load_command(self):
|
|
# Patches LC_VERSION_MIN_MACOSX in Load Command to report 10.10
|
|
#
|
|
# By default Pyinstaller will create binaries supporting 10.13+
|
|
# However this limitation is entirely arbitrary for our libraries
|
|
# and instead we're able to support 10.10 without issues.
|
|
#
|
|
# To verify set version:
|
|
# otool -l ./dist/OpenCore-Patcher.app/Contents/MacOS/OpenCore-Patcher
|
|
#
|
|
# cmd LC_VERSION_MIN_MACOSX
|
|
# cmdsize 16
|
|
# version 10.13
|
|
# sdk 10.9
|
|
print(" - Patching LC_VERSION_MIN_MACOSX")
|
|
path = './dist/OpenCore-Patcher.app/Contents/MacOS/OpenCore-Patcher'
|
|
find = b'\x00\x0D\x0A\x00' # 10.13 (0xA0D)
|
|
replace = b'\x00\x0A\x0A\x00' # 10.10 (0xA0A)
|
|
with open(path, 'rb') as f:
|
|
data = f.read()
|
|
data = data.replace(find, replace, 1)
|
|
with open(path, 'wb') as f:
|
|
f.write(data)
|
|
|
|
def move_launcher(self):
|
|
print(" - Adding TUI launcher")
|
|
mv_output = subprocess.run(
|
|
["cp", "./payloads/launcher.sh", "./dist/OpenCore-Patcher.app/Contents/MacOS/Launcher"],
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
|
)
|
|
if mv_output.returncode != 0:
|
|
print(" - Move failed")
|
|
print(mv_output.stderr.decode('utf-8'))
|
|
raise Exception("Move failed")
|
|
|
|
if __name__ == "__main__":
|
|
create_binary() |