123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750 |
- #!/usr/bin/env python
- """
- Utility for building Buildroot packages for existing PyPI packages
- Any package built by scanpypi should be manually checked for
- errors.
- """
- from __future__ import print_function
- from __future__ import absolute_import
- import argparse
- import json
- import sys
- import os
- import shutil
- import tarfile
- import zipfile
- import errno
- import hashlib
- import re
- import textwrap
- import tempfile
- import imp
- from functools import wraps
- import six.moves.urllib.request
- import six.moves.urllib.error
- import six.moves.urllib.parse
- from six.moves import map
- from six.moves import zip
- from six.moves import input
- if six.PY2:
- import StringIO
- else:
- import io
- BUF_SIZE = 65536
- try:
- import spdx_lookup as liclookup
- except ImportError:
- # spdx_lookup is not installed
- print('spdx_lookup module is not installed. This can lead to an '
- 'inaccurate licence detection. Please install it via\n'
- 'pip install spdx_lookup')
- liclookup = None
- def setup_decorator(func, method):
- """
- Decorator for distutils.core.setup and setuptools.setup.
- Puts the arguments with which setup is called as a dict
- Add key 'method' which should be either 'setuptools' or 'distutils'.
- Keyword arguments:
- func -- either setuptools.setup or distutils.core.setup
- method -- either 'setuptools' or 'distutils'
- """
- @wraps(func)
- def closure(*args, **kwargs):
- # Any python packages calls its setup function to be installed.
- # Argument 'name' of this setup function is the package's name
- BuildrootPackage.setup_args[kwargs['name']] = kwargs
- BuildrootPackage.setup_args[kwargs['name']]['method'] = method
- return closure
- # monkey patch
- import setuptools # noqa E402
- setuptools.setup = setup_decorator(setuptools.setup, 'setuptools')
- import distutils # noqa E402
- distutils.core.setup = setup_decorator(setuptools.setup, 'distutils')
- def find_file_upper_case(filenames, path='./'):
- """
- List generator:
- Recursively find files that matches one of the specified filenames.
- Returns a relative path starting with path argument.
- Keyword arguments:
- filenames -- List of filenames to be found
- path -- Path to the directory to search
- """
- for root, dirs, files in os.walk(path):
- for file in files:
- if file.upper() in filenames:
- yield (os.path.join(root, file))
- def pkg_buildroot_name(pkg_name):
- """
- Returns the Buildroot package name for the PyPI package pkg_name.
- Remove all non alphanumeric characters except -
- Also lowers the name and adds 'python-' suffix
- Keyword arguments:
- pkg_name -- String to rename
- """
- name = re.sub('[^\w-]', '', pkg_name.lower())
- name = name.replace('_', '-')
- prefix = 'python-'
- pattern = re.compile('^(?!' + prefix + ')(.+?)$')
- name = pattern.sub(r'python-\1', name)
- return name
- class DownloadFailed(Exception):
- pass
- class BuildrootPackage():
- """This class's methods are not meant to be used individually please
- use them in the correct order:
- __init__
- download_package
- extract_package
- load_module
- get_requirements
- create_package_mk
- create_hash_file
- create_config_in
- """
- setup_args = {}
- def __init__(self, real_name, pkg_folder):
- self.real_name = real_name
- self.buildroot_name = pkg_buildroot_name(self.real_name)
- self.pkg_dir = os.path.join(pkg_folder, self.buildroot_name)
- self.mk_name = self.buildroot_name.upper().replace('-', '_')
- self.as_string = None
- self.md5_sum = None
- self.metadata = None
- self.metadata_name = None
- self.metadata_url = None
- self.pkg_req = None
- self.setup_metadata = None
- self.tmp_extract = None
- self.used_url = None
- self.filename = None
- self.url = None
- self.version = None
- self.license_files = []
- def fetch_package_info(self):
- """
- Fetch a package's metadata from the python package index
- """
- self.metadata_url = 'https://pypi.org/pypi/{pkg}/json'.format(
- pkg=self.real_name)
- try:
- pkg_json = six.moves.urllib.request.urlopen(self.metadata_url).read().decode()
- except six.moves.urllib.error.HTTPError as error:
- print('ERROR:', error.getcode(), error.msg, file=sys.stderr)
- print('ERROR: Could not find package {pkg}.\n'
- 'Check syntax inside the python package index:\n'
- 'https://pypi.python.org/pypi/ '
- .format(pkg=self.real_name))
- raise
- except six.moves.urllib.error.URLError:
- print('ERROR: Could not find package {pkg}.\n'
- 'Check syntax inside the python package index:\n'
- 'https://pypi.python.org/pypi/ '
- .format(pkg=self.real_name))
- raise
- self.metadata = json.loads(pkg_json)
- self.version = self.metadata['info']['version']
- self.metadata_name = self.metadata['info']['name']
- def download_package(self):
- """
- Download a package using metadata from pypi
- """
- download = None
- try:
- self.metadata['urls'][0]['filename']
- except IndexError:
- print(
- 'Non-conventional package, ',
- 'please check carefully after creation')
- self.metadata['urls'] = [{
- 'packagetype': 'sdist',
- 'url': self.metadata['info']['download_url'],
- 'digests': None}]
- # In this case, we can't get the name of the downloaded file
- # from the pypi api, so we need to find it, this should work
- urlpath = six.moves.urllib.parse.urlparse(
- self.metadata['info']['download_url']).path
- # urlparse().path give something like
- # /path/to/file-version.tar.gz
- # We use basename to remove /path/to
- self.metadata['urls'][0]['filename'] = os.path.basename(urlpath)
- for download_url in self.metadata['urls']:
- if 'bdist' in download_url['packagetype']:
- continue
- try:
- print('Downloading package {pkg} from {url}...'.format(
- pkg=self.real_name, url=download_url['url']))
- download = six.moves.urllib.request.urlopen(download_url['url'])
- except six.moves.urllib.error.HTTPError as http_error:
- download = http_error
- else:
- self.used_url = download_url
- self.as_string = download.read()
- if not download_url['digests']['md5']:
- break
- self.md5_sum = hashlib.md5(self.as_string).hexdigest()
- if self.md5_sum == download_url['digests']['md5']:
- break
- if download is None:
- raise DownloadFailed('Failed to download package {pkg}: '
- 'No source archive available'
- .format(pkg=self.real_name))
- elif download.__class__ == six.moves.urllib.error.HTTPError:
- raise download
- self.filename = self.used_url['filename']
- self.url = self.used_url['url']
- def check_archive(self, members):
- """
- Check archive content before extracting
- Keyword arguments:
- members -- list of archive members
- """
- # Protect against https://github.com/snyk/zip-slip-vulnerability
- # Older python versions do not validate that the extracted files are
- # inside the target directory. Detect and error out on evil paths
- evil = [e for e in members if os.path.relpath(e).startswith(('/', '..'))]
- if evil:
- print('ERROR: Refusing to extract {} with suspicious members {}'.format(
- self.filename, evil))
- sys.exit(1)
- def extract_package(self, tmp_path):
- """
- Extract the package contents into a directrory
- Keyword arguments:
- tmp_path -- directory where you want the package to be extracted
- """
- if six.PY2:
- as_file = StringIO.StringIO(self.as_string)
- else:
- as_file = io.BytesIO(self.as_string)
- if self.filename[-3:] == 'zip':
- with zipfile.ZipFile(as_file) as as_zipfile:
- tmp_pkg = os.path.join(tmp_path, self.buildroot_name)
- try:
- os.makedirs(tmp_pkg)
- except OSError as exception:
- if exception.errno != errno.EEXIST:
- print("ERROR: ", exception.strerror, file=sys.stderr)
- return
- print('WARNING:', exception.strerror, file=sys.stderr)
- print('Removing {pkg}...'.format(pkg=tmp_pkg))
- shutil.rmtree(tmp_pkg)
- os.makedirs(tmp_pkg)
- self.check_archive(as_zipfile.namelist())
- as_zipfile.extractall(tmp_pkg)
- pkg_filename = self.filename.split(".zip")[0]
- else:
- with tarfile.open(fileobj=as_file) as as_tarfile:
- tmp_pkg = os.path.join(tmp_path, self.buildroot_name)
- try:
- os.makedirs(tmp_pkg)
- except OSError as exception:
- if exception.errno != errno.EEXIST:
- print("ERROR: ", exception.strerror, file=sys.stderr)
- return
- print('WARNING:', exception.strerror, file=sys.stderr)
- print('Removing {pkg}...'.format(pkg=tmp_pkg))
- shutil.rmtree(tmp_pkg)
- os.makedirs(tmp_pkg)
- self.check_archive(as_tarfile.getnames())
- as_tarfile.extractall(tmp_pkg)
- pkg_filename = self.filename.split(".tar")[0]
- tmp_extract = '{folder}/{name}'
- self.tmp_extract = tmp_extract.format(
- folder=tmp_pkg,
- name=pkg_filename)
- def load_setup(self):
- """
- Loads the corresponding setup and store its metadata
- """
- current_dir = os.getcwd()
- os.chdir(self.tmp_extract)
- sys.path.append(self.tmp_extract)
- s_file, s_path, s_desc = imp.find_module('setup', [self.tmp_extract])
- setup = imp.load_module('setup', s_file, s_path, s_desc)
- if self.metadata_name in self.setup_args:
- pass
- elif self.metadata_name.replace('_', '-') in self.setup_args:
- self.metadata_name = self.metadata_name.replace('_', '-')
- elif self.metadata_name.replace('-', '_') in self.setup_args:
- self.metadata_name = self.metadata_name.replace('-', '_')
- try:
- self.setup_metadata = self.setup_args[self.metadata_name]
- except KeyError:
- # This means setup was not called which most likely mean that it is
- # called through the if __name__ == '__main__' directive.
- # In this case, we can only pray that it is called through a
- # function called main() in setup.py.
- setup.main() # Will raise AttributeError if not found
- self.setup_metadata = self.setup_args[self.metadata_name]
- # Here we must remove the module the hard way.
- # We must do this because of a very specific case: if a package calls
- # setup from the __main__ but does not come with a 'main()' function,
- # for some reason setup.main() will successfully call the main
- # function of a previous package...
- sys.modules.pop('setup', None)
- del setup
- os.chdir(current_dir)
- sys.path.remove(self.tmp_extract)
- def get_requirements(self, pkg_folder):
- """
- Retrieve dependencies from the metadata found in the setup.py script of
- a pypi package.
- Keyword Arguments:
- pkg_folder -- location of the already created packages
- """
- if 'install_requires' not in self.setup_metadata:
- self.pkg_req = None
- return set()
- self.pkg_req = self.setup_metadata['install_requires']
- self.pkg_req = [re.sub('([-.\w]+).*', r'\1', req)
- for req in self.pkg_req]
- # get rid of commented lines and also strip the package strings
- self.pkg_req = [item.strip() for item in self.pkg_req
- if len(item) > 0 and item[0] != '#']
- req_not_found = self.pkg_req
- self.pkg_req = list(map(pkg_buildroot_name, self.pkg_req))
- pkg_tuples = list(zip(req_not_found, self.pkg_req))
- # pkg_tuples is a list of tuples that looks like
- # ('werkzeug','python-werkzeug') because I need both when checking if
- # dependencies already exist or are already in the download list
- req_not_found = set(
- pkg[0] for pkg in pkg_tuples
- if not os.path.isdir(pkg[1])
- )
- return req_not_found
- def __create_mk_header(self):
- """
- Create the header of the <package_name>.mk file
- """
- header = ['#' * 80 + '\n']
- header.append('#\n')
- header.append('# {name}\n'.format(name=self.buildroot_name))
- header.append('#\n')
- header.append('#' * 80 + '\n')
- header.append('\n')
- return header
- def __create_mk_download_info(self):
- """
- Create the lines refering to the download information of the
- <package_name>.mk file
- """
- lines = []
- version_line = '{name}_VERSION = {version}\n'.format(
- name=self.mk_name,
- version=self.version)
- lines.append(version_line)
- if self.buildroot_name != self.real_name:
- targz = self.filename.replace(
- self.version,
- '$({name}_VERSION)'.format(name=self.mk_name))
- targz_line = '{name}_SOURCE = {filename}\n'.format(
- name=self.mk_name,
- filename=targz)
- lines.append(targz_line)
- if self.filename not in self.url:
- # Sometimes the filename is in the url, sometimes it's not
- site_url = self.url
- else:
- site_url = self.url[:self.url.find(self.filename)]
- site_line = '{name}_SITE = {url}'.format(name=self.mk_name,
- url=site_url)
- site_line = site_line.rstrip('/') + '\n'
- lines.append(site_line)
- return lines
- def __create_mk_setup(self):
- """
- Create the line refering to the setup method of the package of the
- <package_name>.mk file
- There are two things you can use to make an installer
- for a python package: distutils or setuptools
- distutils comes with python but does not support dependencies.
- distutils is mostly still there for backward support.
- setuptools is what smart people use,
- but it is not shipped with python :(
- """
- lines = []
- setup_type_line = '{name}_SETUP_TYPE = {method}\n'.format(
- name=self.mk_name,
- method=self.setup_metadata['method'])
- lines.append(setup_type_line)
- return lines
- def __get_license_names(self, license_files):
- """
- Try to determine the related license name.
- There are two possibilities. Either the script tries to
- get license name from package's metadata or, if spdx_lookup
- package is available, the script compares license files with
- SPDX database.
- """
- license_line = ''
- if liclookup is None:
- license_dict = {
- 'Apache Software License': 'Apache-2.0',
- 'BSD License': 'FIXME: please specify the exact BSD version',
- 'European Union Public Licence 1.0': 'EUPL-1.0',
- 'European Union Public Licence 1.1': 'EUPL-1.1',
- "GNU General Public License": "GPL",
- "GNU General Public License v2": "GPL-2.0",
- "GNU General Public License v2 or later": "GPL-2.0+",
- "GNU General Public License v3": "GPL-3.0",
- "GNU General Public License v3 or later": "GPL-3.0+",
- "GNU Lesser General Public License v2": "LGPL-2.1",
- "GNU Lesser General Public License v2 or later": "LGPL-2.1+",
- "GNU Lesser General Public License v3": "LGPL-3.0",
- "GNU Lesser General Public License v3 or later": "LGPL-3.0+",
- "GNU Library or Lesser General Public License": "LGPL-2.0",
- "ISC License": "ISC",
- "MIT License": "MIT",
- "Mozilla Public License 1.0": "MPL-1.0",
- "Mozilla Public License 1.1": "MPL-1.1",
- "Mozilla Public License 2.0": "MPL-2.0",
- "Zope Public License": "ZPL"
- }
- regexp = re.compile('^License :* *.* *:+ (.*)( \(.*\))?$')
- classifiers_licenses = [regexp.sub(r"\1", lic)
- for lic in self.metadata['info']['classifiers']
- if regexp.match(lic)]
- licenses = [license_dict[x] if x in license_dict else x for x in classifiers_licenses]
- if not len(licenses):
- print('WARNING: License has been set to "{license}". It is most'
- ' likely wrong, please change it if need be'.format(
- license=', '.join(licenses)))
- licenses = [self.metadata['info']['license']]
- license_line = '{name}_LICENSE = {license}\n'.format(
- name=self.mk_name,
- license=', '.join(licenses))
- else:
- license_names = []
- for license_file in license_files:
- with open(license_file) as lic_file:
- match = liclookup.match(lic_file.read())
- if match is not None and match.confidence >= 90.0:
- license_names.append(match.license.id)
- else:
- license_names.append("FIXME: license id couldn't be detected")
- if len(license_names) > 0:
- license_line = ('{name}_LICENSE ='
- ' {names}\n'.format(
- name=self.mk_name,
- names=', '.join(license_names)))
- return license_line
- def __create_mk_license(self):
- """
- Create the lines referring to the package's license informations of the
- <package_name>.mk file
- The license's files are found by searching the package (case insensitive)
- for files named license, license.txt etc. If more than one license file
- is found, the user is asked to select which ones he wants to use.
- """
- lines = []
- filenames = ['LICENCE', 'LICENSE', 'LICENSE.RST', 'LICENSE.TXT',
- 'COPYING', 'COPYING.TXT']
- self.license_files = list(find_file_upper_case(filenames, self.tmp_extract))
- lines.append(self.__get_license_names(self.license_files))
- license_files = [license.replace(self.tmp_extract, '')[1:]
- for license in self.license_files]
- if len(license_files) > 0:
- if len(license_files) > 1:
- print('More than one file found for license:',
- ', '.join(license_files))
- license_files = [filename
- for index, filename in enumerate(license_files)]
- license_file_line = ('{name}_LICENSE_FILES ='
- ' {files}\n'.format(
- name=self.mk_name,
- files=' '.join(license_files)))
- lines.append(license_file_line)
- else:
- print('WARNING: No license file found,'
- ' please specify it manually afterwards')
- license_file_line = '# No license file found\n'
- return lines
- def __create_mk_requirements(self):
- """
- Create the lines referring to the dependencies of the of the
- <package_name>.mk file
- Keyword Arguments:
- pkg_name -- name of the package
- pkg_req -- dependencies of the package
- """
- lines = []
- dependencies_line = ('{name}_DEPENDENCIES ='
- ' {reqs}\n'.format(
- name=self.mk_name,
- reqs=' '.join(self.pkg_req)))
- lines.append(dependencies_line)
- return lines
- def create_package_mk(self):
- """
- Create the lines corresponding to the <package_name>.mk file
- """
- pkg_mk = '{name}.mk'.format(name=self.buildroot_name)
- path_to_mk = os.path.join(self.pkg_dir, pkg_mk)
- print('Creating {file}...'.format(file=path_to_mk))
- lines = self.__create_mk_header()
- lines += self.__create_mk_download_info()
- lines += self.__create_mk_setup()
- lines += self.__create_mk_license()
- lines.append('\n')
- lines.append('$(eval $(python-package))')
- lines.append('\n')
- with open(path_to_mk, 'w') as mk_file:
- mk_file.writelines(lines)
- def create_hash_file(self):
- """
- Create the lines corresponding to the <package_name>.hash files
- """
- pkg_hash = '{name}.hash'.format(name=self.buildroot_name)
- path_to_hash = os.path.join(self.pkg_dir, pkg_hash)
- print('Creating {filename}...'.format(filename=path_to_hash))
- lines = []
- if self.used_url['digests']['md5'] and self.used_url['digests']['sha256']:
- hash_header = '# md5, sha256 from {url}\n'.format(
- url=self.metadata_url)
- lines.append(hash_header)
- hash_line = '{method}\t{digest} {filename}\n'.format(
- method='md5',
- digest=self.used_url['digests']['md5'],
- filename=self.filename)
- lines.append(hash_line)
- hash_line = '{method}\t{digest} {filename}\n'.format(
- method='sha256',
- digest=self.used_url['digests']['sha256'],
- filename=self.filename)
- lines.append(hash_line)
- if self.license_files:
- lines.append('# Locally computed sha256 checksums\n')
- for license_file in self.license_files:
- sha256 = hashlib.sha256()
- with open(license_file, 'rb') as lic_f:
- while True:
- data = lic_f.read(BUF_SIZE)
- if not data:
- break
- sha256.update(data)
- hash_line = '{method}\t{digest} {filename}\n'.format(
- method='sha256',
- digest=sha256.hexdigest(),
- filename=license_file.replace(self.tmp_extract, '')[1:])
- lines.append(hash_line)
- with open(path_to_hash, 'w') as hash_file:
- hash_file.writelines(lines)
- def create_config_in(self):
- """
- Creates the Config.in file of a package
- """
- path_to_config = os.path.join(self.pkg_dir, 'Config.in')
- print('Creating {file}...'.format(file=path_to_config))
- lines = []
- config_line = 'config BR2_PACKAGE_{name}\n'.format(
- name=self.mk_name)
- lines.append(config_line)
- bool_line = '\tbool "{name}"\n'.format(name=self.buildroot_name)
- lines.append(bool_line)
- if self.pkg_req:
- for dep in self.pkg_req:
- dep_line = '\tselect BR2_PACKAGE_{req} # runtime\n'.format(
- req=dep.upper().replace('-', '_'))
- lines.append(dep_line)
- lines.append('\thelp\n')
- help_lines = textwrap.wrap(self.metadata['info']['summary'], 62,
- initial_indent='\t ',
- subsequent_indent='\t ')
- # make sure a help text is terminated with a full stop
- if help_lines[-1][-1] != '.':
- help_lines[-1] += '.'
- # \t + two spaces is 3 char long
- help_lines.append('')
- help_lines.append('\t ' + self.metadata['info']['home_page'])
- help_lines = [x + '\n' for x in help_lines]
- lines += help_lines
- with open(path_to_config, 'w') as config_file:
- config_file.writelines(lines)
- def main():
- # Building the parser
- parser = argparse.ArgumentParser(
- description="Creates buildroot packages from the metadata of "
- "an existing PyPI packages and include it "
- "in menuconfig")
- parser.add_argument("packages",
- help="list of packages to be created",
- nargs='+')
- parser.add_argument("-o", "--output",
- help="""
- Output directory for packages.
- Default is ./package
- """,
- default='./package')
- args = parser.parse_args()
- packages = list(set(args.packages))
- # tmp_path is where we'll extract the files later
- tmp_prefix = 'scanpypi-'
- pkg_folder = args.output
- tmp_path = tempfile.mkdtemp(prefix=tmp_prefix)
- try:
- for real_pkg_name in packages:
- package = BuildrootPackage(real_pkg_name, pkg_folder)
- print('buildroot package name for {}:'.format(package.real_name),
- package.buildroot_name)
- # First we download the package
- # Most of the info we need can only be found inside the package
- print('Package:', package.buildroot_name)
- print('Fetching package', package.real_name)
- try:
- package.fetch_package_info()
- except (six.moves.urllib.error.URLError, six.moves.urllib.error.HTTPError):
- continue
- if package.metadata_name.lower() == 'setuptools':
- # setuptools imports itself, that does not work very well
- # with the monkey path at the begining
- print('Error: setuptools cannot be built using scanPyPI')
- continue
- try:
- package.download_package()
- except six.moves.urllib.error.HTTPError as error:
- print('Error: {code} {reason}'.format(code=error.code,
- reason=error.reason))
- print('Error downloading package :', package.buildroot_name)
- print()
- continue
- # extract the tarball
- try:
- package.extract_package(tmp_path)
- except (tarfile.ReadError, zipfile.BadZipfile):
- print('Error extracting package {}'.format(package.real_name))
- print()
- continue
- # Loading the package install info from the package
- try:
- package.load_setup()
- except ImportError as err:
- if 'buildutils' in err.message:
- print('This package needs buildutils')
- else:
- raise
- continue
- except AttributeError as error:
- print('Error: Could not install package {pkg}: {error}'.format(
- pkg=package.real_name, error=error))
- continue
- # Package requirement are an argument of the setup function
- req_not_found = package.get_requirements(pkg_folder)
- req_not_found = req_not_found.difference(packages)
- packages += req_not_found
- if req_not_found:
- print('Added packages \'{pkgs}\' as dependencies of {pkg}'
- .format(pkgs=", ".join(req_not_found),
- pkg=package.buildroot_name))
- print('Checking if package {name} already exists...'.format(
- name=package.pkg_dir))
- try:
- os.makedirs(package.pkg_dir)
- except OSError as exception:
- if exception.errno != errno.EEXIST:
- print("ERROR: ", exception.message, file=sys.stderr)
- continue
- print('Error: Package {name} already exists'
- .format(name=package.pkg_dir))
- del_pkg = input(
- 'Do you want to delete existing package ? [y/N]')
- if del_pkg.lower() == 'y':
- shutil.rmtree(package.pkg_dir)
- os.makedirs(package.pkg_dir)
- else:
- continue
- package.create_package_mk()
- package.create_hash_file()
- package.create_config_in()
- print()
- # printing an empty line for visual confort
- finally:
- shutil.rmtree(tmp_path)
- if __name__ == "__main__":
- main()
|