Quantitative Analysis
Parallel Processing
Numerical Analysis
C++ Multithreading
Python for Excel
Python Utilities
Services
Author

I. Python Object Browser.
II. Python to R Communicator.
III. Manipulation of piecewise polynomial functions.
IV. Building C++ projects.
1. Why not use bjam?
2. Installation of ots.make.
3. Code structure (ots.make).
4. Example. Building boost::python DLL.
5. Example. Building boost::python extension involving Cuda code.
6. Example. Building in-process COM DLL with embedded boost::python engine.
Downloads. Index. Contents.

Code structure (ots.make).


call to ots.make.generator.MakeFile constructor takes a list of files. Each file is converted into a file object containing file's directory, name and extension. The function MakeFile.make() converts these input files according to a set of rules. File objects with "cpp" extension are converted into file objects with "obj" extension, "cu" files become "cu.obj" files and so fourth. Collateral result of such process is forming make file macros and clauses.

The following is the principal part of the code:

class MakeFile :

doDeleteFiles=True

def __init__(self,

outputName,outputDir,files,

objDir='objs',

version=None,runtime=None,output=None,

askBeforeDelete=True,

\qquad** kwargs

) :

import ots.make.options as opt

if( version is None ) :

version=opt.Version.Release

if( runtime is None ) :

runtime=opt.Runtime.Msvc9

if( output is None ) :

output=opt.Output.Dll

self.version=version

self.runtime=runtime

self.output=output

self.outputName=outputName

self.outputDir=outputDir

self.objDir=objDir

self.files=files

self.askBeforeDelete=askBeforeDelete

od=str(objDir)

if( od[1]==':' ) :

self.objDir=objDir

else :

self.objDir=self.outputDir+objDir

for k in kwargs :

setattr(self,k,kwargs[k])

def make(self) :

import ots.make.generator as g

import ots.io as io

clauses=g.Clauses()

macros=g.Macros()

rules=g.Rules()

files=[]

for (dr,fls) in self.files :

for f in fls :

sn=io.ShortFileName(f)

files.append(g.File(dirPath=dr,name=sn._name,ext=sn._ext))

while(True) :

if( len(files)==0 ) :

break

f=files.pop(0)

processed=False

for r in rules :

try :

ff=r.process(

file=f,

macros=macros,

clauses=clauses,

generator=self

)

if( not ff is None ) :

files.append(ff)

processed=True

break

except NotApplicable :

pass

if( not processed ) :

raise(Exception('Could not find a rule for the following file:\n'+str(f)))

import os

objDir=str(self.objDir)

if( not os.path.exists(objDir) ) :

os.makedirs(objDir)

makeFile=str(self.objDir+'doIt.mak')

f=open(makeFile,'w')

f.write(str(macros))

f.write('\n')

f.write(str(clauses))

f.write('\n')

f.close()

In the constructor all the input data is attached to "self". The calculated make file information is accumulated in the fields self.macros and self.clauses created anew at every call to make(). All of these fields are propagated everywhere in the code.

The lines

if( not ff is None ) :

files.append(ff)

refer to the possibility of not returning any result from the call to process(). This happens, for example, with "obj" files. Processing of "obj" file results in incremental construction of link clause and does not require further processing.

The line "rules=g.Rules()" refers to the function Rules() declared in the file make/generator/rules.py:

def Rules() :

return [

_CppRule(),

_ObjRule(),

_CuRule(),

_CuObjRule(),

_IdlRule(),

_TlbRule(),

_ResRule(),

_DefRule(),

_RcRule()

]

The following an example of a rule:

class _ObjRule(IFinalRule) :

def _isApplicable(self) :

return self.file.ext=='obj'

def _process(self) :

self.macros.objs.add(self.file)

return None

where the IFinalRule contains implementation of the function "process":

class IRule(object) :

def process(self,file,macros,clauses,generator) :

self.file=file

import ots.make.generator as g

if( not isinstance(self.file,g.File) ) :

raise(Exception('ots.make.generator.File object expected'))

if( not self._isApplicable() ) :

raise(NotApplicable())

self.macros=macros

self.clauses=clauses

self.generator=generator

self._preprocessing()

return self._process()

def _preprocessing(self) :

pass

def _process(self) :

raise(Exception('process is not implemented'))

def _isApplicable(self) :

raise(Exception('process is not implemented'))

class IFinalRule(IRule) :

...

One can see that IRule takes care of propagation of data and leaves out hooks _preprocessing, _process and _isApplicable. The following rule is a more informative example of how such data is used.

class _CppRule(IInitialRule) :

def _isApplicable(self) :

return self.file.ext=='cpp' or self.file.ext=='c'

def _process(self) :

self.macros.addIfDoesNotExist('compilerOptions',self._makeCompilerOptionsMacro)

i=self._inputDirIndex

r=File(dirPath='$(OBJ_DIR'+str(i)+')',name=self.file.name,ext='obj')

self.clauses.add(self._makeClause(i,r))

return r

def _makeClause(self,index,objFile) :

import ots.io as io

return Clause(

goal='"'+str(objFile)

+'" : "$(SRC_DIR'+str(index)+')'

+io.separator

+str(self.file.name)+'.'+str(self.file.ext)+'"',

makeLines=['cl $? -Fo$@ $(CP_OPTS)']

)

def _makeCompilerOptionsMacro(self) :

return Macro(name='CP_OPTS',clause=_getOptions(self.generator,'compile'))

The function "addIfDoesNotExist" makes sure that the argument "self._makeCompilerOptionsMacro" is called only once and the field self.macros.compilerOptions is created only once (per call to MakeFile.make()).

The function "_getOptions" scans entire reference tree started at self.generator for anything that has "compile" attribute and collect results of all calls to "compile". This mechanics is responsible for assembling the input information into a meaningful make file.





Downloads. Index. Contents.


















Copyright 2007