import os, sys, glob
from distutils.command import build_py
from distutils.errors import DistutilsExecError
from distutils.util import convert_path
class BuildPy(build_py.build_py):
command_name = 'build_py'
if sys.version < '2.4':
def initialize_options(self):
build_py.build_py.initialize_options(self)
self.package_data = None
return
def finalize_options(self):
build_py.build_py.finalize_options(self)
self.package_data = self.distribution.package_data
self.data_files = self.get_data_files()
return
def run(self):
if self.py_modules:
self.build_modules()
if self.packages:
self.build_packages()
self.build_package_data()
self.byte_compile(self.get_outputs(include_bytecode=0))
return
def get_data_files(self):
"""Generate list of '(package, src_dir, build_dir, filenames)'
tuples."""
data = []
for package in self.packages:
# Locate package source directory
src_dir = self.get_package_dir(package)
# Compute package build directory
build_dir = os.path.join(self.build_lib, *package.split('.'))
# Length of path to strip from found files
plen = len(src_dir)+1
# Strip directory from globbed filenames
filenames = self.find_data_files(package, src_dir)
filenames = [ f[plen:] for f in filenames ]
data.append((package, src_dir, build_dir, filenames))
return data
def find_data_files(self, package, src_dir):
"""Return filenames for package's data files in 'src_dir'"""
globs = (self.package_data.get('', [])
+ self.package_data.get(package, []))
files = []
for pattern in globs:
# Each pattern has to be converted to a platform-specific path
filelist = glob.glob(os.path.join(src_dir, convert_path(pattern)))
# Files that match more than one pattern are only added once
files.extend([fn for fn in filelist if fn not in files])
return files
def build_package_data(self):
"""Copy data files into build directory"""
lastdir = None
for package, src_dir, build_dir, filenames in self.data_files:
for filename in filenames:
target = os.path.join(build_dir, filename)
self.mkpath(os.path.dirname(target))
self.copy_file(os.path.join(src_dir, filename), target,
preserve_mode=False)
def get_outputs(self, include_bytecode=1):
outputs = build_py.build_py.get_outputs(self, include_bytecode)
for package, src_dir, build_dir, filenames in self.data_files:
for filename in filenames:
outputs.append(os.path.join(build_dir, filename))
return outputs
if sys.version < '2.3':
# Add support for both py_modules and packages
def find_all_modules(self):
"""Compute the list of all modules that will be built, whether
they are specified one-module-at-a-time ('self.py_modules') or
by whole packages ('self.packages'). Return a list of tuples
(package, module, module_file), just like 'find_modules()' and
'find_package_modules()' do."""
modules = []
if self.py_modules:
modules.extend(self.find_modules())
if self.packages:
for package in self.packages:
package_dir = self.get_package_dir(package)
m = self.find_package_modules(package, package_dir)
modules.extend(m)
return modules
def get_source_files(self):
sources = build_py.build_py.get_source_files(self)
for package, src_dir, build_dir, filenames in self.data_files:
for filename in filenames:
sources.append(os.path.join(src_dir, filename))
return sources
def copy_file(self, src, dest, *args, **kwds):
"""
Overridden to validate Python sources before copying them.
"""
if src.endswith('.py') or (sys.platform == 'win32' and
src.endswith('.pyw')):
try:
# Python 2.3+
f = open(src, 'rU')
except:
# Python 2.2
f = open(src, 'r')
codestring = f.read()
codestring = codestring.replace("\r\n", "\n")
codestring = codestring.replace("\r", "\n")
else:
codestring = f.read()
f.close()
if codestring and codestring[-1] != '\n':
codestring = codestring + '\n'
try:
compile(codestring, src, 'exec')
except SyntaxError, detail:
msg, (filename, lineno, offset, line) = detail
if not filename: filename = src
L = ['Syntax error in file "%s", line %d: %s' % (filename,
lineno, msg)]
if line is not None:
i = 0
while i < len(line) and line[i].isspace():
i = i+1
L.append(' %s' % line.strip())
if offset is not None:
s = ' '
for c in line[i:offset-1]:
if c.isspace():
s = s + c
else:
s = s + ' '
L.append('%s^' % s)
raise DistutilsExecError('\n'.join(L))
return build_py.build_py.copy_file(self, src, dest, *args, **kwds)
|