from setuptools import setup, Extension, find_packages
import subprocess
import errno
import re
import os
import shutil
import sys
import zipfile
from urllib.request import urlretrieve

import numpy
from Cython.Build import cythonize
   
isWindows = os.name == 'nt'
isMac = sys.platform == 'darwin'
is64Bit = sys.maxsize > 2**32

# adapted from cffi's setup.py
# the following may be overridden if pkg-config exists
libraries = ['lensfun']
include_dirs = []
library_dirs = []
extra_compile_args = []
extra_link_args = []

def _ask_pkg_config(resultlist, option, result_prefix='', sysroot=False):
    pkg_config = os.environ.get('PKG_CONFIG','pkg-config')
    try:
        p = subprocess.Popen([pkg_config, option, 'lensfun'],
                             stdout=subprocess.PIPE)
    except OSError as e:
        if e.errno != errno.ENOENT:
            raise
    else:
        t = p.stdout.read().decode().strip()
        if p.wait() == 0:
            res = t.split()
            # '-I/usr/...' -> '/usr/...'
            for x in res:
                assert x.startswith(result_prefix)
            res = [x[len(result_prefix):] for x in res]

            sysroot = sysroot and os.environ.get('PKG_CONFIG_SYSROOT_DIR', '')
            if sysroot:
                # old versions of pkg-config don't support this env var,
                # so here we emulate its effect if needed
                res = [path if path.startswith(sysroot)
                            else sysroot + path
                         for path in res]
            resultlist[:] = res

def use_pkg_config():
    _ask_pkg_config(include_dirs,       '--cflags-only-I', '-I', sysroot=True)
    _ask_pkg_config(extra_compile_args, '--cflags-only-other')
    _ask_pkg_config(library_dirs,       '--libs-only-L', '-L', sysroot=True)
    _ask_pkg_config(extra_link_args,    '--libs-only-other')
    _ask_pkg_config(libraries,          '--libs-only-l', '-l')

if isWindows or isMac:
    cmake_build = os.path.abspath('external/lensfun/build')
    install_dir = os.path.join(cmake_build, 'install')
    
    include_dirs += [os.path.join(install_dir, 'include', 'lensfun')]
    library_dirs += [os.path.join(install_dir, 'lib')]
else:
    use_pkg_config()
    
# this must be after use_pkg_config()!
include_dirs += [numpy.get_include()]

# for version_helper.h
include_dirs += [os.path.abspath('lensfunpy')]

def clone_submodules():
    if not os.path.exists('external/lensfun/README.md'):
        print('lensfun git submodule not cloned yet, will invoke "git submodule update --init" now')
        if os.system('git submodule update --init') != 0:
            raise Exception('git failed')

def windows_lensfun_compile():
    clone_submodules()

    cwd = os.getcwd()
    
    # Download cmake to build lensfun
    cmake_version = '3.13.4'
    cmake_url = 'https://github.com/Kitware/CMake/releases/download/v{v}/cmake-{v}-win32-x86.zip'.format(v=cmake_version)
    cmake = os.path.abspath('external/cmake-{}-win32-x86/bin/cmake.exe'.format(cmake_version))

    # Download vcpkg to build dependencies of lensfun
    vcpkg_commit = '2021.05.12'
    vcpkg_url = 'https://github.com/Microsoft/vcpkg/archive/{}.zip'.format(vcpkg_commit)
    vcpkg_dir = os.path.abspath('external/vcpkg-{}'.format(vcpkg_commit))
    vcpkg_bootstrap = os.path.join(vcpkg_dir, 'bootstrap-vcpkg.bat')
    vcpkg = os.path.join(vcpkg_dir, 'vcpkg.exe')
    
    files = [(cmake_url, 'external', cmake),
             (vcpkg_url, 'external', vcpkg_bootstrap)]

    for url, extractdir, extractcheck in files:
        if not os.path.exists(extractcheck):
            path = 'external/' + os.path.basename(url)
            if not os.path.exists(path):
                print('Downloading', url)
                try:
                    urlretrieve(url, path)
                except:
                    # repeat once in case of network issues
                    urlretrieve(url, path)
        
            with zipfile.ZipFile(path) as z:
                print('Extracting', path, 'into', extractdir)
                z.extractall(extractdir)
                
            if not os.path.exists(path):
                raise RuntimeError(path + ' not found!')

    # Bootstrap vcpkg
    os.chdir(vcpkg_dir)
    if not os.path.exists(vcpkg):
        code = os.system(vcpkg_bootstrap)
        if code != 0:
            sys.exit(code) 

    # lensfun depends on glib2, so let's build it with vcpkg
    vcpkg_arch = 'x64' if is64Bit else 'x86'
    vcpkg_triplet = '{}-windows'.format(vcpkg_arch)
    code = os.system(vcpkg + ' install glib:' + vcpkg_triplet)
    if code != 0:
        sys.exit(code)
    vcpkg_install_dir = os.path.join(vcpkg_dir, 'installed', vcpkg_triplet)
    
    # bundle runtime dlls
    vcpkg_bin_dir = os.path.join(vcpkg_install_dir, 'bin')
    glib2_dll = os.path.join(vcpkg_bin_dir, 'glib-2.0-0.dll')

    # configure and compile lensfun
    if not os.path.exists(cmake_build):
        os.mkdir(cmake_build)
    os.chdir(cmake_build)
    # temporary hack to avoid https://stackoverflow.com/a/53547931
    # (python module not needed here anyway)
    patch_path = '../apps/CMakeLists.txt'
    with open(patch_path) as f:
        content = f.read()
    content = content.replace('IF(PYTHON)', 'IF(FALSE)')
    with open(patch_path, 'w') as f:
        f.write(content)
    cmds = [cmake + ' .. -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release ' +\
                    '-DBUILD_TESTS=off -DINSTALL_HELPER_SCRIPTS=off ' +\
                    '-DCMAKE_TOOLCHAIN_FILE={}/scripts/buildsystems/vcpkg.cmake '.format(vcpkg_dir) +\
                    '-DGLIB2_BASE_DIR={} -DGLIB2_DLL={} -DCMAKE_INSTALL_PREFIX=install'.format(vcpkg_install_dir, glib2_dll),
            cmake + ' --build .',
            cmake + ' --build . --target install',
            ]
    for cmd in cmds:
        print(cmd)
        code = os.system(cmd)
        if code != 0:
            sys.exit(code) 
    os.chdir(cwd)

    dll_runtime_libs = [('lensfun.dll', os.path.join(install_dir, 'bin')),
                        ('glib-2.0-0.dll', vcpkg_bin_dir),
                        # dependencies of glib
                        ('pcre.dll', vcpkg_bin_dir),
                        ('iconv-2.dll', vcpkg_bin_dir),
                        ('charset-1.dll', vcpkg_bin_dir),
                        ('intl-8.dll', vcpkg_bin_dir),
                        ]
    
    for filename, folder in dll_runtime_libs:
        src = os.path.join(folder, filename)
        dest = 'lensfunpy/' + filename
        print('copying', src, '->', dest)
        shutil.copyfile(src, dest)


def mac_lensfun_compile():
    clone_submodules()
        
    # configure and compile lensfun
    cwd = os.getcwd()
    if not os.path.exists(cmake_build):
        os.mkdir(cmake_build)
    os.chdir(cmake_build)
    install_name_dir = os.path.join(install_dir, 'lib')
    cmds = ['cmake .. -DCMAKE_BUILD_TYPE=Release ' +\
                    '-DBUILD_TESTS=off -DINSTALL_HELPER_SCRIPTS=off ' +\
                    '-DCMAKE_INSTALL_PREFIX=install ' +\
                    '-DCMAKE_INSTALL_NAME_DIR=' + install_name_dir,
            'cmake --build .',
            'cmake --build . --target install',
            ]
    for cmd in cmds:
        print(cmd)
        code = os.system(cmd)
        if code != 0:
            sys.exit(code)
    os.chdir(cwd)

def bundle_db_files():
    import glob
    db_files = 'lensfunpy/db_files'
    if not os.path.exists(db_files):
        os.makedirs(db_files)
    for path in glob.glob('external/lensfun/data/db/*.xml'):
        dest = os.path.join(db_files, os.path.basename(path))
        print('copying', path, '->', dest)
        shutil.copyfile(path, dest)

package_data = {'lensfunpy': []}

# evil hack, check cmd line for relevant commands
# custom cmdclasses didn't work out in this case
cmdline = ''.join(sys.argv[1:])
needsCompile = any(s in cmdline for s in ['install', 'bdist', 'build_ext', 'wheel', 'nosetests'])
if isWindows and needsCompile:
    windows_lensfun_compile()
    package_data['lensfunpy'].append('*.dll')

elif isMac and needsCompile:
    mac_lensfun_compile()

if any(s in cmdline for s in ['clean', 'sdist']):
    # When running sdist after a previous run of bdist or build_ext
    # then even with the 'clean' command the .egg-info folder stays.
    # This folder contains SOURCES.txt which in turn is used by sdist
    # to include package data files, but we don't want .dll's and .xml
    # files in our source distribution. Therefore, to prevent accidents,
    # we help a little...
    egg_info = 'lensfunpy.egg-info'
    print('removing', egg_info)
    shutil.rmtree(egg_info, ignore_errors=True)

if 'sdist' not in cmdline:
    # This assumes that the lensfun version from external/lensfun was used.
    # If that's not the case, the bundled files may fail to load, for example,
    # if lensfunpy was linked against an older lensfun version already on
    # the system (Linux mostly) and the database format changed in an incompatible way.
    # In that case, loading of bundled files can still be disabled
    # with Database(load_bundled=False).
    package_data['lensfunpy'].append('db_files/*.xml')
    bundle_db_files()

# Support for optional Cython line tracing
# run the following to generate a test coverage report:
# $ export LINETRACE=1
# $ python setup.py build_ext --inplace
# $ nosetests --with-coverage --cover-html --cover-package=lensfunpy
compdirectives = {}
macros = []
if (os.environ.get('LINETRACE', False)):
    compdirectives['linetrace'] = True
    macros.append(('CYTHON_TRACE', '1'))

extensions = cythonize([Extension("lensfunpy._lensfun",
              include_dirs=include_dirs,
              sources=[os.path.join('lensfunpy', '_lensfun.pyx')],
              libraries=libraries,
              library_dirs=library_dirs,
              extra_compile_args=extra_compile_args,
              extra_link_args=extra_link_args,
              define_macros=macros
             )],
             compiler_directives=compdirectives)

# make __version__ available (https://stackoverflow.com/a/16084844)
exec(open('lensfunpy/_version.py').read())

setup(
      name = 'lensfunpy',
      version = __version__,
      description = 'Lens distortion correction for Python, a wrapper for lensfun',
      long_description = open('README.rst').read(),
      author = 'Maik Riechert',
      author_email = 'maik.riechert@arcor.de',
      url = 'https://github.com/letmaik/lensfunpy',
      classifiers=[
        'Development Status :: 4 - Beta',
        'Intended Audience :: Developers',
        'Natural Language :: English',
        'License :: OSI Approved :: MIT License',
        'Programming Language :: Cython',
        'Programming Language :: Python',
        'Programming Language :: Python :: 3',
        'Programming Language :: Python :: 3.4',
        'Programming Language :: Python :: 3.5',
        'Programming Language :: Python :: 3.6',
        'Programming Language :: Python :: 3.7',
        'Operating System :: MacOS',
        'Operating System :: Microsoft :: Windows',
        'Operating System :: POSIX',
        'Operating System :: Unix',
        'Topic :: Multimedia :: Graphics',
        'Topic :: Software Development :: Libraries',
      ],
      packages = find_packages(),
      ext_modules = extensions,
      package_data = package_data,
      install_requires=['numpy']
)

[((9442, 9476), 'os.environ.get', 'os.environ.get', (['"""LINETRACE"""', '(False)'], {}), "('LINETRACE', False)\n", (9456, 9476), False, 'import os\n'), ((613, 655), 'os.environ.get', 'os.environ.get', (['"""PKG_CONFIG"""', '"""pkg-config"""'], {}), "('PKG_CONFIG', 'pkg-config')\n", (627, 655), False, 'import os\n'), ((1962, 2003), 'os.path.abspath', 'os.path.abspath', (['"""external/lensfun/build"""'], {}), "('external/lensfun/build')\n", (1977, 2003), False, 'import os\n'), ((2022, 2058), 'os.path.join', 'os.path.join', (['cmake_build', '"""install"""'], {}), "(cmake_build, 'install')\n", (2034, 2058), False, 'import os\n'), ((2277, 2296), 'numpy.get_include', 'numpy.get_include', ([], {}), '()\n', (2294, 2296), False, 'import numpy\n'), ((2339, 2367), 'os.path.abspath', 'os.path.abspath', (['"""lensfunpy"""'], {}), "('lensfunpy')\n", (2354, 2367), False, 'import os\n'), ((2718, 2729), 'os.getcwd', 'os.getcwd', ([], {}), '()\n', (2727, 2729), False, 'import os\n'), ((3291, 3337), 'os.path.join', 'os.path.join', (['vcpkg_dir', '"""bootstrap-vcpkg.bat"""'], {}), "(vcpkg_dir, 'bootstrap-vcpkg.bat')\n", (3303, 3337), False, 'import os\n'), ((3350, 3386), 'os.path.join', 'os.path.join', (['vcpkg_dir', '"""vcpkg.exe"""'], {}), "(vcpkg_dir, 'vcpkg.exe')\n", (3362, 3386), False, 'import os\n'), ((4214, 4233), 'os.chdir', 'os.chdir', (['vcpkg_dir'], {}), '(vcpkg_dir)\n', (4222, 4233), False, 'import os\n'), ((4530, 4581), 'os.system', 'os.system', (["(vcpkg + ' install glib:' + vcpkg_triplet)"], {}), "(vcpkg + ' install glib:' + vcpkg_triplet)\n", (4539, 4581), False, 'import os\n'), ((4647, 4698), 'os.path.join', 'os.path.join', (['vcpkg_dir', '"""installed"""', 'vcpkg_triplet'], {}), "(vcpkg_dir, 'installed', vcpkg_triplet)\n", (4659, 4698), False, 'import os\n'), ((4750, 4788), 'os.path.join', 'os.path.join', (['vcpkg_install_dir', '"""bin"""'], {}), "(vcpkg_install_dir, 'bin')\n", (4762, 4788), False, 'import os\n'), ((4805, 4850), 'os.path.join', 'os.path.join', (['vcpkg_bin_dir', '"""glib-2.0-0.dll"""'], {}), "(vcpkg_bin_dir, 'glib-2.0-0.dll')\n", (4817, 4850), False, 'import os\n'), ((4962, 4983), 'os.chdir', 'os.chdir', (['cmake_build'], {}), '(cmake_build)\n', (4970, 4983), False, 'import os\n'), ((5919, 5932), 'os.chdir', 'os.chdir', (['cwd'], {}), '(cwd)\n', (5927, 5932), False, 'import os\n'), ((6682, 6693), 'os.getcwd', 'os.getcwd', ([], {}), '()\n', (6691, 6693), False, 'import os\n'), ((6768, 6789), 'os.chdir', 'os.chdir', (['cmake_build'], {}), '(cmake_build)\n', (6776, 6789), False, 'import os\n'), ((6813, 6845), 'os.path.join', 'os.path.join', (['install_dir', '"""lib"""'], {}), "(install_dir, 'lib')\n", (6825, 6845), False, 'import os\n'), ((7314, 7327), 'os.chdir', 'os.chdir', (['cwd'], {}), '(cwd)\n', (7322, 7327), False, 'import os\n'), ((7487, 7530), 'glob.glob', 'glob.glob', (['"""external/lensfun/data/db/*.xml"""'], {}), "('external/lensfun/data/db/*.xml')\n", (7496, 7530), False, 'import glob\n'), ((8600, 8643), 'shutil.rmtree', 'shutil.rmtree', (['egg_info'], {'ignore_errors': '(True)'}), '(egg_info, ignore_errors=True)\n', (8613, 8643), False, 'import shutil\n'), ((676, 749), 'subprocess.Popen', 'subprocess.Popen', (["[pkg_config, option, 'lensfun']"], {'stdout': 'subprocess.PIPE'}), "([pkg_config, option, 'lensfun'], stdout=subprocess.PIPE)\n", (692, 749), False, 'import subprocess\n'), ((2085, 2132), 'os.path.join', 'os.path.join', (['install_dir', '"""include"""', '"""lensfun"""'], {}), "(install_dir, 'include', 'lensfun')\n", (2097, 2132), False, 'import os\n'), ((2155, 2187), 'os.path.join', 'os.path.join', (['install_dir', '"""lib"""'], {}), "(install_dir, 'lib')\n", (2167, 2187), False, 'import os\n'), ((2405, 2449), 'os.path.exists', 'os.path.exists', (['"""external/lensfun/README.md"""'], {}), "('external/lensfun/README.md')\n", (2419, 2449), False, 'import os\n'), ((4245, 4266), 'os.path.exists', 'os.path.exists', (['vcpkg'], {}), '(vcpkg)\n', (4259, 4266), False, 'import os\n'), ((4283, 4309), 'os.system', 'os.system', (['vcpkg_bootstrap'], {}), '(vcpkg_bootstrap)\n', (4292, 4309), False, 'import os\n'), ((4608, 4622), 'sys.exit', 'sys.exit', (['code'], {}), '(code)\n', (4616, 4622), False, 'import sys\n'), ((4899, 4926), 'os.path.exists', 'os.path.exists', (['cmake_build'], {}), '(cmake_build)\n', (4913, 4926), False, 'import os\n'), ((4936, 4957), 'os.mkdir', 'os.mkdir', (['cmake_build'], {}), '(cmake_build)\n', (4944, 4957), False, 'import os\n'), ((5850, 5864), 'os.system', 'os.system', (['cmd'], {}), '(cmd)\n', (5859, 5864), False, 'import os\n'), ((6428, 6458), 'os.path.join', 'os.path.join', (['folder', 'filename'], {}), '(folder, filename)\n', (6440, 6458), False, 'import os\n'), ((6548, 6574), 'shutil.copyfile', 'shutil.copyfile', (['src', 'dest'], {}), '(src, dest)\n', (6563, 6574), False, 'import shutil\n'), ((6705, 6732), 'os.path.exists', 'os.path.exists', (['cmake_build'], {}), '(cmake_build)\n', (6719, 6732), False, 'import os\n'), ((6742, 6763), 'os.mkdir', 'os.mkdir', (['cmake_build'], {}), '(cmake_build)\n', (6750, 6763), False, 'import os\n'), ((7246, 7260), 'os.system', 'os.system', (['cmd'], {}), '(cmd)\n', (7255, 7260), False, 'import os\n'), ((7415, 7439), 'os.path.exists', 'os.path.exists', (['db_files'], {}), '(db_files)\n', (7429, 7439), False, 'import os\n'), ((7449, 7470), 'os.makedirs', 'os.makedirs', (['db_files'], {}), '(db_files)\n', (7460, 7470), False, 'import os\n'), ((7645, 7672), 'shutil.copyfile', 'shutil.copyfile', (['path', 'dest'], {}), '(path, dest)\n', (7660, 7672), False, 'import shutil\n'), ((11249, 11264), 'setuptools.find_packages', 'find_packages', ([], {}), '()\n', (11262, 11264), False, 'from setuptools import setup, Extension, find_packages\n'), ((2563, 2603), 'os.system', 'os.system', (['"""git submodule update --init"""'], {}), "('git submodule update --init')\n", (2572, 2603), False, 'import os\n'), ((3556, 3584), 'os.path.exists', 'os.path.exists', (['extractcheck'], {}), '(extractcheck)\n', (3570, 3584), False, 'import os\n'), ((4344, 4358), 'sys.exit', 'sys.exit', (['code'], {}), '(code)\n', (4352, 4358), False, 'import sys\n'), ((5899, 5913), 'sys.exit', 'sys.exit', (['code'], {}), '(code)\n', (5907, 5913), False, 'import sys\n'), ((5974, 6006), 'os.path.join', 'os.path.join', (['install_dir', '"""bin"""'], {}), "(install_dir, 'bin')\n", (5986, 6006), False, 'import os\n'), ((7295, 7309), 'sys.exit', 'sys.exit', (['code'], {}), '(code)\n', (7303, 7309), False, 'import sys\n'), ((7570, 7592), 'os.path.basename', 'os.path.basename', (['path'], {}), '(path)\n', (7586, 7592), False, 'import os\n'), ((1176, 1220), 'os.environ.get', 'os.environ.get', (['"""PKG_CONFIG_SYSROOT_DIR"""', '""""""'], {}), "('PKG_CONFIG_SYSROOT_DIR', '')\n", (1190, 1220), False, 'import os\n'), ((3619, 3640), 'os.path.basename', 'os.path.basename', (['url'], {}), '(url)\n', (3635, 3640), False, 'import os\n'), ((3660, 3680), 'os.path.exists', 'os.path.exists', (['path'], {}), '(path)\n', (3674, 3680), False, 'import os\n'), ((3941, 3962), 'zipfile.ZipFile', 'zipfile.ZipFile', (['path'], {}), '(path)\n', (3956, 3962), False, 'import zipfile\n'), ((4108, 4128), 'os.path.exists', 'os.path.exists', (['path'], {}), '(path)\n', (4122, 4128), False, 'import os\n'), ((3765, 3787), 'urllib.request.urlretrieve', 'urlretrieve', (['url', 'path'], {}), '(url, path)\n', (3776, 3787), False, 'from urllib.request import urlretrieve\n'), ((9680, 9721), 'os.path.join', 'os.path.join', (['"""lensfunpy"""', '"""_lensfun.pyx"""'], {}), "('lensfunpy', '_lensfun.pyx')\n", (9692, 9721), False, 'import os\n'), ((3892, 3914), 'urllib.request.urlretrieve', 'urlretrieve', (['url', 'path'], {}), '(url, path)\n', (3903, 3914), False, 'from urllib.request import urlretrieve\n')]