Commit ff954f31 authored by Robert Bradshaw's avatar Robert Bradshaw

New build mechanism, dependency resolution, only pass .c files to distutils.

parent 03199009
from Cython import Utils from glob import glob
import re import re
from distutils.extension import Extension
from Cython import Utils
def cached_method(f): def cached_method(f):
cache_name = '__%s_cache' % f.__name__ cache_name = '__%s_cache' % f.__name__
def wrapper(self, *args): def wrapper(self, *args):
...@@ -114,23 +119,31 @@ class DependencyTree(object): ...@@ -114,23 +119,31 @@ class DependencyTree(object):
return parse_dependencies(source_filename) return parse_dependencies(source_filename)
@cached_method @cached_method
def immediate_dependencies(self, filename): def cimports_and_externs(self, filename):
cimports, includes, externs = self.parse_dependencies(filename) cimports, includes, externs = self.parse_dependencies(filename)
cimports = set(cimports) cimports = set(cimports)
externs = set(externs) externs = set(externs)
for include in includes: for include in includes:
a, b = self.immediate_dependencies(os.path.join(os.path.dirname(filename), include)) a, b = self.cimports_and_externs(os.path.join(os.path.dirname(filename), include))
cimports.update(a) cimports.update(a)
externs.update(b) externs.update(b)
return cimports, externs return tuple(cimports), tuple(externs)
def cimports(self, filename):
return self.cimports_and_externs(filename)[0]
@cached_method @cached_method
def package(self, filename): def package(self, filename):
dir = os.path.dirname(filename) dir = os.path.dirname(filename)
if os.path.exists(os.path.join(dir, '__init__.py')): if os.path.exists(os.path.join(dir, '__init__.py')):
return self.package(dir) + [os.path.basename(dir)] return self.package(dir) + (os.path.basename(dir),)
else: else:
return [] return ()
@cached_method
def fully_qualifeid_name(self, filename):
module = os.path.splitext(os.path.basename(filename))[0]
return '.'.join(self.package(filename) + (module,))
def find_pxd(self, module, filename=None): def find_pxd(self, module, filename=None):
if module[0] == '.': if module[0] == '.':
...@@ -145,22 +158,29 @@ class DependencyTree(object): ...@@ -145,22 +158,29 @@ class DependencyTree(object):
@cached_method @cached_method
def cimported_files(self, filename): def cimported_files(self, filename):
if filename[-4:] == '.pyx' and os.path.exists(filename[:-4] + '.pxd'): if filename[-4:] == '.pyx' and os.path.exists(filename[:-4] + '.pxd'):
self_pxd = [filename[:-4] + '.pxd'] self_pxd = (filename[:-4] + '.pxd',)
else: else:
self_pxd = [] self_pxd = ()
a = self.immediate_dependencies(filename)[0] a = self.cimports(filename)
b = filter(None, [self.find_pxd(m, filename) for m in self.immediate_dependencies(filename)[0]]) b = filter(None, [self.find_pxd(m, filename) for m in self.cimports(filename)])
if len(a) != len(b): if len(a) != len(b):
print (filename) print (filename)
print ("\n\t".join(a)) print ("\n\t".join(a))
print ("\n\t".join(b)) print ("\n\t".join(b))
return self_pxd + filter(None, [self.find_pxd(m, filename) for m in self.immediate_dependencies(filename)[0]]) return tuple(self_pxd + filter(None, [self.find_pxd(m, filename) for m in self.cimports(filename)]))
def immediate_dependencies(self, filename):
all = list(self.cimported_files(filename))
for extern in self.cimports_and_externs(filename):
all.append(os.path.normpath(os.path.join(os.path.dirname(filename), extern)))
return tuple(all)
@cached_method @cached_method
def timestamp(self, filename): def timestamp(self, filename):
return os.path.getmtime(filename) return os.path.getmtime(filename)
def extract_timestamp(self, filename): def extract_timestamp(self, filename):
# TODO: .h files from extern blocks
return self.timestamp(filename), filename return self.timestamp(filename), filename
def newest_dependency(self, filename): def newest_dependency(self, filename):
...@@ -198,3 +218,59 @@ class DependencyTree(object): ...@@ -198,3 +218,59 @@ class DependencyTree(object):
return deps, loop return deps, loop
finally: finally:
del stack[node] del stack[node]
_dep_tree = None
def create_dependency_tree(ctx):
global _dep_tree
if _dep_tree is None:
_dep_tree = DependencyTree(ctx)
return _dep_tree
def create_extension_list(filepatterns, ctx):
deps = create_dependency_tree(ctx)
if isinstance(filepatterns, str):
filepatterns = [filepatterns]
for pattern in filepatterns:
for file in glob(pattern):
pkg = deps.package(file)
name = fully_qualifeid_name(file)
return Extension(name=name, sources=[file])
def cythonize(module_list):
deps = create_dependency_tree(ctx)
to_compile = []
for m in module_list:
new_sources = []
for source in m.sources:
base, ext = os.path.splitext(source)
if ext in ('pyx', 'py'):
if m.language == 'c++':
c_file = base + '.cpp'
else:
c_file = base + '.c'
if os.path.exists(c_file):
c_timestamp = os.path.getmtime(outfile)
else:
c_timestamp = -1
if c_timestamp < deps.timestamp(source):
dep, dep_timestamp = deps.timestamp(source), source
priority = 0
else:
dep, dep_timestamp = deps.newest_dependency(source)
priority = 2 - (dep in deps.immediate_dependencies(source))
if c_timestamp < dep_timestamp:
print ("Compiling", source, "because it depends on ", dep)
if dep == source:
to_compile.append((priority, source, c_file))
new_sources.append(outfile)
else:
new_sources.append(source)
m.sources = new_sources
to_compile.sort()
# TODO: invoke directly
cython_py = os.path.join(os.path.dirname(__FILE__), '../../cython.py')
for priority, pyx_file, c_file in to_compile:
cmd = "python %s %s -o %s" % (python_py, pyx_file, c_file)
print cmd
os.system(cmd)
return module_list
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment