from os.path import join, dirname, isdir, exists, isfile, split, realpath, basename
import importlib
import zipfile
import glob
from shutil import rmtree
from six import PY2, with_metaclass
import hashlib
import sh
import shutil
import fnmatch
from os import listdir, unlink, environ, mkdir, curdir, walk
from sys import stdout
try:
from urlparse import urlparse
except ImportError:
from urllib.parse import urlparse
from pythonforandroid.logger import (logger, info, warning, error, debug, shprint, info_main)
from pythonforandroid.util import (urlretrieve, current_directory, ensure_dir)
# this import is necessary to keep imp.load_source from complaining :)
import pythonforandroid.recipes
if PY2:
import imp
import_recipe = imp.load_source
else:
import importlib.util
if hasattr(importlib.util, 'module_from_spec'):
def import_recipe(module, filename):
spec = importlib.util.spec_from_file_location(module, filename)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
else:
from importlib.machinery import SourceFileLoader
def import_recipe(module, filename):
return SourceFileLoader(module, filename).load_module()
class RecipeMeta(type):
def __new__(cls, name, bases, dct):
if name != 'Recipe':
if 'url' in dct:
dct['_url'] = dct.pop('url')
if 'version' in dct:
dct['_version'] = dct.pop('version')
return super(RecipeMeta, cls).__new__(cls, name, bases, dct)
class Recipe(with_metaclass(RecipeMeta)):
_url = None
'''The address from which the recipe may be downloaded. This is not
essential, it may be omitted if the source is available some other
way, such as via the :class:`IncludedFilesBehaviour` mixin.
If the url includes the version, you may (and probably should)
replace this with ``{version}``, which will automatically be
replaced by the :attr:`version` string during download.
.. note:: Methods marked (internal) are used internally and you
probably don't need to call them, but they are available
if you want.
'''
_version = None
'''A string giving the version of the software the recipe describes,
e.g. ``2.0.3`` or ``master``.'''
md5sum = None
'''The md5sum of the source from the :attr:`url`. Non-essential, but
you should try to include this, it is used to check that the download
finished correctly.
'''
depends = []
'''A list containing the names of any recipes that this recipe depends on.
'''
conflicts = []
'''A list containing the names of any recipes that are known to be
incompatible with this one.'''
opt_depends = []
'''A list of optional dependencies, that must be built before this
recipe if they are built at all, but whose presence is not essential.'''
patches = []
'''A list of patches to apply to the source. Values can be either a string
referring to the patch file relative to the recipe dir, or a tuple of the
string patch file and a callable, which will receive the kwargs `arch` and
`recipe`, which should return True if the patch should be applied.'''
python_depends = []
'''A list of pure-Python packages that this package requires. These
packages will NOT be available at build time, but will be added to the
list of pure-Python packages to install via pip. If you need these packages
at build time, you must create a recipe.'''
archs = ['armeabi'] # Not currently implemented properly
@property
def version(self):
key = 'VERSION_' + self.name
return environ.get(key, self._version)
@property
def url(self):
key = 'URL_' + self.name
return environ.get(key, self._url)
@property
def versioned_url(self):
'''A property returning the url of the recipe with ``{version}``
replaced by the :attr:`url`. If accessing the url, you should use this
property, *not* access the url directly.'''
if self.url is None:
return None
return self.url.format(version=self.version)
def download_file(self, url, target, cwd=None):
"""
(internal) Download an ``url`` to a ``target``.
"""
if not url:
return
info('Downloading {} from {}'.format(self.name, url))
if cwd:
target = join(cwd, target)
parsed_url = urlparse(url)
if parsed_url.scheme in ('http', 'https'):
def report_hook(index, blksize, size):
if size