diff --git a/.travis.yml b/.travis.yml index 07259cef8..6e099eaab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ env: LIBGIT2=~/libgit2/_install/ LD_LIBRARY_PATH=~/libgit2/_install/lib before_install: - sudo apt-get install cmake - - pip install cffi + - pip install 'cffi>=1.0.0' - "./.travis.sh" script: diff --git a/pygit2/__init__.py b/pygit2/__init__.py index 709adca78..3e437d7e0 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -43,7 +43,7 @@ from .settings import Settings from .submodule import Submodule from .utils import to_bytes, to_str -from ._utils import __version__ +from .libgit2_build import __version__ # Features diff --git a/pygit2/ffi.py b/pygit2/ffi.py index ae9e12cda..47542a16c 100644 --- a/pygit2/ffi.py +++ b/pygit2/ffi.py @@ -29,7 +29,8 @@ from __future__ import absolute_import # Import from pygit2 -from ._utils import get_ffi - - -ffi, C = get_ffi() +try: + from ._libgit2 import ffi, lib as C +except ImportError: + from .libgit2_build import ffi, C_HEADER_SRC, C_KEYWORDS + C = ffi.verify(C_HEADER_SRC, **C_KEYWORDS) diff --git a/pygit2/_utils.py b/pygit2/libgit2_build.py similarity index 63% rename from pygit2/_utils.py rename to pygit2/libgit2_build.py index f7b0714bf..0b2b50d67 100644 --- a/pygit2/_utils.py +++ b/pygit2/libgit2_build.py @@ -69,42 +69,37 @@ def get_libgit2_paths(): ) -# -# Loads the cffi extension -# -def get_ffi(): - import cffi +import cffi - ffi = cffi.FFI() +ffi = cffi.FFI() - # Load C definitions - if getattr(sys, 'frozen', False): - if hasattr(sys, '_MEIPASS'): - dir_path = sys._MEIPASS - else: - dir_path = dirname(abspath(sys.executable)) +# Load C definitions +if getattr(sys, 'frozen', False): + if hasattr(sys, '_MEIPASS'): + dir_path = sys._MEIPASS else: - dir_path = dirname(abspath(__file__)) - - decl_path = os.path.join(dir_path, 'decl.h') - with codecs.open(decl_path, 'r', 'utf-8') as header: - ffi.cdef(header.read()) - - # The modulename - # Simplified version of what cffi does: remove kwargs and vengine - preamble = "#include " - key = [sys.version[:3], cffi.__version__, preamble] + ffi._cdefsources - key = '\x00'.join(key) - if sys.version_info >= (3,): - key = key.encode('utf-8') - k1 = hex(crc32(key[0::2]) & 0xffffffff).lstrip('0x').rstrip('L') - k2 = hex(crc32(key[1::2]) & 0xffffffff).lstrip('0').rstrip('L') - modulename = 'pygit2_cffi_%s%s' % (k1, k2) - - # Load extension module - libgit2_bin, libgit2_include, libgit2_lib = get_libgit2_paths() - C = ffi.verify(preamble, modulename=modulename, libraries=["git2"], - include_dirs=[libgit2_include], library_dirs=[libgit2_lib]) - - # Ok - return ffi, C + dir_path = dirname(abspath(sys.executable)) +else: + dir_path = dirname(abspath(__file__)) + +decl_path = os.path.join(dir_path, 'decl.h') +with codecs.open(decl_path, 'r', 'utf-8') as header: + C_HEADER_SRC = header.read() + +libgit2_bin, libgit2_include, libgit2_lib = get_libgit2_paths() + +C_KEYWORDS = dict(libraries=['git2'], + library_dirs=[libgit2_lib], + include_dirs=[libgit2_include]) + +# The modulename +# Simplified version of what cffi does: remove kwargs and vengine +preamble = "#include " + +if hasattr(ffi, 'set_source'): + ffi.set_source("pygit2._libgit2", preamble, **C_KEYWORDS) + +ffi.cdef(C_HEADER_SRC) + +if __name__ == '__main__': + ffi.compile() diff --git a/setup.py b/setup.py index 92b528011..dd6179669 100644 --- a/setup.py +++ b/setup.py @@ -47,7 +47,7 @@ # Import stuff from pygit2/_utils.py without loading the whole pygit2 package sys.path.insert(0, 'pygit2') -from _utils import __version__, get_libgit2_paths, get_ffi +from libgit2_build import __version__, get_libgit2_paths del sys.path[0] # Python 2 support @@ -95,19 +95,42 @@ def run(self): unittest.main(None, defaultTest='test.test_suite', argv=test_argv) -class CFFIBuild(build): - """Hack to combat the chicken and egg problem that we need cffi - to add cffi as an extension. - """ - def finalize_options(self): - ffi, C = get_ffi() - self.distribution.ext_modules.append(ffi.verifier.get_extension()) - build.finalize_options(self) +class sdist_files_from_git(sdist): + def get_file_list(self): + popen = Popen(['git', 'ls-files'], stdout=PIPE, stderr=PIPE) + stdoutdata, stderrdata = popen.communicate() + if popen.returncode != 0: + print(stderrdata) + sys.exit() + + for line in stdoutdata.splitlines(): + # Skip hidden files at the root + if line[0] == '.': + continue + self.filelist.append(line) + + # Ok + self.filelist.sort() + self.filelist.remove_duplicates() + self.write_manifest() + + +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "Topic :: Software Development :: Version Control"] + +with codecs.open('README.rst', 'r', 'utf-8') as readme: + long_description = readme.read() +cmdclass = { + 'test': TestCommand, + 'sdist': sdist_files_from_git, +} -class BuildWithDLLs(CFFIBuild): - # On Windows, we install the git2.dll too. +# On Windows, we install the git2.dll too. +class BuildWithDLLs(build): def _get_dlls(self): # return a list of (FQ-in-name, relative-out-name) tuples. ret = [] @@ -133,45 +156,12 @@ def _get_dlls(self): def run(self): build.run(self) - # On Windows we package up the dlls with the plugin. for s, d in self._get_dlls(): self.copy_file(s, d) - -class sdist_files_from_git(sdist): - def get_file_list(self): - popen = Popen(['git', 'ls-files'], stdout=PIPE, stderr=PIPE) - stdoutdata, stderrdata = popen.communicate() - if popen.returncode != 0: - print(stderrdata) - sys.exit() - - for line in stdoutdata.splitlines(): - # Skip hidden files at the root - if line[0] == '.': - continue - self.filelist.append(line) - - # Ok - self.filelist.sort() - self.filelist.remove_duplicates() - self.write_manifest() - -classifiers = [ - "Development Status :: 3 - Alpha", - "Intended Audience :: Developers", - "Topic :: Software Development :: Version Control"] - - -with codecs.open('README.rst', 'r', 'utf-8') as readme: - long_description = readme.read() - - -cmdclass = { - 'build': BuildWithDLLs if os.name == 'nt' else CFFIBuild, - 'test': TestCommand, - 'sdist': sdist_files_from_git, - } +# On Windows we package up the dlls with the plugin. +if os.name == 'nt': + cmdclass['build'] = BuildWithDLLs setup(name='pygit2', description='Python bindings for libgit2.', @@ -185,6 +175,7 @@ def get_file_list(self): long_description=long_description, packages=['pygit2'], package_data={'pygit2': ['decl.h']}, + cffi_modules=['pygit2/libgit2_build.py:ffi'], setup_requires=['cffi'], install_requires=['cffi'], zip_safe=False,