diff --git a/release/scripts/addons b/release/scripts/addons index 1be0b3210d8..feca8c52897 160000 --- a/release/scripts/addons +++ b/release/scripts/addons @@ -1 +1 @@ -Subproject commit 1be0b3210d8a3a30e99a853b50703a7ca7e8ac1e +Subproject commit feca8c5289794a70bdd375be76fc4bc59d83c96b diff --git a/release/windows/msix/AppxManifest.xml.template b/release/windows/msix/AppxManifest.xml.template new file mode 100644 index 00000000000..9289a67efef --- /dev/null +++ b/release/windows/msix/AppxManifest.xml.template @@ -0,0 +1,60 @@ + + + + + Blender[LTSORNOT] + Blender Foundation + Blender [VERSION] is the Free and Open Source 3D creation suite + Assets\StoreLogo.png + + + + + + + + + + + + + + + + + + + + + + + + + + .blend + + + open + + + + + + + + + + + + diff --git a/release/windows/msix/Assets/Square150x150Logo.png b/release/windows/msix/Assets/Square150x150Logo.png new file mode 100644 index 00000000000..7dbbde793ba Binary files /dev/null and b/release/windows/msix/Assets/Square150x150Logo.png differ diff --git a/release/windows/msix/Assets/Square310x310Logo.png b/release/windows/msix/Assets/Square310x310Logo.png new file mode 100644 index 00000000000..2190415e35b Binary files /dev/null and b/release/windows/msix/Assets/Square310x310Logo.png differ diff --git a/release/windows/msix/Assets/Square44x44Logo.png b/release/windows/msix/Assets/Square44x44Logo.png new file mode 100644 index 00000000000..7b9ca070558 Binary files /dev/null and b/release/windows/msix/Assets/Square44x44Logo.png differ diff --git a/release/windows/msix/Assets/Square71x71Logo.png b/release/windows/msix/Assets/Square71x71Logo.png new file mode 100644 index 00000000000..75bd9bb264a Binary files /dev/null and b/release/windows/msix/Assets/Square71x71Logo.png differ diff --git a/release/windows/msix/Assets/StoreLogo.png b/release/windows/msix/Assets/StoreLogo.png new file mode 100644 index 00000000000..e8a671b7e0f Binary files /dev/null and b/release/windows/msix/Assets/StoreLogo.png differ diff --git a/release/windows/msix/Assets/Wide310x150Logo.png b/release/windows/msix/Assets/Wide310x150Logo.png new file mode 100644 index 00000000000..59f5736393a Binary files /dev/null and b/release/windows/msix/Assets/Wide310x150Logo.png differ diff --git a/release/windows/msix/README.md b/release/windows/msix/README.md new file mode 100644 index 00000000000..8967a655e3d --- /dev/null +++ b/release/windows/msix/README.md @@ -0,0 +1,81 @@ +create_msix_package +=================== + +This tool is used to create MSIX packages from a given ZiP archive. The MSIX +package is distributed mainly through the Microsoft Store. It can also be +installed when downloaded from blender.org. For that to work the MSIX package +needs to be signed. + +Requirements +============ + +* MakeAppX - this tool is distributed with Windows 10 SDK +* SignTool - this tool is also distributed with Windows 10 SDK +* Python 3 (3.7 or later tested) - to run the create_msix_package.py script +* requests module - can be installed with `pip install requests` +* PFX file (optional, but strongly recommended) - for signing the resulting MSIX + package. **NOTE:** If the MSIX package is not signed when uploaded to the Microsoft + store the validation and certification process can take up to three full + business day. + +Usage +===== + +On the command-line: +```batch +set VERSION=2.83.4.0 +set URL=https://download.blender.org/release/Blender2.83/blender-2.83.4-windows64.zip +set PUBID=CN=PUBIDHERE +set PFX=X:\path\to\cert.pfx +set PFXPW=pwhere + +python create_msix_package.py --version %VERSION% --url %URL% --publisher %PUBID% --pfx %PFX% --password %PFXPW% +``` + +Result will be a MSIX package with the name `blender-2.83.4-windows64.msix`. +With the above usage it will be signed. If the signing options are left out the +package will not be signed. + +Optional arguments +================== + +In support of testing and developing the manifest and scripts there are a few +optional arguments: + +* `--skipdl` : If a `blender.zip` is available already next to the tool use this + to skip actual downloading of the archive designated by `--url`. The latter + option is still required +* `--overwrite` : When script fails the final clean-up may be incomplete leaving + the `Content` folder with its structure. Specify this argument to automatically + clean up this folder before starting to seed the `Content` folder +* `--leavezip` : When specified leave the `blender.zip` file while cleaning up + all other intermediate files, including the `Content` folder. This is useful + to not have to re-download the same archive from `--url` on each usage + + +What it does +============ + +The tool creates in the directory it lives a subfolder called `Content`. This is +where all necessary files are placed. + +The `Assets` folder is copied to the `Content` folder. + +From the application manifest template a version with necessary parts replaced as +their actual values as specified on the command-line is realized. This manifest controls the packaging of Blender into the MSIX format. + +Next the tool downloads the designated ZIP archive locally as blender.zip. From +this archive the files are extracted into the `Content\Blender` folder, 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.4-windows64\blender.exe` + +Once the extraction is completed the MakeAppX tool is executed with the `Content` +folder as input. The result will be the MSIX package with the name in the form +`blender-X.YY.Z-windows64.msix`. + +If the PFX file and its password are given on the command-line this MSIX package +will be signed. + +All intermediate files and directories will be removed. diff --git a/release/windows/msix/create_msix_package.py b/release/windows/msix/create_msix_package.py new file mode 100644 index 00000000000..69b0a2ef901 --- /dev/null +++ b/release/windows/msix/create_msix_package.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 + +import argparse +import os +import pathlib +import requests +import shutil +import subprocess +import zipfile + +parser = argparse.ArgumentParser() +parser.add_argument("--version", required=True, help="Version string in the form of 2.83.3.0") +parser.add_argument("--url", required=True, help="Location of the release ZIP archive to download") +parser.add_argument("--publisher", required=True, help="A string in the form of 'CN=PUBLISHER'") +parser.add_argument("--pfx", required=False, help="Absolute path to the PFX file used for signing the resulting MSIX package") +parser.add_argument("--password", required=False, default="blender", help="Password for the PFX file") +parser.add_argument("--lts", required=False, help="If set this MSIX is for an LTS release", action='store_const', const=1) +parser.add_argument("--skipdl", required=False, help="If set skip downloading of the specified URL as blender.zip. The tool assumes blender.zip exists", action='store_const', const=1) +parser.add_argument("--leavezip", required=False, help="If set don't clean up the downloaded blender.zip", action='store_const', const=1) +parser.add_argument("--overwrite", required=False, help="If set remove Content folder if it already exists", action='store_const', const=1) +args = parser.parse_args() + +def execute_command(cmd : list, name : str, errcode : int): + """ + Execute given command in cmd. Output is captured. If an error + occurs name is used to print ERROR message, along with stderr and + stdout of the process if either was captured. + """ + cmd_process = subprocess.run(cmd, capture_output=True, encoding="UTF-8") + if cmd_process.returncode != 0: + print(f"ERROR: {name} failed.") + if cmd_process.stdout: print(cmd_process.stdout) + if cmd_process.stderr: print(cmd_process.stderr) + exit(errcode) + +LTSORNOT = "" +PACKAGETYPE = "" +if args.lts: + versionparts = args.version.split(".") + LTSORNOT = f" {versionparts[0]}.{versionparts[1]} LTS" + PACKAGETYPE = f"{versionparts[0]}.{versionparts[1]}LTS" + +blender_package_msix = pathlib.Path(".", f"blender-{args.version}-windows64.msix").absolute() +content_folder = pathlib.Path(".", "Content") +content_blender_folder = pathlib.Path(content_folder, "Blender").absolute() +content_assets_folder = pathlib.Path(content_folder, "Assets") +assets_original_folder = pathlib.Path(".", "Assets") + +local_blender_zip = pathlib.Path(".", "blender.zip") + +if args.pfx: + pfx_path = pathlib.Path(args.pfx) + if not pfx_path.exists(): + print("ERROR: PFX file not found. Please ensure you give the correct path to the PFX file on the command-line.") + exit(1) + print(f"Creating MSIX package with signing using PFX file at {pfx_path}") +else: + pfx_path = None + print("Creating MSIX package without signing.") + +msix_command = ["makeappx", + "pack", + "/h", "SHA256", + "/d", f"{content_folder.absolute()}", + "/p", f"{blender_package_msix}" + ] +if pfx_path: + sign_command = ["signtool", + "sign", + "/fd", "sha256", + "/a", "/f", f"{pfx_path.absolute()}", + "/p", f"{args.password}", + f"{blender_package_msix}" + ] + +if args.overwrite: + if content_folder.joinpath("Assets").exists(): + shutil.rmtree(content_folder) +content_folder.mkdir(exist_ok=True) +shutil.copytree(assets_original_folder, content_assets_folder) + +manifest_text = pathlib.Path("AppxManifest.xml.template").read_text() +manifest_text = manifest_text.replace("[VERSION]", args.version) +manifest_text = manifest_text.replace("[PUBLISHER]", args.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) + +if not args.skipdl: + print(f"Downloading blender archive {args.url} to {local_blender_zip}...") + + with open(local_blender_zip, "wb") as download_zip: + response = requests.get(args.url) + download_zip.write(response.content) + + print("... download complete.") +else: + print("Skipping download") + +print(f"Extracting files from ZIP to {content_blender_folder}...") + +# 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(local_blender_zip, "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) + +print("... extraction complete.") + + + +print(f"Creating MSIX package using command: {' '.join(msix_command)}") + +# Remove MSIX file if it already exists. Otherwise the MakeAppX tool +# will hang. +if blender_package_msix.exists(): + os.remove(blender_package_msix) +execute_command(msix_command, "MakeAppX", 2) + +if args.pfx: + print(f"Signing MSIX package using command: {' '.join(sign_command)}") + execute_command(sign_command, "SignTool", 3) + +if not args.leavezip: + os.remove(local_blender_zip) +shutil.rmtree(content_folder) + +print("Done.")