importos,sys,inspect,re,imp,pyfromctypes_configureimportstdoutcaptureimportdistutilsdebug=0configdir=py.path.local.make_numbered_dir(prefix='ctypes_configure-')classExternalCompilationInfo(object):_ATTRIBUTES=['pre_include_lines','includes','include_dirs','post_include_lines','libraries','library_dirs','separate_module_sources','separate_module_files']_AVOID_DUPLICATES=['separate_module_files','libraries','includes','include_dirs','library_dirs','separate_module_sources']def__init__(self,pre_include_lines=[],includes=[],include_dirs=[],post_include_lines=[],libraries=[],library_dirs=[],separate_module_sources=[],separate_module_files=[]):""" pre_include_lines: list of lines that should be put at the top of the generated .c files, before any #include. They shouldn't contain an #include themselves. includes: list of .h file names to be #include'd from the generated .c files. include_dirs: list of dir names that is passed to the C compiler post_include_lines: list of lines that should be put at the top of the generated .c files, after the #includes. libraries: list of library names that is passed to the linker library_dirs: list of dir names that is passed to the linker separate_module_sources: list of multiline strings that are each written to a .c file and compiled separately and linked later on. (If function prototypes are needed for other .c files to access this, they can be put in post_include_lines.) separate_module_files: list of .c file names that are compiled separately and linked later on. (If an .h file is needed for other .c files to access this, it can be put in includes.) """fornameinself._ATTRIBUTES:value=locals()[name]assertisinstance(value,(list,tuple))setattr(self,name,tuple(value))def_value(self):returntuple([getattr(self,x)forxinself._ATTRIBUTES])def__hash__(self):returnhash(self._value())def__eq__(self,other):returnself.__class__isother.__class__and \
self._value()==other._value()def__ne__(self,other):returnnotself==otherdef__repr__(self):info=[]forattrinself._ATTRIBUTES:val=getattr(self,attr)info.append("%s=%s"%(attr,repr(val)))return"<ExternalCompilationInfo (%s)>"%", ".join(info)defmerge(self,*others):others=list(others)attrs={}fornameinself._ATTRIBUTES:ifnamenotinself._AVOID_DUPLICATES:s=[]foriin[self]+others:s+=getattr(i,name)attrs[name]=selse:s=set()attr=[]foronein[self]+others:forelemingetattr(one,name):ifelemnotins:s.add(elem)attr.append(elem)attrs[name]=attrreturnExternalCompilationInfo(**attrs)defwrite_c_header(self,fileobj):forlineinself.pre_include_lines:print>>fileobj,lineforpathinself.includes:print>>fileobj,'#include <%s>'%(path,)forlineinself.post_include_lines:print>>fileobj,linedef_copy_attributes(self):d={}forattrinself._ATTRIBUTES:d[attr]=getattr(self,attr)returnddefconvert_sources_to_files(self,cache_dir=None,being_main=False):ifnotself.separate_module_sources:returnselfifcache_dirisNone:cache_dir=configdir.join('module_cache').ensure(dir=1)num=0files=[]forsourceinself.separate_module_sources:while1:filename=cache_dir.join('module_%d.c'%num)num+=1ifnotfilename.check():breakf=filename.open("w")ifbeing_main:f.write("#define PYPY_NOT_MAIN_FILE\n")self.write_c_header(f)source=str(source)f.write(source)ifnotsource.endswith('\n'):f.write('\n')f.close()files.append(str(filename))d=self._copy_attributes()d['separate_module_sources']=()d['separate_module_files']+=tuple(files)returnExternalCompilationInfo(**d)defcompile_shared_lib(self):self=self.convert_sources_to_files()ifnotself.separate_module_files:returnselflib=compile_c_module([],'externmod',self)d=self._copy_attributes()d['libraries']+=(lib,)d['separate_module_files']=()d['separate_module_sources']=()returnExternalCompilationInfo(**d)ifsys.platform=='win32':so_ext='.dll'else:so_ext='.so'defcompiler_command():# e.g. for tcc, you might set this to# "tcc -shared -o %s.so %s.c"returnos.getenv('PYPY_CC')defenable_fast_compilation():ifsys.platform=='win32':dash='/'else:dash='-'fromdistutilsimportsysconfiggcv=sysconfig.get_config_vars()opt=gcv.get('OPT')# not always existentifopt:opt=re.sub('%sO\d+'%dash,'%sO0'%dash,opt)else:opt='%sO0'%dashgcv['OPT']=optdefensure_correct_math():ifsys.platform!='win32':return# so farfromdistutilsimportsysconfiggcv=sysconfig.get_config_vars()opt=gcv.get('OPT')# not always existentifoptand'/Op'notinopt:opt+='/Op'gcv['OPT']=optdeftry_compile(c_files,eci):try:build_executable(c_files,eci)result=Trueexcept(distutils.errors.CompileError,distutils.errors.LinkError):result=Falsereturnresultdefcompile_c_module(cfiles,modbasename,eci,tmpdir=None):#try:# from distutils.log import set_threshold# set_threshold(10000)#except ImportError:# print "ERROR IMPORTING"# passcfiles=[py.path.local(f)forfincfiles]iftmpdirisNone:tmpdir=configdir.join("module_cache").ensure(dir=1)num=0cfiles+=eci.separate_module_filesinclude_dirs=list(eci.include_dirs)library_dirs=list(eci.library_dirs)if(sys.platform=='darwin'or# support Fink & Darwinportssys.platform.startswith('freebsd')):forsin('/sw/','/opt/local/','/usr/local/'):ifs+'include'notininclude_dirsand \
os.path.exists(s+'include'):include_dirs.append(s+'include')ifs+'lib'notinlibrary_dirsand \
os.path.exists(s+'lib'):library_dirs.append(s+'lib')num=0modname=modbasenamewhile1:ifnottmpdir.join(modname+so_ext).check():breaknum+=1modname='%s_%d'%(modbasename,num)lastdir=tmpdir.chdir()libraries=eci.librariesensure_correct_math()try:ifdebug:print"modname",modnamec=stdoutcapture.Capture(mixed_out_err=True)try:try:ifcompiler_command():# GCC-ish options onlyfromdistutilsimportsysconfiggcv=sysconfig.get_config_vars()cmd=compiler_command().replace('%s',str(tmpdir.join(modname)))fordirin[gcv['INCLUDEPY']]+list(include_dirs):cmd+=' -I%s'%dirfordirinlibrary_dirs:cmd+=' -L%s'%diros.system(cmd)else:fromdistutils.distimportDistributionfromdistutils.extensionimportExtensionfromdistutils.ccompilerimportget_default_compilersaved_environ=os.environ.items()try:# distutils.core.setup() is really meant for end-user# interactive usage, because it eats most exceptions and# turn them into SystemExits. Instead, we directly# instantiate a Distribution, which also allows us to# ignore unwanted features like config files.extra_compile_args=[]# ensure correct math on windowsifsys.platform=='win32':extra_compile_args.append('/Op')# get extra precisionifget_default_compiler()=='unix':old_version=Falsetry:g=os.popen('gcc --version','r')verinfo=g.read()g.close()except(OSError,IOError):passelse:old_version=verinfo.startswith('2')ifnotold_version:extra_compile_args.extend(["-Wno-unused-label","-Wno-unused-variable"])attrs={'name':"testmodule",'ext_modules':[Extension(modname,[str(cfile)forcfileincfiles],include_dirs=include_dirs,library_dirs=library_dirs,extra_compile_args=extra_compile_args,libraries=list(libraries),)],'script_name':'setup.py','script_args':['-q','build_ext','--inplace','--force'],}dist=Distribution(attrs)ifnotdist.parse_command_line():raiseValueError,"distutils cmdline parse error"dist.run_commands()finally:forkey,valueinsaved_environ:ifos.environ.get(key)!=value:os.environ[key]=valuefinally:foutput,foutput=c.done()data=foutput.read()ifdata:fdump=open("%s.errors"%modname,"w")fdump.write(data)fdump.close()# XXX do we need to do some check on fout/ferr?# XXX not a nice way to import a moduleexcept:print>>sys.stderr,dataraisefinally:lastdir.chdir()returnstr(tmpdir.join(modname)+so_ext)defmake_module_from_c(cfile,eci):cfile=py.path.local(cfile)modname=cfile.purebasenamecompile_c_module([cfile],modname,eci)returnimport_module_from_directory(cfile.dirpath(),modname)defimport_module_from_directory(dir,modname):file,pathname,description=imp.find_module(modname,[str(dir)])try:mod=imp.load_module(modname,file,pathname,description)finally:iffile:file.close()returnmoddeflog_spawned_cmd(spawn):defspawn_and_log(cmd,*args,**kwds):ifdebug:print' '.join(cmd)returnspawn(cmd,*args,**kwds)returnspawn_and_logclassProfOpt(object):#XXX assuming gcc style flags for nowname="profopt"def__init__(self,compiler):self.compiler=compilerdeffirst(self):self.build('-fprofile-generate')defprobe(self,exe,args):# 'args' is a single string typically containing spaces# and quotes, which represents several arguments.os.system("'%s' %s"%(exe,args))defafter(self):self.build('-fprofile-use')defbuild(self,option):compiler=self.compilercompiler.compile_extra.append(option)compiler.link_extra.append(option)try:compiler._build()finally:compiler.compile_extra.pop()compiler.link_extra.pop()classCCompiler:def__init__(self,cfilenames,eci,outputfilename=None,compiler_exe=None,profbased=None):self.cfilenames=cfilenamesext=''self.compile_extra=[]self.link_extra=[]self.libraries=list(eci.libraries)self.include_dirs=list(eci.include_dirs)self.library_dirs=list(eci.library_dirs)self.compiler_exe=compiler_exeself.profbased=profbasedifnotsys.platformin('win32','darwin','cygwin'):# xxxif'm'notinself.libraries:self.libraries.append('m')if'pthread'notinself.libraries:self.libraries.append('pthread')self.compile_extra+=['-O3','-fomit-frame-pointer','-pthread']self.link_extra+=['-pthread']ifsys.platform=='win32':self.link_extra+=['/DEBUG']# generate .pdb fileif(sys.platform=='darwin'or# support Fink & Darwinportssys.platform.startswith('freebsd')):forsin('/sw/','/opt/local/','/usr/local/'):ifs+'include'notinself.include_dirsand \
os.path.exists(s+'include'):self.include_dirs.append(s+'include')ifs+'lib'notinself.library_dirsand \
os.path.exists(s+'lib'):self.library_dirs.append(s+'lib')self.compile_extra+=['-O3','-fomit-frame-pointer']ifoutputfilenameisNone:self.outputfilename=py.path.local(cfilenames[0]).new(ext=ext)else:self.outputfilename=py.path.local(outputfilename)defbuild(self,noerr=False):basename=self.outputfilename.new(ext='')data=''try:saved_environ=os.environ.copy()c=stdoutcapture.Capture(mixed_out_err=True)try:self._build()finally:# workaround for a distutils bugs where some env vars can# become longer and longer every time it is usedforkey,valueinsaved_environ.items():ifos.environ.get(key)!=value:os.environ[key]=valuefoutput,foutput=c.done()data=foutput.read()ifdata:fdump=basename.new(ext='errors').open("w")fdump.write(data)fdump.close()except:ifnotnoerr:print>>sys.stderr,dataraisedef_build(self):fromdistutils.ccompilerimportnew_compilercompiler=new_compiler(force=1)ifself.compiler_exeisnotNone:forcin'''compiler compiler_so compiler_cxx linker_exe linker_so'''.split():compiler.executables[c][0]=self.compiler_execompiler.spawn=log_spawned_cmd(compiler.spawn)objects=[]forcfileinself.cfilenames:cfile=py.path.local(cfile)old=cfile.dirpath().chdir()try:res=compiler.compile([cfile.basename],include_dirs=self.include_dirs,extra_preargs=self.compile_extra)assertlen(res)==1cobjfile=py.path.local(res[0])assertcobjfile.check()objects.append(str(cobjfile))finally:old.chdir()compiler.link_executable(objects,str(self.outputfilename),libraries=self.libraries,extra_preargs=self.link_extra,library_dirs=self.library_dirs)defbuild_executable(*args,**kwds):noerr=kwds.pop('noerr',False)compiler=CCompiler(*args,**kwds)compiler.build(noerr=noerr)returnstr(compiler.outputfilename)