This repository has been archived on 2023-10-03. You can view files and clone it, but cannot push or open issues or pull requests.

219 lines
7.9 KiB
Python
Raw Permalink Normal View History

#!/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 glob
import sys
import shutil
import subprocess
import re
import pathlib
from distutils import log
from distutils.core import Command
from distutils.command.bdist import bdist
from distutils.command.install import install
from distutils.command.install_egg_info import install_egg_info
from setuptools import setup, find_packages
requirement_re = re.compile('[><=]+')
sys.dont_write_bytecode = True
2016-05-03 13:42:00 +02:00
def set_default_path(var, default):
"""convert CLI-arguments (string) to Paths"""
if var is None:
return default
return pathlib.Path(var)
# noinspection PyAttributeOutsideInit
class BuildWheels(Command):
"""Builds or downloads the dependencies as wheel files."""
description = "builds/downloads the dependencies as wheel files"
user_options = [
('wheels-path=', None, "wheel file installation path"),
('deps-path=', None, "path in which dependencies are built"),
('cachecontrol-path=', None, "subdir of deps-path containing CacheControl"),
]
def initialize_options(self):
self.wheels_path = None # path that will contain the installed wheels.
self.deps_path = None # path in which dependencies are built.
self.cachecontrol_path = None # subdir of deps_path containing CacheControl
def finalize_options(self):
self.my_path = pathlib.Path(__file__).resolve().parent
package_path = self.my_path / self.distribution.get_name()
2016-05-03 13:42:00 +02:00
self.wheels_path = set_default_path(self.wheels_path, package_path / 'wheels')
self.deps_path = set_default_path(self.deps_path, self.my_path / 'build/deps')
self.cachecontrol_path = set_default_path(self.cachecontrol_path,
self.deps_path / 'cachecontrol')
def run(self):
log.info('Storing wheels in %s', self.wheels_path)
# Parse the requirements.txt file
requirements = {}
with open(str(self.my_path / 'requirements.txt')) as reqfile:
for line in reqfile.readlines():
line = line.strip()
if not line or line.startswith('#'):
# comments are lines that start with # only
continue
line_req = requirement_re.split(line)
package = line_req[0]
version = line_req[-1]
requirements[package] = (line, version)
# log.info(' - %s = %s / %s', package, line, line_req[-1])
self.wheels_path.mkdir(parents=True, exist_ok=True)
# Download lockfile, as there is a suitable wheel on pypi.
if not list(self.wheels_path.glob('lockfile*.whl')):
log.info('Downloading lockfile wheel')
2016-05-03 13:42:00 +02:00
self.download_wheel(requirements['lockfile'])
2016-05-03 13:42:00 +02:00
# Download Pillar Python SDK from pypi.
if not list(self.wheels_path.glob('pillarsdk*.whl')):
log.info('Downloading Pillar Python SDK wheel')
self.download_wheel(requirements['pillarsdk'])
# Build CacheControl.
if not list(self.wheels_path.glob('CacheControl*.whl')):
log.info('Building CacheControl in %s', self.cachecontrol_path)
# self.git_clone(self.cachecontrol_path,
# 'https://github.com/ionrock/cachecontrol.git',
# 'v%s' % requirements['CacheControl'][1])
# FIXME: we need my clone until pull request #125 has been merged & released
self.git_clone(self.cachecontrol_path,
'https://github.com/sybrenstuvel/cachecontrol.git',
'sybren-filecache-delete-crash-fix')
self.build_copy_wheel(self.cachecontrol_path)
# Ensure that the wheels are added to the data files.
self.distribution.data_files.append(
('blender_cloud/wheels', (str(p) for p in self.wheels_path.glob('*.whl')))
)
2016-05-03 13:42:00 +02:00
def download_wheel(self, requirement):
"""Downloads a wheel from PyPI and saves it in self.wheels_path."""
subprocess.check_call([
'pip', 'download',
'--no-deps',
'--dest', str(self.wheels_path),
requirement[0]
])
def git_clone(self, workdir: pathlib.Path, git_url: str, checkout: str = None):
if workdir.exists():
# Directory exists, expect it to be set up correctly.
return
workdir.mkdir(parents=True)
subprocess.check_call(['git', 'clone', git_url, str(workdir)],
cwd=str(workdir.parent))
if checkout:
subprocess.check_call(['git', 'checkout', checkout],
cwd=str(workdir))
def build_copy_wheel(self, package_path: pathlib.Path):
# Make sure no wheels exist yet, so that we know which one to copy later.
to_remove = list((package_path / 'dist').glob('*.whl'))
for fname in to_remove:
fname.unlink()
subprocess.check_call([sys.executable, 'setup.py', 'bdist_wheel'],
cwd=str(package_path))
wheel = next((package_path / 'dist').glob('*.whl'))
log.info('copying %s to %s', wheel, self.wheels_path)
shutil.copy(str(wheel), str(self.wheels_path))
2016-05-03 13:42:00 +02:00
# noinspection PyAttributeOutsideInit
class BlenderAddonBdist(bdist):
"""Ensures that 'python setup.py bdist' creates a zip file."""
def initialize_options(self):
super().initialize_options()
self.formats = ['zip']
self.plat_name = 'addon' # use this instead of 'linux-x86_64' or similar.
def run(self):
self.run_command('wheels')
super().run()
2016-05-03 13:42:00 +02:00
# noinspection PyAttributeOutsideInit
class BlenderAddonInstall(install):
"""Ensures the module is placed at the root of the zip file."""
def initialize_options(self):
super().initialize_options()
self.prefix = ''
self.install_lib = ''
class AvoidEggInfo(install_egg_info):
"""Makes sure the egg-info directory is NOT created.
If we skip this, the user's addon directory will be polluted by egg-info
directories, which Blender doesn't use anyway.
"""
def run(self):
pass
setup(
cmdclass={'bdist': BlenderAddonBdist,
'install': BlenderAddonInstall,
'install_egg_info': AvoidEggInfo,
'wheels': BuildWheels},
name='blender_cloud',
description='The Blender Cloud addon allows browsing the Blender Cloud from Blender.',
version='1.4.99',
author='Sybren A. Stüvel',
author_email='sybren@stuvel.eu',
packages=find_packages('.'),
data_files=[('blender_cloud', ['README.md']),
('blender_cloud/icons', glob.glob('blender_cloud/icons/*'))],
scripts=[],
url='https://developer.blender.org/diffusion/BCA/',
license='GNU General Public License v2 or later (GPLv2+)',
platforms='',
classifiers=[
'Intended Audience :: End Users/Desktop',
'Operating System :: OS Independent',
'Environment :: Plugins',
'License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)',
'Programming Language :: Python',
'Programming Language :: Python :: 3.5',
],
zip_safe=False,
)