Moved to new git repo
This commit is contained in:
parent
5baf1dddd5
commit
51bbdfbef3
@ -1,55 +0,0 @@
|
||||
|
||||
macOS app bundling guide
|
||||
========================
|
||||
|
||||
Install Code Signing Certificate
|
||||
--------------------------------
|
||||
|
||||
* Go to https://developer.apple.com/account/resources/certificates/list
|
||||
* Download the Developer ID Application certificate.
|
||||
* Double click the file and add to key chain (default options).
|
||||
* Delete the file from the Downloads folder.
|
||||
|
||||
* You will also need to install a .p12 public/private key file for the
|
||||
certificate. This is only available for the owner of the Blender account,
|
||||
or can be exported and copied from another system that already has code
|
||||
signing set up.
|
||||
|
||||
Find the codesigning identity by running:
|
||||
|
||||
$ security find-identity -v -p codesigning
|
||||
|
||||
"Developer ID Application: Stichting Blender Foundation" is the identity needed.
|
||||
The long code at the start of the line is used as <identity> below.
|
||||
|
||||
Setup Apple ID
|
||||
--------------
|
||||
|
||||
* The Apple ID must have two step verification enabled.
|
||||
* Create an app specific password for the code signing app (label can be anything):
|
||||
https://support.apple.com/en-us/HT204397
|
||||
* Add the app specific password to keychain:
|
||||
|
||||
$ security add-generic-password -a <apple-id> -w <app-specific-password> -s altool-password
|
||||
|
||||
When running the bundle script, there will be a popup. To avoid that either:
|
||||
* Click Always Allow in the popup
|
||||
* In the Keychain Access app, change the Access Control settings on altool-password
|
||||
|
||||
Bundle
|
||||
------
|
||||
|
||||
Then the bundle is created as follows:
|
||||
|
||||
$ ./bundle.sh --source <sourcedir> --dmg <dmg> --bundle-id <bundleid> --username <apple-id> --password "@keychain:altool-password" --codesign <identity>
|
||||
|
||||
<sourcedir> directory where built Blender.app is
|
||||
<dmg> location and name of the final disk image
|
||||
<bundleid> id on notarization, for example org.blenderfoundation.blender.release
|
||||
<apple-id> your appleid email
|
||||
<identity> codesigning identity
|
||||
|
||||
When specifying only --sourcedir and --dmg, the build will not be signed.
|
||||
|
||||
Example :
|
||||
$ ./bundle.sh --source /data/build/bin --dmg /data/Blender-2.8-alpha-macOS-10.11.dmg --bundle-id org.blenderfoundation.blender.release --username "foo@mac.com" --password "@keychain:altool-password" --codesign AE825E26F12D08B692F360133210AF46F4CF7B97
|
@ -1,18 +0,0 @@
|
||||
tell application "Finder"
|
||||
tell disk "Blender"
|
||||
open
|
||||
set current view of container window to icon view
|
||||
set toolbar visible of container window to false
|
||||
set statusbar visible of container window to false
|
||||
set the bounds of container window to {100, 100, 640, 472}
|
||||
set theViewOptions to icon view options of container window
|
||||
set arrangement of theViewOptions to not arranged
|
||||
set icon size of theViewOptions to 128
|
||||
set background picture of theViewOptions to file ".background:background.tif"
|
||||
set position of item " " of container window to {400, 190}
|
||||
set position of item "blender.app" of container window to {135, 190}
|
||||
update without registering applications
|
||||
delay 5
|
||||
close
|
||||
end tell
|
||||
end tell
|
@ -1,212 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Script to create a macOS dmg file for Blender builds, including code
|
||||
# signing and notarization for releases.
|
||||
|
||||
# Check that we have all needed tools.
|
||||
for i in osascript git codesign hdiutil xcrun ; do
|
||||
if [ ! -x "$(which ${i})" ]; then
|
||||
echo "Unable to execute command $i, macOS broken?"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Defaults settings.
|
||||
_script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
_volume_name="Blender"
|
||||
_tmp_dir="$(mktemp -d)"
|
||||
_tmp_dmg="/tmp/blender-tmp.dmg"
|
||||
_background_image="${_script_dir}/background.tif"
|
||||
_mount_dir="/Volumes/${_volume_name}"
|
||||
_entitlements="${_script_dir}/entitlements.plist"
|
||||
|
||||
# Handle arguments.
|
||||
while [[ $# -gt 0 ]]; do
|
||||
key=$1
|
||||
case $key in
|
||||
-s|--source)
|
||||
SRC_DIR="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
-d|--dmg)
|
||||
DEST_DMG="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
-b|--bundle-id)
|
||||
N_BUNDLE_ID="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
-u|--username)
|
||||
N_USERNAME="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
-p|--password)
|
||||
N_PASSWORD="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
-c|--codesign)
|
||||
C_CERT="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
--background-image)
|
||||
_background_image="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
echo "Usage:"
|
||||
echo " $(basename "$0") --source DIR --dmg IMAGENAME "
|
||||
echo " optional arguments:"
|
||||
echo " --codesign <certname>"
|
||||
echo " --username <username>"
|
||||
echo " --password <password>"
|
||||
echo " --bundle-id <bundleid>"
|
||||
echo " Check https://developer.apple.com/documentation/security/notarizing_your_app_before_distribution/customizing_the_notarization_workflow "
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ ! -d "${SRC_DIR}/Blender.app" ]; then
|
||||
echo "use --source parameter to set source directory where Blender.app can be found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "${DEST_DMG}" ]; then
|
||||
echo "use --dmg parameter to set output dmg name"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Destroy destination dmg if there is any.
|
||||
test -f "${DEST_DMG}" && rm "${DEST_DMG}"
|
||||
if [ -d "${_mount_dir}" ]; then
|
||||
echo -n "Ejecting existing blender volume.."
|
||||
DEV_FILE=$(mount | grep "${_mount_dir}" | awk '{ print $1 }')
|
||||
diskutil eject "${DEV_FILE}" || exit 1
|
||||
echo
|
||||
fi
|
||||
|
||||
# Copy dmg contents.
|
||||
echo -n "Copying Blender.app..."
|
||||
cp -r "${SRC_DIR}/Blender.app" "${_tmp_dir}/" || exit 1
|
||||
echo
|
||||
|
||||
# Create the disk image.
|
||||
_directory_size=$(du -sh ${_tmp_dir} | awk -F'[^0-9]*' '$0=$1')
|
||||
_image_size=$(echo "${_directory_size}" + 400 | bc) # extra 400 need for codesign to work (why on earth?)
|
||||
|
||||
echo
|
||||
echo -n "Creating disk image of size ${_image_size}M.."
|
||||
test -f "${_tmp_dmg}" && rm "${_tmp_dmg}"
|
||||
hdiutil create -size "${_image_size}m" -fs HFS+ -srcfolder "${_tmp_dir}" -volname "${_volume_name}" -format UDRW "${_tmp_dmg}" -mode 755
|
||||
|
||||
echo "Mounting readwrite image..."
|
||||
hdiutil attach -readwrite -noverify -noautoopen "${_tmp_dmg}"
|
||||
|
||||
echo "Setting background picture.."
|
||||
if ! test -z "${_background_image}"; then
|
||||
echo "Copying background image ..."
|
||||
test -d "${_mount_dir}/.background" || mkdir "${_mount_dir}/.background"
|
||||
_background_image_NAME=$(basename "${_background_image}")
|
||||
cp "${_background_image}" "${_mount_dir}/.background/${_background_image_NAME}"
|
||||
fi
|
||||
|
||||
echo "Creating link to /Applications ..."
|
||||
ln -s /Applications "${_mount_dir}/Applications"
|
||||
echo "Renaming Applications to empty string."
|
||||
mv ${_mount_dir}/Applications "${_mount_dir}/ "
|
||||
|
||||
echo "Running applescript to set folder looks ..."
|
||||
cat "${_script_dir}/blender.applescript" | osascript
|
||||
|
||||
echo "Waiting after applescript ..."
|
||||
sleep 5
|
||||
|
||||
if [ ! -z "${C_CERT}" ]; then
|
||||
# Codesigning requires all libs and binaries to be signed separately.
|
||||
echo -n "Codesigning Python"
|
||||
for f in $(find "${_mount_dir}/Blender.app/Contents/Resources" -name "python*"); do
|
||||
if [ -x ${f} ] && [ ! -d ${f} ]; then
|
||||
codesign --remove-signature "${f}"
|
||||
codesign --timestamp --options runtime --entitlements="${_entitlements}" --sign "${C_CERT}" "${f}"
|
||||
fi
|
||||
done
|
||||
echo ; echo -n "Codesigning .dylib and .so libraries"
|
||||
for f in $(find "${_mount_dir}/Blender.app" -name "*.dylib" -o -name "*.so"); do
|
||||
codesign --remove-signature "${f}"
|
||||
codesign --timestamp --options runtime --entitlements="${_entitlements}" --sign "${C_CERT}" "${f}"
|
||||
done
|
||||
echo ; echo -n "Codesigning Blender.app"
|
||||
codesign --remove-signature "${_mount_dir}/Blender.app"
|
||||
codesign --timestamp --options runtime --entitlements="${_entitlements}" --sign "${C_CERT}" "${_mount_dir}/Blender.app"
|
||||
echo
|
||||
else
|
||||
echo "No codesigning cert given, skipping..."
|
||||
fi
|
||||
|
||||
# Need to eject dev files to remove /dev files and free .dmg for converting
|
||||
echo "Unmounting rw disk image ..."
|
||||
DEV_FILE=$(mount | grep "${_mount_dir}" | awk '{ print $1 }')
|
||||
diskutil eject "${DEV_FILE}"
|
||||
|
||||
sleep 3
|
||||
|
||||
echo "Compressing disk image ..."
|
||||
hdiutil convert "${_tmp_dmg}" -format UDZO -o "${DEST_DMG}"
|
||||
|
||||
# Codesign the dmg
|
||||
if [ ! -z "${C_CERT}" ]; then
|
||||
echo -n "Codesigning dmg..."
|
||||
codesign --timestamp --force --sign "${C_CERT}" "${DEST_DMG}"
|
||||
echo
|
||||
fi
|
||||
|
||||
# Cleanup
|
||||
rm -rf "${_tmp_dir}"
|
||||
rm "${_tmp_dmg}"
|
||||
|
||||
# Notarize
|
||||
if [ ! -z "${N_USERNAME}" ] && [ ! -z "${N_PASSWORD}" ] && [ ! -z "${N_BUNDLE_ID}" ]; then
|
||||
# Send to Apple
|
||||
echo "Sending ${DEST_DMG} for notarization..."
|
||||
_tmpout=$(mktemp)
|
||||
echo xcrun altool --notarize-app --verbose -f "${DEST_DMG}" --primary-bundle-id "${N_BUNDLE_ID}" --username "${N_USERNAME}" --password "${N_PASSWORD}"
|
||||
xcrun altool --notarize-app --verbose -f "${DEST_DMG}" --primary-bundle-id "${N_BUNDLE_ID}" --username "${N_USERNAME}" --password "${N_PASSWORD}" >${_tmpout} 2>&1
|
||||
|
||||
# Parse request uuid
|
||||
_requuid=$(cat "${_tmpout}" | grep "RequestUUID" | awk '{ print $3 }')
|
||||
echo "RequestUUID: ${_requuid}"
|
||||
if [ ! -z "${_requuid}" ]; then
|
||||
# Wait for Apple to confirm notarization is complete
|
||||
echo "Waiting for notarization to be complete.."
|
||||
for c in {20..0};do
|
||||
sleep 600
|
||||
xcrun altool --notarization-info "${_requuid}" --username "${N_USERNAME}" --password "${N_PASSWORD}" >${_tmpout} 2>&1
|
||||
_status=$(cat "${_tmpout}" | grep "Status:" | awk '{ print $2 }')
|
||||
if [ "${_status}" == "invalid" ]; then
|
||||
echo "Got invalid notarization!"
|
||||
break;
|
||||
fi
|
||||
|
||||
if [ "${_status}" == "success" ]; then
|
||||
echo -n "Notarization successful! Stapling..."
|
||||
xcrun stapler staple -v "${DEST_DMG}"
|
||||
break;
|
||||
fi
|
||||
echo "Notarization in progress, waiting..."
|
||||
done
|
||||
else
|
||||
cat ${_tmpout}
|
||||
echo "Error getting RequestUUID, notarization unsuccessful"
|
||||
fi
|
||||
else
|
||||
echo "No notarization credentials supplied, skipping..."
|
||||
fi
|
||||
|
||||
echo "..done. You should have ${DEST_DMG} ready to upload"
|
@ -1,38 +0,0 @@
|
||||
|
||||
Snap Package Instructions
|
||||
=========================
|
||||
|
||||
This folder contains the scripts for creating and uploading the snap on:
|
||||
https://snapcraft.io/blender
|
||||
|
||||
|
||||
Setup
|
||||
-----
|
||||
|
||||
This has only been tested to work on Ubuntu.
|
||||
|
||||
# Install required packages
|
||||
sudo apt install snapd snapcraft
|
||||
|
||||
|
||||
Steps
|
||||
-----
|
||||
|
||||
# Build the snap file
|
||||
python3 bundle.py --version 2.XX --url https://download.blender.org/release/Blender2.XX/blender-2.XX-x86_64.tar.bz2
|
||||
|
||||
# Install snap to test
|
||||
# --dangerous is needed since the snap has not been signed yet
|
||||
# --classic is required for installing Blender in general
|
||||
sudo snap install --dangerous --classic blender_2.XX_amd64.snap
|
||||
|
||||
# Upload
|
||||
snapcraft push --release=stable blender_2.XX_amd64.snap
|
||||
|
||||
|
||||
Release Values
|
||||
--------------
|
||||
|
||||
stable: final release
|
||||
candidate: release candidates
|
||||
|
@ -1,21 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import pathlib
|
||||
import subprocess
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--version", required=True)
|
||||
parser.add_argument("--url", required=True)
|
||||
parser.add_argument("--grade", default="stable", choices=["stable", "devel"])
|
||||
args = parser.parse_args()
|
||||
|
||||
yaml_text = pathlib.Path("snapcraft.yaml.in").read_text()
|
||||
yaml_text = yaml_text.replace("@VERSION@", args.version)
|
||||
yaml_text = yaml_text.replace("@URL@", args.url)
|
||||
yaml_text = yaml_text.replace("@GRADE@", args.grade)
|
||||
pathlib.Path("snapcraft.yaml").write_text(yaml_text)
|
||||
|
||||
subprocess.call(["snapcraft", "clean"])
|
||||
subprocess.call(["snapcraft", "snap"])
|
@ -1,53 +0,0 @@
|
||||
name: blender
|
||||
summary: Blender is the free and open source 3D creation suite.
|
||||
description: |
|
||||
Blender is the free and open source 3D creation suite. It supports the
|
||||
entirety of the 3D pipeline—modeling, rigging, animation, simulation,
|
||||
rendering, compositing and motion tracking, and video editing.
|
||||
|
||||
Blender is a public project, made by hundreds of people from around the
|
||||
world; by studios and individual artists, professionals and hobbyists,
|
||||
scientists, students, VFX experts, animators, game artists, modders, and
|
||||
the list goes on.
|
||||
|
||||
The standard snap channels are used in the following way:
|
||||
|
||||
stable - Latest stable release.
|
||||
candidate - Test builds for the upcoming stable release.
|
||||
|
||||
icon: ../icons/scalable/apps/blender.svg
|
||||
|
||||
passthrough:
|
||||
license: GPL-3.0
|
||||
|
||||
confinement: classic
|
||||
|
||||
apps:
|
||||
blender:
|
||||
command: ./blender-wrapper
|
||||
desktop: ./blender.desktop
|
||||
|
||||
version: '@VERSION@'
|
||||
grade: @GRADE@
|
||||
|
||||
parts:
|
||||
blender:
|
||||
plugin: dump
|
||||
source: @URL@
|
||||
build-attributes: [keep-execstack, no-patchelf]
|
||||
override-build: |
|
||||
snapcraftctl build
|
||||
sed -i 's|Icon=blender|Icon=${SNAP}/blender.svg|' ${SNAPCRAFT_PART_INSTALL}/blender.desktop
|
||||
stage-packages:
|
||||
- libxcb1
|
||||
- libxext6
|
||||
- libx11-6
|
||||
- libxi6
|
||||
- libxfixes3
|
||||
- libxrender1
|
||||
- libxxf86vm1
|
||||
wrapper:
|
||||
plugin: copy
|
||||
source: .
|
||||
files:
|
||||
blender-wrapper: blender-wrapper
|
@ -1,70 +0,0 @@
|
||||
Creating Steam builds for Blender
|
||||
=================================
|
||||
|
||||
This script automates creation of the Steam files: download of the archives,
|
||||
extraction of the archives, preparation of the build scripts (VDF files), actual
|
||||
building of the Steam game files.
|
||||
|
||||
Requirements
|
||||
============
|
||||
|
||||
* MacOS machine - Tested on Catalina 10.15.6. Extracting contents from the DMG
|
||||
archive did not work Windows nor on Linux using 7-zip. All DMG archives tested
|
||||
failed to be extracted. As such only MacOS is known to work.
|
||||
* Steam SDK downloaded from SteamWorks - The `steamcmd` is used to generate the
|
||||
Steam game files. The path to the `steamcmd` is what is actually needed.
|
||||
* SteamWorks credentials - Needed to log in using `steamcmd`.
|
||||
* Login to SteamWorks with the `steamcmd` from the command-line at least once -
|
||||
Needded to ensure the user is properly logged in. On a new machine the user
|
||||
will have to go through two-factor authentication.
|
||||
* App ID and Depot IDs - Needed to create the VDF files.
|
||||
* Python 3.x - 3.7 was tested.
|
||||
* Base URL - for downloading the archives.
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
```bash
|
||||
$ export STEAMUSER=SteamUserName
|
||||
$ export STEAMPW=SteamUserPW
|
||||
$ export BASEURL=https://download.blender.org/release/Blender2.83/
|
||||
$ export VERSION=2.83.3
|
||||
$ export APPID=appidnr
|
||||
$ export WINID=winidnr
|
||||
$ export LINID=linuxidnr
|
||||
$ export MACOSID=macosidnr
|
||||
|
||||
# log in to SteamWorks from command-line at least once
|
||||
|
||||
$ ../sdk/tools/ContentBuilder/builder_osx/steamcmd +login $STEAMUSER $STEAMPW
|
||||
|
||||
# once that has been done we can now actually start our tool
|
||||
|
||||
$ python3.7 create_steam_builds.py --baseurl $BASEURL --version $VERSION --appid $APPID --winid $WINID --linuxid $LINID --macosid $MACOSID --steamuser $STEAMUSER --steampw $STEAMPW --steamcmd ../sdk/tools/ContentBuilder/builder_osx/steamcmd
|
||||
```
|
||||
|
||||
All arguments in the above example are required.
|
||||
|
||||
At the start the tool will login using `steamcmd`. This is necessary to let the
|
||||
Steam SDK update itself if necessary.
|
||||
|
||||
There are a few optional arguments:
|
||||
|
||||
* `--dryrun`: If set building the game files will not actually happen. A set of
|
||||
log files and a preview manifest per depot will be created in the output folder.
|
||||
This can be used to double-check everything works as expected.
|
||||
* `--skipdl`: If set will skip downloading of the archives. The tool expects the
|
||||
archives to already exist in the correct content location.
|
||||
* `--skipextract`: If set will skip extraction of all archives. The tool expects
|
||||
the archives to already have been correctly extracted in the content location.
|
||||
|
||||
Run the tool with `-h` for detailed information on each argument.
|
||||
|
||||
The content and output folders are generated through appending the version
|
||||
without dots to the words `content` and `output` respectively, e.g. `content2833`
|
||||
and `output2833`. These folders are created next to the tool.
|
||||
|
||||
From all `.template` files the Steam build scripts will be generated also in the
|
||||
same directory as the tool. The files will have the extension `.vdf`.
|
||||
|
||||
In case of errors the tool will have a non-zero return code.
|
@ -1,17 +0,0 @@
|
||||
"appbuild"
|
||||
{
|
||||
"appid" "[APPID]"
|
||||
"desc" "Blender [VERSION]" // description for this build
|
||||
"buildoutput" "./[OUTPUT]" // build output folder for .log, .csm & .csd files, relative to location of this file
|
||||
"contentroot" "./[CONTENT]" // root content folder, relative to location of this file
|
||||
"setlive" "" // branch to set live after successful build, non if empty
|
||||
"preview" "[DRYRUN]" // 1 to enable preview builds, 0 to commit build to steampipe
|
||||
"local" "" // set to flie path of local content server
|
||||
|
||||
"depots"
|
||||
{
|
||||
"[WINID]" "depot_build_win.vdf"
|
||||
"[LINUXID]" "depot_build_linux.vdf"
|
||||
"[MACOSID]" "depot_build_macos.vdf"
|
||||
}
|
||||
}
|
@ -1,397 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import pathlib
|
||||
import requests
|
||||
import shutil
|
||||
import subprocess
|
||||
from typing import Callable, Iterator, List, Tuple
|
||||
|
||||
# supported archive and platform endings, used to create actual archive names
|
||||
archive_endings = ["windows64.zip", "linux64.tar.xz", "macOS.dmg"]
|
||||
|
||||
|
||||
def add_optional_argument(option: str, help: str) -> None:
|
||||
global parser
|
||||
"""Add an optional argument
|
||||
|
||||
Args:
|
||||
option (str): Option to add
|
||||
help (str): Help description for the argument
|
||||
"""
|
||||
parser.add_argument(option, help=help, action='store_const', const=1)
|
||||
|
||||
|
||||
def blender_archives(version: str) -> Iterator[str]:
|
||||
"""Generator for Blender archives for version.
|
||||
|
||||
Yields for items in archive_endings an archive name in the form of
|
||||
blender-{version}-{ending}.
|
||||
|
||||
Args:
|
||||
version (str): Version string of the form 2.83.2
|
||||
|
||||
|
||||
Yields:
|
||||
Iterator[str]: Name in the form of blender-{version}-{ending}
|
||||
"""
|
||||
global archive_endings
|
||||
|
||||
for ending in archive_endings:
|
||||
yield f"blender-{version}-{ending}"
|
||||
|
||||
|
||||
def get_archive_type(archive_type: str, version: str) -> str:
|
||||
"""Return the archive of given type and version.
|
||||
|
||||
Args:
|
||||
archive_type (str): extension for archive type to check for
|
||||
version (str): Version string in the form 2.83.2
|
||||
|
||||
Raises:
|
||||
Exception: Execption when archive type isn't found
|
||||
|
||||
Returns:
|
||||
str: archive name for given type
|
||||
"""
|
||||
|
||||
for archive in blender_archives(version):
|
||||
if archive.endswith(archive_type):
|
||||
return archive
|
||||
raise Exception("Unknown archive type")
|
||||
|
||||
|
||||
def execute_command(cmd: List[str], name: str, errcode: int, cwd=".", capture_output=True) -> str:
|
||||
"""Execute the given command.
|
||||
|
||||
Returns the process stdout upon success if any.
|
||||
|
||||
On error print message the command with name that has failed. Print stdout
|
||||
and stderr of the process if any, and then exit with given error code.
|
||||
|
||||
Args:
|
||||
cmd (List[str]): Command in list format, each argument as their own item
|
||||
name (str): Name of command to use when printing to command-line
|
||||
errcode (int): Error code to use in case of exit()
|
||||
cwd (str, optional): Folder to use as current work directory for command
|
||||
execution. Defaults to ".".
|
||||
capture_output (bool, optional): Whether to capture command output or not.
|
||||
Defaults to True.
|
||||
|
||||
Returns:
|
||||
str: stdout if any, or empty string
|
||||
"""
|
||||
cmd_process = subprocess.run(
|
||||
cmd, capture_output=capture_output, encoding="UTF-8", cwd=cwd)
|
||||
if cmd_process.returncode == 0:
|
||||
if cmd_process.stdout:
|
||||
return cmd_process.stdout
|
||||
else:
|
||||
return ""
|
||||
else:
|
||||
print(f"ERROR: {name} failed.")
|
||||
if cmd_process.stdout:
|
||||
print(cmd_process.stdout)
|
||||
if cmd_process.stderr:
|
||||
print(cmd_process.stderr)
|
||||
exit(errcode)
|
||||
return ""
|
||||
|
||||
|
||||
def download_archives(base_url: str, archives: Callable[[str], Iterator[str]], version: str, dst_dir: pathlib.Path):
|
||||
"""Download archives from the given base_url.
|
||||
|
||||
Archives is a generator for Blender archive names based on version.
|
||||
|
||||
Archive names are appended to the base_url to load from, and appended to
|
||||
dst_dir to save to.
|
||||
|
||||
Args:
|
||||
base_url (str): Base URL to load archives from
|
||||
archives (Callable[[str], Iterator[str]]): Generator for Blender archive
|
||||
names based on version
|
||||
version (str): Version string in the form of 2.83.2
|
||||
dst_dir (pathlib.Path): Download destination
|
||||
"""
|
||||
|
||||
if base_url[-1] != '/':
|
||||
base_url = base_url + '/'
|
||||
|
||||
for archive in archives(version):
|
||||
download_url = f"{base_url}{archive}"
|
||||
target_file = dst_dir.joinpath(archive)
|
||||
download_file(download_url, target_file)
|
||||
|
||||
|
||||
def download_file(from_url: str, to_file: pathlib.Path) -> None:
|
||||
"""Download from_url as to_file.
|
||||
|
||||
Actual downloading will be skipped if --skipdl is given on the command-line.
|
||||
|
||||
Args:
|
||||
from_url (str): Full URL to resource to download
|
||||
to_file (pathlib.Path): Full path to save downloaded resource as
|
||||
"""
|
||||
global args
|
||||
|
||||
if not args.skipdl or not to_file.exists():
|
||||
print(f"Downloading {from_url}")
|
||||
with open(to_file, "wb") as download_zip:
|
||||
response = requests.get(from_url)
|
||||
if response.status_code != requests.codes.ok:
|
||||
print(f"ERROR: failed to download {from_url} (status code: {response.status_code})")
|
||||
exit(1313)
|
||||
download_zip.write(response.content)
|
||||
else:
|
||||
print(f"Downloading {from_url} skipped")
|
||||
print(" ... OK")
|
||||
|
||||
|
||||
def copy_contents_from_dmg_to_path(dmg_file: pathlib.Path, dst: pathlib.Path) -> None:
|
||||
"""Copy the contents of the given DMG file to the destination folder.
|
||||
|
||||
Args:
|
||||
dmg_file (pathlib.Path): Full path to DMG archive to extract from
|
||||
dst (pathlib.Path): Full path to destination to extract to
|
||||
"""
|
||||
hdiutil_attach = ["hdiutil",
|
||||
"attach",
|
||||
"-readonly",
|
||||
f"{dmg_file}"
|
||||
]
|
||||
attached = execute_command(hdiutil_attach, "hdiutil attach", 1)
|
||||
|
||||
# Last line of output is what we want, it is of the form
|
||||
# /dev/somedisk Apple_HFS /Volumes/Blender
|
||||
# We want to retain the mount point, and the folder the mount is
|
||||
# created on. The mounted disk we need for detaching, the folder we
|
||||
# need to be able to copy the contents to where we can use them
|
||||
attachment_items = attached.splitlines()[-1].split()
|
||||
mounted_disk = attachment_items[0]
|
||||
source_location = pathlib.Path(attachment_items[2], "Blender.app")
|
||||
|
||||
print(f"{source_location} -> {dst}")
|
||||
|
||||
shutil.copytree(source_location, dst)
|
||||
|
||||
hdiutil_detach = ["hdiutil",
|
||||
"detach",
|
||||
f"{mounted_disk}"
|
||||
]
|
||||
execute_command(hdiutil_detach, "hdiutil detach", 2)
|
||||
|
||||
|
||||
def create_build_script(template_name: str, vars: List[Tuple[str, str]]) -> pathlib.Path:
|
||||
"""
|
||||
Create the Steam build script
|
||||
|
||||
Use the given template and template variable tuple list.
|
||||
|
||||
Returns pathlib.Path to the created script.
|
||||
|
||||
Args:
|
||||
template_name (str): [description]
|
||||
vars (List[Tuple[str, str]]): [description]
|
||||
|
||||
Returns:
|
||||
pathlib.Path: Full path to the generated script
|
||||
"""
|
||||
build_script = pathlib.Path(".", template_name).read_text()
|
||||
for var in vars:
|
||||
build_script = build_script.replace(var[0], var[1])
|
||||
build_script_file = template_name.replace(".template", "")
|
||||
build_script_path = pathlib.Path(".", build_script_file)
|
||||
build_script_path.write_text(build_script)
|
||||
return build_script_path
|
||||
|
||||
|
||||
def clean_up() -> None:
|
||||
"""Remove intermediate files depending on given command-line arguments
|
||||
"""
|
||||
global content_location, args
|
||||
|
||||
if not args.leavearch and not args.leaveextracted:
|
||||
shutil.rmtree(content_location)
|
||||
|
||||
if args.leavearch and not args.leaveextracted:
|
||||
shutil.rmtree(content_location.joinpath(zip_extract_folder))
|
||||
shutil.rmtree(content_location.joinpath(tarxz_extract_folder))
|
||||
shutil.rmtree(content_location.joinpath(dmg_extract_folder))
|
||||
|
||||
if args.leaveextracted and not args.leavearch:
|
||||
import os
|
||||
os.remove(content_location.joinpath(zipped_blender))
|
||||
os.remove(content_location.joinpath(tarxz_blender))
|
||||
os.remove(content_location.joinpath(dmg_blender))
|
||||
|
||||
|
||||
def extract_archive(archive: str, extract_folder_name: str,
|
||||
cmd: List[str], errcode: int) -> None:
|
||||
"""Extract all files from archive to given folder name.
|
||||
|
||||
Will not extract if
|
||||
target folder already exists, or if --skipextract was given on the
|
||||
command-line.
|
||||
|
||||
Args:
|
||||
archive (str): Archive name to extract
|
||||
extract_folder_name (str): Folder name to extract to
|
||||
cmd (List[str]): Command with arguments to use
|
||||
errcode (int): Error code to use for exit()
|
||||
"""
|
||||
global args, content_location
|
||||
|
||||
extract_location = content_location.joinpath(extract_folder_name)
|
||||
|
||||
pre_extract = set(content_location.glob("*"))
|
||||
|
||||
if not args.skipextract or not extract_location.exists():
|
||||
print(f"Extracting files from {archive}...")
|
||||
cmd.append(content_location.joinpath(archive))
|
||||
execute_command(cmd, cmd[0], errcode, cwd=content_location)
|
||||
# in case we use a non-release archive the naming will be incorrect.
|
||||
# simply rename to expected target name
|
||||
post_extract = set(content_location.glob("*"))
|
||||
diff_extract = post_extract - pre_extract
|
||||
if not extract_location in diff_extract:
|
||||
folder_to_rename = list(diff_extract)[0]
|
||||
folder_to_rename.rename(extract_location)
|
||||
print(" OK")
|
||||
else:
|
||||
print(f"Skipping extraction {archive}!")
|
||||
|
||||
# ==============================================================================
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
parser.add_argument("--baseurl", required=True,
|
||||
help="The base URL for files to download, "
|
||||
"i.e. https://download.blender.org/release/Blender2.83/")
|
||||
|
||||
parser.add_argument("--version", required=True,
|
||||
help="The Blender version to release, in the form 2.83.3")
|
||||
|
||||
parser.add_argument("--appid", required=True,
|
||||
help="The Blender App ID on Steam")
|
||||
parser.add_argument("--winid", required=True,
|
||||
help="The Windows depot ID")
|
||||
parser.add_argument("--linuxid", required=True,
|
||||
help="The Linux depot ID")
|
||||
parser.add_argument("--macosid", required=True,
|
||||
help="The MacOS depot ID")
|
||||
|
||||
parser.add_argument("--steamcmd", required=True,
|
||||
help="Path to the steamcmd")
|
||||
parser.add_argument("--steamuser", required=True,
|
||||
help="The login for the Steam builder user")
|
||||
parser.add_argument("--steampw", required=True,
|
||||
help="Login password for the Steam builder user")
|
||||
|
||||
add_optional_argument("--dryrun",
|
||||
"If set the Steam files will not be uploaded")
|
||||
add_optional_argument("--leavearch",
|
||||
help="If set don't clean up the downloaded archives")
|
||||
add_optional_argument("--leaveextracted",
|
||||
help="If set don't clean up the extraction folders")
|
||||
add_optional_argument("--skipdl",
|
||||
help="If set downloading the archives is skipped if it already exists locally.")
|
||||
add_optional_argument("--skipextract",
|
||||
help="If set skips extracting of archives. The tool assumes the archives"
|
||||
"have already been extracted to their correct locations")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
VERSIONNODOTS = args.version.replace('.', '')
|
||||
OUTPUT = f"output{VERSIONNODOTS}"
|
||||
CONTENT = f"content{VERSIONNODOTS}"
|
||||
|
||||
# ===== set up main locations
|
||||
|
||||
content_location = pathlib.Path(".", CONTENT).absolute()
|
||||
output_location = pathlib.Path(".", OUTPUT).absolute()
|
||||
|
||||
content_location.mkdir(parents=True, exist_ok=True)
|
||||
output_location.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# ===== login
|
||||
|
||||
# Logging into Steam once to ensure the SDK updates itself properly. If we don't
|
||||
# do that the combined +login and +run_app_build_http at the end of the tool
|
||||
# will fail.
|
||||
steam_login = [args.steamcmd,
|
||||
"+login",
|
||||
args.steamuser,
|
||||
args.steampw,
|
||||
"+quit"
|
||||
]
|
||||
print("Logging in to Steam...")
|
||||
execute_command(steam_login, "Login to Steam", 10)
|
||||
print(" OK")
|
||||
|
||||
# ===== prepare Steam build scripts
|
||||
|
||||
template_vars = [
|
||||
("[APPID]", args.appid),
|
||||
("[OUTPUT]", OUTPUT),
|
||||
("[CONTENT]", CONTENT),
|
||||
("[VERSION]", args.version),
|
||||
("[WINID]", args.winid),
|
||||
("[LINUXID]", args.linuxid),
|
||||
("[MACOSID]", args.macosid),
|
||||
("[DRYRUN]", f"{args.dryrun}" if args.dryrun else "0")
|
||||
]
|
||||
|
||||
blender_app_build = create_build_script(
|
||||
"blender_app_build.vdf.template", template_vars)
|
||||
create_build_script("depot_build_win.vdf.template", template_vars)
|
||||
create_build_script("depot_build_linux.vdf.template", template_vars)
|
||||
create_build_script("depot_build_macos.vdf.template", template_vars)
|
||||
|
||||
# ===== download archives
|
||||
|
||||
download_archives(args.baseurl, blender_archives,
|
||||
args.version, content_location)
|
||||
|
||||
# ===== set up file and folder names
|
||||
|
||||
zipped_blender = get_archive_type("zip", args.version)
|
||||
zip_extract_folder = zipped_blender.replace(".zip", "")
|
||||
tarxz_blender = get_archive_type("tar.xz", args.version)
|
||||
tarxz_extract_folder = tarxz_blender.replace(".tar.xz", "")
|
||||
dmg_blender = get_archive_type("dmg", args.version)
|
||||
dmg_extract_folder = dmg_blender.replace(".dmg", "")
|
||||
|
||||
# ===== extract
|
||||
|
||||
unzip_cmd = ["unzip", "-q"]
|
||||
extract_archive(zipped_blender, zip_extract_folder, unzip_cmd, 3)
|
||||
|
||||
untarxz_cmd = ["tar", "-xf"]
|
||||
extract_archive(tarxz_blender, tarxz_extract_folder, untarxz_cmd, 4)
|
||||
|
||||
if not args.skipextract or not content_location.joinpath(dmg_extract_folder).exists():
|
||||
print("Extracting files from Blender MacOS archive...")
|
||||
blender_dmg = content_location.joinpath(dmg_blender)
|
||||
target_location = content_location.joinpath(
|
||||
dmg_extract_folder, "Blender.app")
|
||||
copy_contents_from_dmg_to_path(blender_dmg, target_location)
|
||||
print(" OK")
|
||||
else:
|
||||
print("Skipping extraction of .dmg!")
|
||||
|
||||
# ===== building
|
||||
|
||||
print("Build Steam game files...")
|
||||
steam_build = [args.steamcmd,
|
||||
"+login",
|
||||
args.steamuser,
|
||||
args.steampw,
|
||||
"+run_app_build_http",
|
||||
blender_app_build.absolute(),
|
||||
"+quit"
|
||||
]
|
||||
execute_command(steam_build, "Build with steamcmd", 13)
|
||||
print(" OK")
|
||||
|
||||
clean_up()
|
@ -1,31 +0,0 @@
|
||||
"DepotBuildConfig"
|
||||
{
|
||||
// Set your assigned depot ID here
|
||||
"DepotID" "[LINUXID]"
|
||||
|
||||
// Set a root for all content.
|
||||
// All relative paths specified below (LocalPath in FileMapping entries, and FileExclusion paths)
|
||||
// will be resolved relative to this root.
|
||||
// If you don't define ContentRoot, then it will be assumed to be
|
||||
// the location of this script file, which probably isn't what you want
|
||||
"ContentRoot" "./blender-[VERSION]-linux64/"
|
||||
|
||||
// include all files recursivley
|
||||
"FileMapping"
|
||||
{
|
||||
// This can be a full path, or a path relative to ContentRoot
|
||||
"LocalPath" "*"
|
||||
|
||||
// This is a path relative to the install folder of your game
|
||||
"DepotPath" "."
|
||||
|
||||
// If LocalPath contains wildcards, setting this means that all
|
||||
// matching files within subdirectories of LocalPath will also
|
||||
// be included.
|
||||
"recursive" "1"
|
||||
}
|
||||
|
||||
// but exclude all symbol files
|
||||
// This can be a full path, or a path relative to ContentRoot
|
||||
"FileExclusion" "*.pdb"
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
"DepotBuildConfig"
|
||||
{
|
||||
// Set your assigned depot ID here
|
||||
"DepotID" "[MACOSID]"
|
||||
|
||||
// Set a root for all content.
|
||||
// All relative paths specified below (LocalPath in FileMapping entries, and FileExclusion paths)
|
||||
// will be resolved relative to this root.
|
||||
// If you don't define ContentRoot, then it will be assumed to be
|
||||
// the location of this script file, which probably isn't what you want
|
||||
"ContentRoot" "./blender-[VERSION]-macOS/"
|
||||
// include all files recursivley
|
||||
"FileMapping"
|
||||
{
|
||||
// This can be a full path, or a path relative to ContentRoot
|
||||
"LocalPath" "*"
|
||||
|
||||
// This is a path relative to the install folder of your game
|
||||
"DepotPath" "."
|
||||
|
||||
// If LocalPath contains wildcards, setting this means that all
|
||||
// matching files within subdirectories of LocalPath will also
|
||||
// be included.
|
||||
"recursive" "1"
|
||||
}
|
||||
|
||||
// but exclude all symbol files
|
||||
// This can be a full path, or a path relative to ContentRoot
|
||||
"FileExclusion" "*.pdb"
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
"DepotBuildConfig"
|
||||
{
|
||||
// Set your assigned depot ID here
|
||||
"DepotID" "[WINID]"
|
||||
|
||||
// Set a root for all content.
|
||||
// All relative paths specified below (LocalPath in FileMapping entries, and FileExclusion paths)
|
||||
// will be resolved relative to this root.
|
||||
// If you don't define ContentRoot, then it will be assumed to be
|
||||
// the location of this script file, which probably isn't what you want
|
||||
"ContentRoot" "./blender-[VERSION]-windows64/"
|
||||
|
||||
// include all files recursivley
|
||||
"FileMapping"
|
||||
{
|
||||
// This can be a full path, or a path relative to ContentRoot
|
||||
"LocalPath" "*"
|
||||
|
||||
// This is a path relative to the install folder of your game
|
||||
"DepotPath" "."
|
||||
|
||||
// If LocalPath contains wildcards, setting this means that all
|
||||
// matching files within subdirectories of LocalPath will also
|
||||
// be included.
|
||||
"recursive" "1"
|
||||
}
|
||||
|
||||
// but exclude all symbol files
|
||||
// This can be a full path, or a path relative to ContentRoot
|
||||
"FileExclusion" "*.pdb"
|
||||
}
|
@ -1,197 +0,0 @@
|
||||
#!/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")
|
||||
|
||||
pri_config_file = pathlib.Path(".", "priconfig.xml")
|
||||
pri_resources_file = pathlib.Path(content_folder, "resources.pri")
|
||||
|
||||
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.")
|
||||
|
||||
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"{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"Generating Package Resource Index (PRI) file using command: {' '.join(pri_command)}")
|
||||
execute_command(pri_command, "MakePri", 4)
|
||||
|
||||
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.")
|
Loading…
Reference in New Issue
Block a user