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.
|