#!/usr/bin/env python
import sys, os, os.path, re, string
from distutils.filelist import FileList
from util import *
import distutils.cmd
import traceback
import time, string
# re for CVS keywords
CVS_keyword = re.compile(r'[$][A-Za-z]+:\s+([^$]+?)\s+[$]')
def check_swig_version(swig_name):
try:
if hasattr( os, 'popen3'):
sin,sout,stderr = os.popen3("%s -version" % swig_name)
data = stderr.read()
else:
data = os.popen( "%s -version" % swig_name).read()
if string.find(data,"1.3.23") == -1:
return 0
else:
return 1
except:
if __debug__:
traceback.print_exc(file = sys.stderr)
return 0
def handle_wrong_swig_version():
print "WARNING!!! wrong swig version. Need 1.3.23, continuing anyway."
time.sleep(3)
class build_w(distutils.cmd.Command):
description = '"build" Python wrappers using SWIG.'
user_options = [
('force', 'f', "forcibly build everything (ignore file timestamps)"),
]
boolean_options = ['force']
def initialize_options (self):
self.force = None
def finalize_options (self):
self.set_undefined_options('build', ('force', 'force'))
def run (self):
# try to run swig
self.swig_name = None
for swig_name in ('swig', 'swig1.3'):
try:
# should do a version check
self.spawn([swig_name, '-version'])
self.swig_name = swig_name
if not check_swig_version(swig_name):
handle_wrong_swig_version()
self.swig_name = None
break
except:
pass
# if brain dead spawn doesn't work...
if self.swig_name is None:
for swig_name in ('swig', 'swig1.3'):
try:
r = os.system("%s -version" % swig_name)
except:
r == 99999999
if r == 256:
self.swig_name = swig_name
if not check_swig_version(swig_name):
handle_wrong_swig_version()
# found the swig name, stop checking.
break
if self.swig_name is None:
self.warn("Can't find SWIG, will just have to do with the existing wrapper source.")
else:
self.mkpath('src/interface')
self.mkpath('src/shadow')
interfaces = []
f = FileList()
f.findall('interface')
f.process_template_line('global-include *.i')
f.files.sort()
for file in f.files:
interfaces.append(string.join(string.split(os.path.splitext(file)[0], os.sep)[1:], '.'))
interfaces.sort()
for full_module_name in interfaces:
self.swig(full_module_name)
def make_version_selector(self, path, config_path, pre_text, version_text, post_text, BUILD):
module_name = os.path.splitext(os.path.basename(path))[0]
f = open(path, 'w')
# not really sure if we need to propagate the BUILD, specifically the libs value, to the C file
# build_ext doesn't need it but build_py may. If distutils supported preprocessing better than
# we wouldn't need to link in in build_py, hence wouldn't need to to propagate the BUILD info.
f.write('/*\n')
for key, item in BUILD.items():
# write the build info, but don't include stuff that setup.py doesn't need
if key not in ('shadow', 'macro_template', 'api_versions', 'headers'):
f.write('# BUILD %s %s\n' % (key, repr(item)))
f.write('*/\n')
# the headers including any needed by the module
f.write('#include "%s"\n' % config_path)
for header in BUILD['headers']:
f.write('#include <%s>\n' % header)
f.write('\n')
f.write(pre_text)
if BUILD.has_key('macro_template'):
for i in range(len(BUILD['api_versions'])):
# api_version_underscore should be a matter of convenience, but SWIG's preprocessor is dysfunctional
api_version_underscore = '%d_%d' % ((BUILD['api_versions'][i] & 0xff00) >> 8, BUILD['api_versions'][i] & 0xff)
# setup the testing macro
macro = BUILD['macro_template'] % {'api_version':BUILD['api_versions'][i], 'api_version_underscore':api_version_underscore}
if i == 0:
f.write('#if ' + macro + '\n')
elif not BUILD['api_version_check'] and i+1 == len(BUILD['api_versions']):
f.write('#else\n')
else:
f.write('#elif ' + macro + '\n')
f.write(version_text % BUILD['api_versions'][i])
if BUILD['api_version_check']:
f.write('#else\n')
f.write('#error "Do not know how to compile %s for API version < 0x%04x"\n' % (module_name, BUILD['api_versions'][-1]))
f.write('#endif\n')
elif len(BUILD['api_versions']) > 1:
print 'Multiple API versions specified for %s but no macro template provided!' % module_name
sys.exit(1)
else:
f.write(version_text % BUILD['api_versions'][0])
f.write(post_text)
f.close()
def make_wrappers(self, swig_cmd, module_name, source_path, shadow_path):
self.swig_just_ran = 1
try:
# this shouldn't be req, but SWIG seems to like it
os.remove(source_path)
except:
pass
try:
self.spawn(swig_cmd)
if not os.path.exists(source_path):
sys.exit(1)
if shadow_path:
# copy the shadow script
t = module_name + '.py'
# SWIG isn't very consistent in its placement of the shadow script
for f in [t, os.path.join('src', t), os.path.join('src', 'interface', t), os.path.join( 'src','shadow',t)]:
if os.path.exists(f):
# does this screw up things on the Mac?
x = open(f).read()
x = string.replace(x, module_name + 'c', module_name + '_')
open(shadow_path, 'w').write(x)
os.remove(f)
break
f = open(source_path)
x = f.read()
f.close()
# replace CVS keywords
x = CVS_keyword.sub(r'\1', x)
x = string.replace(x, module_name + 'c', module_name + '_')
# find all the docstrings and put them into the function table
for method in re.findall('static char _doc[_]([^[\s]+)', x):
x = re.sub(
r'[{]\s*\(\s*char\s*\*\s*\)\s*"%s"\s*,\s*([^ ",]+)\s*,\s*([^,"}]+)[}]' % method,
r'{ (char *)"%s", \1, \2, _doc_%s}' % (method, method),
x
)
f = open(source_path, 'w')
f.write(x)
f.close()
except:
if os.path.exists(source_path):
os.remove(source_path)
sys.exit(1)
def swig(self, full_module_name):
global CVS_keyword
self.announce('Building wrappers for %s' % full_module_name)
# split the full module name into the short name and the path
m = string.split(full_module_name, '.')
module_name = m[-1]
if len(m) == 1:
module_path = ''
else:
module_path = apply(os.path.join, m[:-1])
# the swig cmd line
swig_cmd = [self.swig_name, '-python', '-Iinterface']
# create the req paths and figure out the interface name
base_path = apply(os.path.join, m)
self.mkpath(string.join(['OpenGL'] + m[:-1], '/'))
interface_path = os.path.join('interface', base_path + '.i')
# retrieve the build info
BUILD = get_build_info(interface_path)
if BUILD['shadow']:
mkdir(os.path.join('src', 'shadow'))
# if this is a shadow interface then append a '_' to the end of the c module name
m2 = m[:]
m2[-1] = '_' + m2[-1]
c_full_module_name = '.'.join( m2 )
c_module_name = m2[-1]
# insert the shadow option
swig_cmd.append('-shadow')
# figure out the script names
shadow_version_selector_path = os.path.join('src', 'shadow', full_module_name + '.c')
self.make_file([interface_path],
shadow_version_selector_path,
self.make_version_selector,
(shadow_version_selector_path,
'src/config.h',
'#include <stdio.h>\n\nint main(int argc, char **argv)\n{\n\tFILE *f = fopen("api_version", "w");\n',
'\tfprintf(f, "%d");\n',
'\tfclose(f);\n\treturn 0;\n}\n',
BUILD),
exec_msg = 'Generating shadow version selector',
skip_msg = "Shadow version selector doesn't need updating")
else:
# no modification of the module name needed
c_full_module_name = full_module_name
c_module_name = module_name
# fiqure out the interface source names
src_wrapper_path = os.path.join('src', 'interface', c_full_module_name + '.c')
c_version_selector_path = os.path.join('src', 'interface', c_full_module_name + '.c')
self.make_file([interface_path],
c_version_selector_path,
self.make_version_selector,
(c_version_selector_path,
'../config.h',
'',
'#include "%s.%%04x.inc"\n' % c_full_module_name,
'',
BUILD),
exec_msg = 'Generating C version selector',
skip_msg = "C version selector doesn't need updating")
source_path_template = os.path.join('src', 'interface', c_full_module_name + '.%04x.inc')
shadow_path_template = os.path.join('src', 'shadow', full_module_name + '.%04x.py')
for api_version in BUILD['api_versions']:
# fiqure out the paths
source_path = source_path_template % api_version
if BUILD['shadow']:
shadow_path = shadow_path_template % api_version
outfiles = (source_path, shadow_path)
else:
shadow_path = ''
outfiles = (source_path,)
this_swig_cmd = swig_cmd + ['-DAPI_VERSION=%d' % api_version, '-o', source_path, interface_path]
self.swig_just_ran = 0
args = (this_swig_cmd, module_name, source_path, shadow_path)
exec_msg = 'Generating wrappers for API version 0x%04x' % api_version
skip_msg = "Wrappers for API version 0x%04x don't need updating" % api_version
for outfile in outfiles:
if self.swig_just_ran:
break
self.make_file([interface_path],
outfile,
self.make_wrappers,
args,
exec_msg = exec_msg,
skip_msg = skip_msg)
self.announce('')
|