Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'build_files/buildbot/worker_bundle_dmg.py')
-rwxr-xr-xbuild_files/buildbot/worker_bundle_dmg.py551
1 files changed, 0 insertions, 551 deletions
diff --git a/build_files/buildbot/worker_bundle_dmg.py b/build_files/buildbot/worker_bundle_dmg.py
deleted file mode 100755
index 56e0d7da88e..00000000000
--- a/build_files/buildbot/worker_bundle_dmg.py
+++ /dev/null
@@ -1,551 +0,0 @@
-#!/usr/bin/env python3
-
-# ##### BEGIN GPL LICENSE BLOCK #####
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# ##### END GPL LICENSE BLOCK #####
-
-import argparse
-import re
-import shutil
-import subprocess
-import sys
-import time
-
-from pathlib import Path
-from tempfile import TemporaryDirectory, NamedTemporaryFile
-from typing import List
-
-BUILDBOT_DIRECTORY = Path(__file__).absolute().parent
-CODESIGN_SCRIPT = BUILDBOT_DIRECTORY / 'worker_codesign.py'
-BLENDER_GIT_ROOT_DIRECTORY = BUILDBOT_DIRECTORY.parent.parent
-DARWIN_DIRECTORY = BLENDER_GIT_ROOT_DIRECTORY / 'release' / 'darwin'
-
-
-# Extra size which is added on top of actual files size when estimating size
-# of destination DNG.
-EXTRA_DMG_SIZE_IN_BYTES = 800 * 1024 * 1024
-
-################################################################################
-# Common utilities
-
-
-def get_directory_size(root_directory: Path) -> int:
- """
- Get size of directory on disk
- """
-
- total_size = 0
- for file in root_directory.glob('**/*'):
- total_size += file.lstat().st_size
- return total_size
-
-
-################################################################################
-# DMG bundling specific logic
-
-def create_argument_parser():
- parser = argparse.ArgumentParser()
- parser.add_argument(
- 'source_dir',
- type=Path,
- help='Source directory which points to either existing .app bundle'
- 'or to a directory with .app bundles.')
- parser.add_argument(
- '--background-image',
- type=Path,
- help="Optional background picture which will be set on the DMG."
- "If not provided default Blender's one is used.")
- parser.add_argument(
- '--volume-name',
- type=str,
- help='Optional name of a volume which will be used for DMG.')
- parser.add_argument(
- '--dmg',
- type=Path,
- help='Optional argument which points to a final DMG file name.')
- parser.add_argument(
- '--applescript',
- type=Path,
- help="Optional path to applescript to set up folder looks of DMG."
- "If not provided default Blender's one is used.")
- parser.add_argument(
- '--codesign',
- action="store_true",
- help="Code sign and notarize DMG contents.")
- return parser
-
-
-def collect_app_bundles(source_dir: Path) -> List[Path]:
- """
- Collect all app bundles which are to be put into DMG
-
- If the source directory points to FOO.app it will be the only app bundle
- packed.
-
- Otherwise all .app bundles from given directory are placed to a single
- DMG.
- """
-
- if source_dir.name.endswith('.app'):
- return [source_dir]
-
- app_bundles = []
- for filename in source_dir.glob('*'):
- if not filename.is_dir():
- continue
- if not filename.name.endswith('.app'):
- continue
-
- app_bundles.append(filename)
-
- return app_bundles
-
-
-def collect_and_log_app_bundles(source_dir: Path) -> List[Path]:
- app_bundles = collect_app_bundles(source_dir)
-
- if not app_bundles:
- print('No app bundles found for packing')
- return
-
- print(f'Found {len(app_bundles)} to pack:')
- for app_bundle in app_bundles:
- print(f'- {app_bundle}')
-
- return app_bundles
-
-
-def estimate_dmg_size(app_bundles: List[Path]) -> int:
- """
- Estimate size of DMG to hold requested app bundles
-
- The size is based on actual size of all files in all bundles plus some
- space to compensate for different size-on-disk plus some space to hold
- codesign signatures.
-
- Is better to be on a high side since the empty space is compressed, but
- lack of space might cause silent failures later on.
- """
-
- app_bundles_size = 0
- for app_bundle in app_bundles:
- app_bundles_size += get_directory_size(app_bundle)
-
- return app_bundles_size + EXTRA_DMG_SIZE_IN_BYTES
-
-
-def copy_app_bundles_to_directory(app_bundles: List[Path],
- directory: Path) -> None:
- """
- Copy all bundles to a given directory
-
- This directory is what the DMG will be created from.
- """
- for app_bundle in app_bundles:
- print(f'Copying {app_bundle.name}...')
- shutil.copytree(app_bundle, directory / app_bundle.name)
-
-
-def get_main_app_bundle(app_bundles: List[Path]) -> Path:
- """
- Get application bundle main for the installation
- """
- return app_bundles[0]
-
-
-def create_dmg_image(app_bundles: List[Path],
- dmg_filepath: Path,
- volume_name: str) -> None:
- """
- Create DMG disk image and put app bundles in it
-
- No DMG configuration or codesigning is happening here.
- """
-
- if dmg_filepath.exists():
- print(f'Removing existing writable DMG {dmg_filepath}...')
- dmg_filepath.unlink()
-
- print('Preparing directory with app bundles for the DMG...')
- with TemporaryDirectory(prefix='blender-dmg-content-') as content_dir_str:
- # Copy all bundles to a clean directory.
- content_dir = Path(content_dir_str)
- copy_app_bundles_to_directory(app_bundles, content_dir)
-
- # Estimate size of the DMG.
- dmg_size = estimate_dmg_size(app_bundles)
- print(f'Estimated DMG size: {dmg_size:,} bytes.')
-
- # Create the DMG.
- print(f'Creating writable DMG {dmg_filepath}')
- command = ('hdiutil',
- 'create',
- '-size', str(dmg_size),
- '-fs', 'HFS+',
- '-srcfolder', content_dir,
- '-volname', volume_name,
- '-format', 'UDRW',
- dmg_filepath)
- subprocess.run(command)
-
-
-def get_writable_dmg_filepath(dmg_filepath: Path):
- """
- Get file path for writable DMG image
- """
- parent = dmg_filepath.parent
- return parent / (dmg_filepath.stem + '-temp.dmg')
-
-
-def mount_readwrite_dmg(dmg_filepath: Path) -> None:
- """
- Mount writable DMG
-
- Mounting point would be /Volumes/<volume name>
- """
-
- print(f'Mounting read-write DMG ${dmg_filepath}')
- command = ('hdiutil',
- 'attach', '-readwrite',
- '-noverify',
- '-noautoopen',
- dmg_filepath)
- subprocess.run(command)
-
-
-def get_mount_directory_for_volume_name(volume_name: str) -> Path:
- """
- Get directory under which the volume will be mounted
- """
-
- return Path('/Volumes') / volume_name
-
-
-def eject_volume(volume_name: str) -> None:
- """
- Eject given volume, if mounted
- """
- mount_directory = get_mount_directory_for_volume_name(volume_name)
- if not mount_directory.exists():
- return
- mount_directory_str = str(mount_directory)
-
- print(f'Ejecting volume {volume_name}')
-
- # Figure out which device to eject.
- mount_output = subprocess.check_output(['mount']).decode()
- device = ''
- for line in mount_output.splitlines():
- if f'on {mount_directory_str} (' not in line:
- continue
- tokens = line.split(' ', 3)
- if len(tokens) < 3:
- continue
- if tokens[1] != 'on':
- continue
- if device:
- raise Exception(
- f'Multiple devices found for mounting point {mount_directory}')
- device = tokens[0]
-
- if not device:
- raise Exception(
- f'No device found for mounting point {mount_directory}')
-
- print(f'{mount_directory} is mounted as device {device}, ejecting...')
- subprocess.run(['diskutil', 'eject', device])
-
-
-def copy_background_if_needed(background_image_filepath: Path,
- mount_directory: Path) -> None:
- """
- Copy background to the DMG
-
- If the background image is not specified it will not be copied.
- """
-
- if not background_image_filepath:
- print('No background image provided.')
- return
-
- print(f'Copying background image {background_image_filepath}')
-
- destination_dir = mount_directory / '.background'
- destination_dir.mkdir(exist_ok=True)
-
- destination_filepath = destination_dir / background_image_filepath.name
- shutil.copy(background_image_filepath, destination_filepath)
-
-
-def create_applications_link(mount_directory: Path) -> None:
- """
- Create link to /Applications in the given location
- """
-
- print('Creating link to /Applications')
-
- command = ('ln', '-s', '/Applications', mount_directory / ' ')
- subprocess.run(command)
-
-
-def run_applescript(applescript: Path,
- volume_name: str,
- app_bundles: List[Path],
- background_image_filepath: Path) -> None:
- """
- Run given applescript to adjust look and feel of the DMG
- """
-
- main_app_bundle = get_main_app_bundle(app_bundles)
-
- with NamedTemporaryFile(
- mode='w', suffix='.applescript') as temp_applescript:
- print('Adjusting applescript for volume name...')
- # Adjust script to the specific volume name.
- with open(applescript, mode='r') as input:
- for line in input.readlines():
- stripped_line = line.strip()
- if stripped_line.startswith('tell disk'):
- line = re.sub('tell disk ".*"',
- f'tell disk "{volume_name}"',
- line)
- elif stripped_line.startswith('set background picture'):
- if not background_image_filepath:
- continue
- else:
- background_image_short = \
- '.background:' + background_image_filepath.name
- line = re.sub('to file ".*"',
- f'to file "{background_image_short}"',
- line)
- line = line.replace('blender.app', main_app_bundle.name)
- temp_applescript.write(line)
-
- temp_applescript.flush()
-
- print('Running applescript...')
- command = ('osascript', temp_applescript.name)
- subprocess.run(command)
-
- print('Waiting for applescript...')
-
- # NOTE: This is copied from bundle.sh. The exact reason for sleep is
- # still remained a mystery.
- time.sleep(5)
-
-
-def codesign(subject: Path):
- """
- Codesign file or directory
-
- NOTE: For DMG it will also notarize.
- """
-
- command = (CODESIGN_SCRIPT, subject)
- subprocess.run(command)
-
-
-def codesign_app_bundles_in_dmg(mount_directory: str) -> None:
- """
- Code sign all binaries and bundles in the mounted directory
- """
-
- print(f'Codesigning all app bundles in {mount_directory}')
- codesign(mount_directory)
-
-
-def codesign_and_notarize_dmg(dmg_filepath: Path) -> None:
- """
- Run codesign and notarization pipeline on the DMG
- """
-
- print(f'Codesigning and notarizing DMG {dmg_filepath}')
- codesign(dmg_filepath)
-
-
-def compress_dmg(writable_dmg_filepath: Path,
- final_dmg_filepath: Path) -> None:
- """
- Compress temporary read-write DMG
- """
- command = ('hdiutil', 'convert',
- writable_dmg_filepath,
- '-format', 'UDZO',
- '-o', final_dmg_filepath)
-
- if final_dmg_filepath.exists():
- print(f'Removing old compressed DMG {final_dmg_filepath}')
- final_dmg_filepath.unlink()
-
- print('Compressing disk image...')
- subprocess.run(command)
-
-
-def create_final_dmg(app_bundles: List[Path],
- dmg_filepath: Path,
- background_image_filepath: Path,
- volume_name: str,
- applescript: Path,
- codesign: bool) -> None:
- """
- Create DMG with all app bundles
-
- Will take care configuring background, signing all binaries and app bundles
- and notarizing the DMG.
- """
-
- print('Running all routines to create final DMG')
-
- writable_dmg_filepath = get_writable_dmg_filepath(dmg_filepath)
- mount_directory = get_mount_directory_for_volume_name(volume_name)
-
- # Make sure volume is not mounted.
- # If it is mounted it will prevent removing old DMG files and could make
- # it so app bundles are copied to the wrong place.
- eject_volume(volume_name)
-
- create_dmg_image(app_bundles, writable_dmg_filepath, volume_name)
-
- mount_readwrite_dmg(writable_dmg_filepath)
-
- # Run codesign first, prior to copying amything else.
- #
- # This allows to recurs into the content of bundles without worrying about
- # possible interfereice of Application symlink.
- if codesign:
- codesign_app_bundles_in_dmg(mount_directory)
-
- copy_background_if_needed(background_image_filepath, mount_directory)
- create_applications_link(mount_directory)
- run_applescript(applescript, volume_name, app_bundles,
- background_image_filepath)
-
- print('Ejecting read-write DMG image...')
- eject_volume(volume_name)
-
- compress_dmg(writable_dmg_filepath, dmg_filepath)
- writable_dmg_filepath.unlink()
-
- if codesign:
- codesign_and_notarize_dmg(dmg_filepath)
-
-
-def ensure_dmg_extension(filepath: Path) -> Path:
- """
- Make sure given file have .dmg extension
- """
-
- if filepath.suffix != '.dmg':
- return filepath.with_suffix(f'{filepath.suffix}.dmg')
- return filepath
-
-
-def get_dmg_filepath(requested_name: Path, app_bundles: List[Path]) -> Path:
- """
- Get full file path for the final DMG image
-
- Will use the provided one when possible, otherwise will deduct it from
- app bundles.
-
- If the name is deducted, the DMG is stored in the current directory.
- """
-
- if requested_name:
- return ensure_dmg_extension(requested_name.absolute())
-
- # TODO(sergey): This is not necessarily the main one.
- main_bundle = app_bundles[0]
- # Strip .app from the name
- return Path(main_bundle.name[:-4] + '.dmg').absolute()
-
-
-def get_background_image(requested_background_image: Path) -> Path:
- """
- Get effective filepath for the background image
- """
-
- if requested_background_image:
- return requested_background_image.absolute()
-
- return DARWIN_DIRECTORY / 'background.tif'
-
-
-def get_applescript(requested_applescript: Path) -> Path:
- """
- Get effective filepath for the applescript
- """
-
- if requested_applescript:
- return requested_applescript.absolute()
-
- return DARWIN_DIRECTORY / 'blender.applescript'
-
-
-def get_volume_name_from_dmg_filepath(dmg_filepath: Path) -> str:
- """
- Deduct volume name from the DMG path
-
- Will use first part of the DMG file name prior to dash.
- """
-
- tokens = dmg_filepath.stem.split('-')
- words = tokens[0].split()
-
- return ' '.join(word.capitalize() for word in words)
-
-
-def get_volume_name(requested_volume_name: str,
- dmg_filepath: Path) -> str:
- """
- Get effective name for DMG volume
- """
-
- if requested_volume_name:
- return requested_volume_name
-
- return get_volume_name_from_dmg_filepath(dmg_filepath)
-
-
-def main():
- parser = create_argument_parser()
- args = parser.parse_args()
-
- # Get normalized input parameters.
- source_dir = args.source_dir.absolute()
- background_image_filepath = get_background_image(args.background_image)
- applescript = get_applescript(args.applescript)
- codesign = args.codesign
-
- app_bundles = collect_and_log_app_bundles(source_dir)
- if not app_bundles:
- return
-
- dmg_filepath = get_dmg_filepath(args.dmg, app_bundles)
- volume_name = get_volume_name(args.volume_name, dmg_filepath)
-
- print(f'Will produce DMG "{dmg_filepath.name}" (without quotes)')
-
- create_final_dmg(app_bundles,
- dmg_filepath,
- background_image_filepath,
- volume_name,
- applescript,
- codesign)
-
-
-if __name__ == "__main__":
- main()