# SPDX-License-Identifier: GPL-2.0-or-later # SPDX-FileCopyrightText: 2011-2024 Blender Authors # import pathlib import zipfile import worker.utils def pack( # Version string in the form of 2.83.3.0, this is used in the Store package name version: str, # Input file path input_file_path: pathlib.Path, # A string in the form of 'CN=PUBLISHER' publisher: str, # If set this MSIX is for an LTS release lts: bool = False, # If set remove Content folder if it already exists overwrite: bool = False, # Don't actually execute commands dry_run: bool = False, ) -> pathlib.Path: LTSORNOT = "" PACKAGETYPE = "" if lts: versionparts = version.split(".") LTSORNOT = f" {versionparts[0]}.{versionparts[1]} LTS" PACKAGETYPE = f"{versionparts[0]}.{versionparts[1]}LTS" output_package_file_name = f"{input_file_path.stem}.msix" output_package_file_path = pathlib.Path(".", output_package_file_name) content_folder = pathlib.Path(".", "Content") content_blender_folder = pathlib.Path(content_folder, "Blender") content_assets_folder = pathlib.Path(content_folder, "Assets") assets_original_folder = pathlib.Path(".", "Assets") pri_config_file = pathlib.Path(".", "priconfig.xml") pri_resources_file = pathlib.Path(content_folder, "resources.pri") pri_command = [ "makepri", "new", "/pr", f"{content_folder.absolute()}", "/cf", f"{pri_config_file.absolute()}", "/of", f"{pri_resources_file.absolute()}", ] msix_command = [ "makeappx", "pack", "/h", "sha256", "/d", f"{content_folder.absolute()}", "/p", f"{output_package_file_path.absolute()}", ] if overwrite: if content_folder.joinpath("Assets").exists(): worker.utils.remove_dir(content_folder) content_folder.mkdir(exist_ok=True) worker.utils.copy_dir(assets_original_folder, content_assets_folder) manifest_text = pathlib.Path("AppxManifest.xml.template").read_text() manifest_text = manifest_text.replace("[VERSION]", version) manifest_text = manifest_text.replace("[PUBLISHER]", publisher) manifest_text = manifest_text.replace("[LTSORNOT]", LTSORNOT) manifest_text = manifest_text.replace("[PACKAGETYPE]", PACKAGETYPE) pathlib.Path(content_folder, "AppxManifest.xml").write_text(manifest_text) worker.utils.info( f"Extracting files from [{input_file_path}] to [{content_blender_folder.absolute()}]" ) # Extract the files from the ZIP archive, but skip the leading part of paths # in the ZIP. We want to write the files to the content_blender_folder where # blender.exe ends up as ./Content/Blender/blender.exe, and not # ./Content/Blender/blender-2.83.3-windows64/blender.exe with zipfile.ZipFile(input_file_path, "r") as blender_zip: for entry in blender_zip.infolist(): if entry.is_dir(): continue entry_location = pathlib.Path(entry.filename) target_location = content_blender_folder.joinpath(*entry_location.parts[1:]) pathlib.Path(target_location.parent).mkdir(parents=True, exist_ok=True) extracted_entry = blender_zip.read(entry) target_location.write_bytes(extracted_entry) worker.utils.info("... extraction complete.") worker.utils.info("Generating Package Resource Index (PRI) file") worker.utils.call(pri_command, dry_run=dry_run) worker.utils.info(f"Creating MSIX package using command: {' '.join(msix_command)}") # Remove MSIX file if it already exists. Otherwise the MakeAppX tool # will hang. worker.utils.remove_file(output_package_file_path) worker.utils.call(msix_command, dry_run=dry_run) if dry_run: output_package_file_path.write_text("Dry run dummy package file") worker.utils.remove_dir(content_folder) worker.utils.info("Done.") return output_package_file_path