# This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/.
import concurrent.futures import lzma import plistlib import struct import subprocess from pathlib import Path from string import Template from typing import List from urllib.parse import quote
def get_apple_template(name: str) -> Template: """
Given <name>, open file at <TEMPLATE_DIRECTORY>/<name>, read contents and returnas a Template
Args:
name: str, Filename for the template
Returns:
Template, loaded from file """
tmpl_path = TEMPLATE_DIRECTORY / name ifnot tmpl_path.is_file(): raise Exception(f"Could not find template: {tmpl_path}") with tmpl_path.open("r") as tmpl:
contents = tmpl.read() return Template(contents)
def save_text_file(content: str, destination: Path): """
Saves a text file to <destination> with provided <content>
Note: Overwrites contents
Args:
content: str, The desired contents of the file
destination: Path, The file path """ with destination.open("w") as out_fd:
out_fd.write(content)
print(f"Created text file at {destination}")
print(f"Created text file size: {destination.stat().st_size} bytes")
def get_app_info_plist(app_path: Path) -> dict: """
Retrieve most information from Info.plist file of an app.
The Info.plist file should be located in ?.app/Contents/Info.plist
Note: Ignores properties that are not <string> type
Args:
app_path: Path, the .app file/directory path
Returns:
dict, the dictionary of properties found in Info.plist """
info_plist = app_path / "Contents/Info.plist" ifnot info_plist.is_file(): raise Exception(f"Could not find Info.plist in {info_plist}")
with tmp_payload_path.open("rb") as f_in, destination.open( "wb"
) as f_out, concurrent.futures.ThreadPoolExecutor(
max_workers=cpu_count()
) as executor:
f_out.write(b"pbzx")
f_out.write(struct.pack(">Q", PBZX_CHUNK_SIZE))
chunks = chunker(f_in, PBZX_CHUNK_SIZE) for uncompressed_size, compressed_chunk in executor.map(
compress_chunk, chunks
):
f_out.write(struct.pack(">Q", uncompressed_size)) if len(compressed_chunk) < uncompressed_size:
f_out.write(struct.pack(">Q", len(compressed_chunk)))
f_out.write(compressed_chunk) else: # Considering how unlikely this is, we prefer to just decompress # here than to keep the original uncompressed chunk around
f_out.write(struct.pack(">Q", uncompressed_size))
f_out.write(lzma.decompress(compressed_chunk))
Returns:
list[str], paths found in source directory """ return [f"./{c.relative_to(source)}"for c in source.glob(glob)]
def xar_package_folder(source_path: Path, destination: Path, xar_tool: Path): """
Create a pkg from <source_path> to <destination>
The command is issued with <source_path> as cwd
Args:
source_path: Path, source absolute Path
destination: Path, destination absolute Path
xar_tool: Path, xar tool Path """ ifnot source_path.is_absolute() ornot destination.is_absolute(): raise Exception("Source and destination should be absolute.")
print(f"Creating pkg from {source_path} to {destination}") # Create a list of ./<file> - noting xar takes care of <file>/**
file_list = get_relative_glob_list(source_path, "*")
# This seems arbitrary, there might be another way of doing it, # but Info.plist doesn't provide the simple version we need
major_version = app_info["CFBundleShortVersionString"].split(".")[0]
app_info["simple_version"] = f"{major_version}.0.0"
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.