It may still be in the include directories provided by the operating system, such as it is on the vast majority of Linux distributions.
409 lines
14 KiB
Python
Executable File
409 lines
14 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import sys
|
|
|
|
|
|
# quick version check
|
|
if sys.version_info[0] == 2 or (sys.version_info[0] == 3 and sys.version_info[1] < 4):
|
|
print("Sorry, the Overviewer requires at least Python 3.4 to run.")
|
|
sys.exit(1)
|
|
|
|
|
|
from setuptools import setup
|
|
from setuptools.extension import Extension
|
|
from distutils.command.clean import clean
|
|
from distutils.command.build import build
|
|
from setuptools.command.build_ext import build_ext
|
|
from distutils.command.sdist import sdist
|
|
from distutils.cmd import Command
|
|
from distutils.dir_util import remove_tree
|
|
from distutils.sysconfig import get_python_inc
|
|
from distutils import log
|
|
|
|
import os
|
|
import os.path
|
|
import glob
|
|
import platform
|
|
import time
|
|
import traceback
|
|
|
|
from overviewer_core import util
|
|
|
|
try:
|
|
import py2exe
|
|
except ImportError:
|
|
py2exe = None
|
|
|
|
try:
|
|
import py2app
|
|
except ImportError:
|
|
py2app = None
|
|
|
|
|
|
BUILD_ERROR = """
|
|
Failed to build Overviewer!
|
|
Please review the errors printed above and the build instructions at
|
|
<http://docs.overviewer.org/en/latest/building/>. If you are still having
|
|
build problems, file an incident on the github tracker or find us in IRC.
|
|
"""
|
|
|
|
# make sure our current working directory is the same directory
|
|
# setup.py is in
|
|
curdir = os.path.split(sys.argv[0])[0]
|
|
if curdir:
|
|
os.chdir(curdir)
|
|
|
|
# now, setup the keyword arguments for setup
|
|
# (because we don't know until runtime if py2exe/py2app is available)
|
|
setup_kwargs = dict({
|
|
'name': 'Minecraft-Overviewer',
|
|
'url': 'http://overviewer.org/',
|
|
'version': util.findGitVersion(),
|
|
'author': 'Andrew Brown',
|
|
'author_email': 'brownan@gmail.com',
|
|
'description': 'Generates large resolution images of a Minecraft map.',
|
|
'long_description': open('README.rst', 'r').read(),
|
|
'license': open('COPYING.txt', 'r').read(),
|
|
|
|
'ext_modules': [],
|
|
'cmdclass': {},
|
|
'options': {},
|
|
'install_requires': ['numpy', 'pillow']
|
|
})
|
|
|
|
#
|
|
# metadata
|
|
#
|
|
|
|
|
|
# top-level files that should be included as documentation
|
|
doc_files = ['COPYING.txt',
|
|
'README.rst',
|
|
'CONTRIBUTORS.rst',
|
|
'sample_config.py']
|
|
|
|
|
|
def data_files(src, dest=None):
|
|
"""helper to create a 'data_files'-type sequence recursively for a given
|
|
dir
|
|
|
|
:param src: directory to include files from
|
|
:param dest:
|
|
:return: list
|
|
|
|
"""
|
|
if dest is None:
|
|
dest = src
|
|
|
|
ret = []
|
|
for dirpath, dirnames, filenames in os.walk(src):
|
|
current_dest = os.path.relpath(dirpath, src)
|
|
if current_dest == '.':
|
|
current_dest = dest
|
|
else:
|
|
current_dest = os.path.join(dest, current_dest)
|
|
|
|
current_sources = map(lambda p: os.path.join(dirpath, p), filenames)
|
|
|
|
ret.append((current_dest, current_sources))
|
|
return ret
|
|
|
|
|
|
def package_data(src, package_dir='overviewer_core'):
|
|
"""Helper to create a 'package_data'-type sequence recursively for a given
|
|
dir
|
|
|
|
:param src: source directory
|
|
:param package_dir: package directory
|
|
:return: list
|
|
|
|
"""
|
|
full_src = os.path.join(package_dir, src)
|
|
ret = []
|
|
for dirpath, dirnames, filenames in os.walk(full_src, topdown=False):
|
|
current_path = os.path.relpath(dirpath, package_dir)
|
|
for filename in filenames:
|
|
ret.append(os.path.join(current_path, filename))
|
|
return ret
|
|
|
|
#
|
|
# py2exe options
|
|
#
|
|
|
|
if py2exe is not None:
|
|
setup_kwargs['comments'] = 'http://overviewer.org'
|
|
# py2exe likes a very particular type of version number:
|
|
setup_kwargs['version'] = setup_kwargs['version'].replace('-', '.')
|
|
|
|
setup_kwargs['console'] = ['overviewer.py', 'contribManager.py']
|
|
setup_kwargs['data_files'] = [('', doc_files)]
|
|
setup_kwargs['data_files'] += data_files('overviewer_core/data/textures',
|
|
'textures')
|
|
setup_kwargs['data_files'] += data_files('overviewer_core/data/web_assets',
|
|
'web_assets')
|
|
setup_kwargs['data_files'] += data_files('overviewer_core/data/js_src',
|
|
'js_src')
|
|
setup_kwargs['data_files'] += data_files('contrib', 'contrib')
|
|
setup_kwargs['zipfile'] = None
|
|
if platform.system() == 'Windows' and '64bit' in platform.architecture():
|
|
b = 3
|
|
else:
|
|
b = 1
|
|
setup_kwargs['options']['py2exe'] = \
|
|
{'bundle_files': b,
|
|
'excludes': 'Tkinter',
|
|
'includes': ['fileinput',
|
|
'overviewer_core.items',
|
|
'overviewer_core.aux_files.genPOI']}
|
|
|
|
#
|
|
# py2app options
|
|
#
|
|
|
|
if py2app is not None:
|
|
setup_kwargs['app'] = ['overviewer.py']
|
|
setup_kwargs['options']['py2app'] = {'argv_emulation': False}
|
|
setup_kwargs['setup_requires'] = ['py2app']
|
|
|
|
#
|
|
# script, package, and data
|
|
#
|
|
|
|
setup_kwargs['packages'] = ['overviewer_core', 'overviewer_core/aux_files']
|
|
setup_kwargs['scripts'] = ['overviewer.py']
|
|
setup_kwargs['package_data'] = \
|
|
{'overviewer_core': (package_data('data/textures') +
|
|
package_data('data/web_assets') +
|
|
package_data('data/js_src'))}
|
|
|
|
if py2exe is None:
|
|
setup_kwargs['data_files'] = [('share/doc/minecraft-overviewer',
|
|
doc_files)]
|
|
|
|
|
|
def get_numpy_include():
|
|
"""Obtain the numpy include directory. This logic works across numpy
|
|
versions
|
|
|
|
:rtype: list
|
|
|
|
"""
|
|
print(sys.path)
|
|
try:
|
|
import numpy
|
|
except ImportError:
|
|
if sys.argv[1] != 'develop':
|
|
print('numpy not found, please run "python setup.py develop"')
|
|
sys.exit(1)
|
|
return []
|
|
try:
|
|
return numpy.get_include()
|
|
except AttributeError:
|
|
return numpy.get_numpy_include()
|
|
|
|
# Third-party modules - we depend on numpy for everything
|
|
numpy_include = get_numpy_include()
|
|
|
|
# Try and find the Imaging path automatically
|
|
pil_include = []
|
|
imaging_path = os.environ.get('PIL_INCLUDE_DIR',
|
|
os.path.normpath('./Imaging/libImaging'))
|
|
if not os.path.exists(imaging_path):
|
|
imaging_path = os.path.join(get_python_inc(plat_specific=1), 'Imaging')
|
|
if os.path.exists(imaging_path):
|
|
pil_include.append(imaging_path)
|
|
|
|
SRC_PATH = 'overviewer_core/src/'
|
|
|
|
# used to figure out what files to compile
|
|
# auto-created from files in primitives/, but we need the raw names so
|
|
# we can use them later.
|
|
primitives = []
|
|
for name in glob.glob(SRC_PATH + 'primitives/*.c'):
|
|
name = os.path.split(name)[-1]
|
|
name = os.path.splitext(name)[0]
|
|
primitives.append(name)
|
|
|
|
c_overviewer_files = ['main.c', 'composite.c', 'iterate.c', 'endian.c', 'rendermodes.c', 'block_class.c']
|
|
c_overviewer_files += ['primitives/%s.c' % (mode) for mode in primitives]
|
|
c_overviewer_files += ['Draw.c']
|
|
c_overviewer_includes = ['overviewer.h', 'rendermodes.h']
|
|
|
|
c_overviewer_files = ['overviewer_core/src/' + s for s in c_overviewer_files]
|
|
c_overviewer_includes = ['overviewer_core/src/' + s for s in c_overviewer_includes]
|
|
|
|
setup_kwargs['ext_modules'].append(Extension('overviewer_core.c_overviewer', c_overviewer_files, include_dirs=['.', numpy_include] + pil_include, depends=c_overviewer_includes, extra_link_args=[]))
|
|
|
|
|
|
# tell build_ext to build the extension in-place
|
|
# (NOT in build/)
|
|
setup_kwargs['options']['build_ext'] = {'inplace' : 1}
|
|
|
|
include_dirs = ['.', numpy_include] + pil_include
|
|
|
|
setup_kwargs['ext_modules'].append(Extension('overviewer_core.c_overviewer',
|
|
c_overviewer_files,
|
|
include_dirs=include_dirs,
|
|
depends=c_overviewer_includes,
|
|
extra_link_args=[]))
|
|
|
|
# tell build_ext to build the extension in-place (NOT in build/)
|
|
setup_kwargs['options']['build_ext'] = {'inplace': 1}
|
|
|
|
# custom clean command to remove in-place extension
|
|
# and the version file, primitives header
|
|
class CustomClean(clean):
|
|
def run(self):
|
|
# do the normal cleanup
|
|
clean.run(self)
|
|
|
|
# try to remove '_composite.{so,pyd,...}' extension,
|
|
# regardless of the current system's extension name convention
|
|
build_ext = self.get_finalized_command('build_ext')
|
|
ext_fname = build_ext.get_ext_filename('overviewer_core.c_overviewer')
|
|
versionpath = os.path.join("overviewer_core", "overviewer_version.py")
|
|
primspath = os.path.join("overviewer_core", "src", "primitives.h")
|
|
|
|
for fname in [ext_fname, primspath]:
|
|
if os.path.exists(fname):
|
|
try:
|
|
log.info("removing '%s'", fname)
|
|
if not self.dry_run:
|
|
os.remove(fname)
|
|
|
|
except OSError:
|
|
log.warning("'%s' could not be cleaned -- permission denied",
|
|
fname)
|
|
else:
|
|
log.debug("'%s' does not exist -- can't clean it",
|
|
fname)
|
|
|
|
# now try to purge all *.pyc files
|
|
for root, dirs, files in os.walk(os.path.join(os.path.dirname(__file__), ".")):
|
|
for f in files:
|
|
if f.endswith(".pyc"):
|
|
if self.dry_run:
|
|
log.warning("Would remove %s", os.path.join(root,f))
|
|
else:
|
|
os.remove(os.path.join(root, f))
|
|
|
|
def generate_version_py():
|
|
try:
|
|
with open('overviewer_core/overviewer_version.py', 'w') as f:
|
|
f.write('\n'.join(['VERSION=%r' % util.findGitVersion(),
|
|
'HASH=%r' % util.findGitHash(),
|
|
'BUILD_DATE=%r' % time.asctime(),
|
|
'BUILD_PLATFORM=%r' % platform.processor(),
|
|
'BUILD_OS=%r' % platform.platform()]) + '\n')
|
|
except Exception:
|
|
print('WARNING: failed to build overviewer_version file')
|
|
|
|
|
|
def generate_primitives_h():
|
|
prims = [p.lower().replace('-', '_') for p in primitives]
|
|
out = ['/* this file is auto-generated by setup.py */']
|
|
for p in prims:
|
|
out.append('extern RenderPrimitiveInterface primitive_{0};'.format(p))
|
|
out.append('static RenderPrimitiveInterface *render_primitives[] = {')
|
|
for p in prims:
|
|
out.append(' &primitive_{0},'.format(p))
|
|
out.append(' NULL')
|
|
out.append('};\n')
|
|
with open(SRC_PATH + 'primitives.h', 'w') as f:
|
|
f.write('\n'.join(out))
|
|
|
|
|
|
class CustomBuild(build):
|
|
|
|
def run(self):
|
|
generate_version_py()
|
|
generate_primitives_h()
|
|
try:
|
|
build.run(self)
|
|
print('\nBuild Complete')
|
|
except Exception:
|
|
traceback.print_exc(limit=1)
|
|
print("\nFailed to build Overviewer!")
|
|
print("Please review the errors printed above and the build instructions")
|
|
print("at <http://docs.overviewer.org/en/latest/building/>. If you are")
|
|
print("still having build problems, file an incident on the github tracker")
|
|
print("or find us in IRC.")
|
|
sys.exit(1)
|
|
|
|
class CustomBuildExt(build_ext):
|
|
|
|
def build_extensions(self):
|
|
c = self.compiler.compiler_type
|
|
if c == "msvc":
|
|
# customize the build options for this compilier
|
|
for e in self.extensions:
|
|
e.extra_link_args.append("/MANIFEST")
|
|
e.extra_link_args.append("/DWINVER=0x060")
|
|
e.extra_link_args.append("/D_WIN32_WINNT=0x060")
|
|
elif c == "unix":
|
|
# customize the build options for this compilier
|
|
for e in self.extensions:
|
|
e.extra_compile_args.append("-Wno-unused-variable") # quell some annoying warnings
|
|
e.extra_compile_args.append("-Wno-unused-function") # quell some annoying warnings
|
|
e.extra_compile_args.append("-Wdeclaration-after-statement")
|
|
e.extra_compile_args.append("-Werror=declaration-after-statement")
|
|
e.extra_compile_args.append("-O3")
|
|
|
|
|
|
# build in place, and in the build/ tree
|
|
self.inplace = False
|
|
build_ext.build_extensions(self)
|
|
|
|
self.inplace = True
|
|
build_ext.build_extensions(self)
|
|
|
|
|
|
class CustomClean(clean):
|
|
"""Custom clean command to remove in-place extension and the version file,
|
|
primitives header
|
|
|
|
"""
|
|
def run(self):
|
|
clean.run(self)
|
|
# try to remove '_composite.{so,pyd,...}' extension,
|
|
# regardless of the current system's extension name convention
|
|
build_ext = self.get_finalized_command('build_ext')
|
|
ext_fname = build_ext.get_ext_filename('overviewer_core.c_overviewer')
|
|
primspath = os.path.join('overviewer_core', 'src', 'primitives.h')
|
|
|
|
for fname in [ext_fname, primspath]:
|
|
if os.path.exists(fname):
|
|
try:
|
|
log.info("removing '%s'", fname)
|
|
if not self.dry_run:
|
|
os.remove(fname)
|
|
except OSError as error:
|
|
log.warn("'%s' could not be cleaned: %s", fname, error)
|
|
else:
|
|
log.debug("'%s' does not exist -- can't clean it", fname)
|
|
|
|
# now try to purge all *.pyc files
|
|
for root, dirs, files in \
|
|
os.walk(os.path.join(os.path.dirname(__file__), '.')):
|
|
for f in files:
|
|
if f.endswith('.pyc'):
|
|
if self.dry_run:
|
|
log.warn('Would remove %s', os.path.join(root, f))
|
|
else:
|
|
os.remove(os.path.join(root, f))
|
|
|
|
|
|
class CustomSDist(sdist):
|
|
def run(self):
|
|
generate_version_py()
|
|
generate_primitives_h()
|
|
sdist.run(self)
|
|
|
|
|
|
setup_kwargs['cmdclass']['clean'] = CustomClean
|
|
setup_kwargs['cmdclass']['sdist'] = CustomSDist
|
|
setup_kwargs['cmdclass']['build'] = CustomBuild
|
|
setup_kwargs['cmdclass']['build_ext'] = CustomBuildExt
|
|
|
|
setup(**setup_kwargs)
|