Back to index

enigmail  1.4.3
functions.py
Go to the documentation of this file.
00001 """
00002 Makefile functions.
00003 """
00004 
00005 import parser, util
00006 import subprocess, os, logging
00007 from globrelative import glob
00008 from cStringIO import StringIO
00009 
00010 log = logging.getLogger('pymake.data')
00011 
00012 class Function(object):
00013     """
00014     An object that represents a function call. This class is always subclassed
00015     with the following methods and attributes:
00016 
00017     minargs = minimum # of arguments
00018     maxargs = maximum # of arguments (0 means unlimited)
00019 
00020     def resolve(self, makefile, variables, fd, setting)
00021         Calls the function
00022         calls fd.write() with strings
00023     """
00024 
00025     __slots__ = ('_arguments', 'loc')
00026 
00027     def __init__(self, loc):
00028         self._arguments = []
00029         self.loc = loc
00030         assert self.minargs > 0
00031 
00032     def __getitem__(self, key):
00033         return self._arguments[key]
00034 
00035     def setup(self):
00036         argc = len(self._arguments)
00037 
00038         if argc < self.minargs:
00039             raise data.DataError("Not enough arguments to function %s, requires %s" % (self.name, self.minargs), self.loc)
00040 
00041         assert self.maxargs == 0 or argc <= self.maxargs, "Parser screwed up, gave us too many args"
00042 
00043     def append(self, arg):
00044         assert isinstance(arg, (data.Expansion, data.StringExpansion))
00045         self._arguments.append(arg)
00046 
00047     def __len__(self):
00048         return len(self._arguments)
00049 
00050 class VariableRef(Function):
00051     __slots__ = ('vname', 'loc')
00052 
00053     def __init__(self, loc, vname):
00054         self.loc = loc
00055         assert isinstance(vname, (data.Expansion, data.StringExpansion))
00056         self.vname = vname
00057         
00058     def setup(self):
00059         assert False, "Shouldn't get here"
00060 
00061     def resolve(self, makefile, variables, fd, setting):
00062         vname = self.vname.resolvestr(makefile, variables, setting)
00063         if vname in setting:
00064             raise data.DataError("Setting variable '%s' recursively references itself." % (vname,), self.loc)
00065 
00066         flavor, source, value = variables.get(vname)
00067         if value is None:
00068             log.debug("%s: variable '%s' was not set" % (self.loc, vname))
00069             return
00070 
00071         value.resolve(makefile, variables, fd, setting + [vname])
00072 
00073 class SubstitutionRef(Function):
00074     """$(VARNAME:.c=.o) and $(VARNAME:%.c=%.o)"""
00075 
00076     __slots__ = ('loc', 'vname', 'substfrom', 'substto')
00077 
00078     def __init__(self, loc, varname, substfrom, substto):
00079         self.loc = loc
00080         self.vname = varname
00081         self.substfrom = substfrom
00082         self.substto = substto
00083 
00084     def setup(self):
00085         assert False, "Shouldn't get here"
00086 
00087     def resolve(self, makefile, variables, fd, setting):
00088         vname = self.vname.resolvestr(makefile, variables, setting)
00089         if vname in setting:
00090             raise data.DataError("Setting variable '%s' recursively references itself." % (vname,), self.loc)
00091 
00092         substfrom = self.substfrom.resolvestr(makefile, variables, setting)
00093         substto = self.substto.resolvestr(makefile, variables, setting)
00094 
00095         flavor, source, value = variables.get(vname)
00096         if value is None:
00097             log.debug("%s: variable '%s' was not set" % (self.loc, vname))
00098             return
00099 
00100         f = data.Pattern(substfrom)
00101         if not f.ispattern():
00102             f = data.Pattern('%' + substfrom)
00103             substto = '%' + substto
00104 
00105         fd.write(' '.join([f.subst(substto, word, False)
00106                            for word in value.resolvesplit(makefile, variables, setting + [vname])]))
00107 
00108 class SubstFunction(Function):
00109     name = 'subst'
00110     minargs = 3
00111     maxargs = 3
00112 
00113     __slots__ = Function.__slots__
00114 
00115     def resolve(self, makefile, variables, fd, setting):
00116         s = self._arguments[0].resolvestr(makefile, variables, setting)
00117         r = self._arguments[1].resolvestr(makefile, variables, setting)
00118         d = self._arguments[2].resolvestr(makefile, variables, setting)
00119         fd.write(d.replace(s, r))
00120 
00121 class PatSubstFunction(Function):
00122     name = 'patsubst'
00123     minargs = 3
00124     maxargs = 3
00125 
00126     __slots__ = Function.__slots__
00127 
00128     def resolve(self, makefile, variables, fd, setting):
00129         s = self._arguments[0].resolvestr(makefile, variables, setting)
00130         r = self._arguments[1].resolvestr(makefile, variables, setting)
00131 
00132         p = data.Pattern(s)
00133         fd.write(' '.join([p.subst(r, word, False)
00134                            for word in self._arguments[2].resolvesplit(makefile, variables, setting)]))
00135 
00136 class StripFunction(Function):
00137     name = 'strip'
00138     minargs = 1
00139     maxargs = 1
00140 
00141     __slots__ = Function.__slots__
00142 
00143     def resolve(self, makefile, variables, fd, setting):
00144         util.joiniter(fd, self._arguments[0].resolvesplit(makefile, variables, setting))
00145 
00146 class FindstringFunction(Function):
00147     name = 'findstring'
00148     minargs = 2
00149     maxargs = 2
00150 
00151     __slots__ = Function.__slots__
00152 
00153     def resolve(self, makefile, variables, fd, setting):
00154         s = self._arguments[0].resolvestr(makefile, variables, setting)
00155         r = self._arguments[1].resolvestr(makefile, variables, setting)
00156         if r.find(s) == -1:
00157             return
00158         fd.write(s)
00159 
00160 class FilterFunction(Function):
00161     name = 'filter'
00162     minargs = 2
00163     maxargs = 2
00164 
00165     __slots__ = Function.__slots__
00166 
00167     def resolve(self, makefile, variables, fd, setting):
00168         plist = [data.Pattern(p)
00169                  for p in self._arguments[0].resolvesplit(makefile, variables, setting)]
00170 
00171         fd.write(' '.join([w for w in self._arguments[1].resolvesplit(makefile, variables, setting)
00172                            if util.any((p.match(w) for p in plist))]))
00173 
00174 class FilteroutFunction(Function):
00175     name = 'filter-out'
00176     minargs = 2
00177     maxargs = 2
00178 
00179     __slots__ = Function.__slots__
00180 
00181     def resolve(self, makefile, variables, fd, setting):
00182         plist = [data.Pattern(p)
00183                  for p in self._arguments[0].resolvesplit(makefile, variables, setting)]
00184 
00185         fd.write(' '.join([w for w in self._arguments[1].resolvesplit(makefile, variables, setting)
00186                            if not util.any((p.match(w) for p in plist))]))
00187 
00188 class SortFunction(Function):
00189     name = 'sort'
00190     minargs = 1
00191     maxargs = 1
00192 
00193     __slots__ = Function.__slots__
00194 
00195     def resolve(self, makefile, variables, fd, setting):
00196         d = set(self._arguments[0].resolvesplit(makefile, variables, setting))
00197         util.joiniter(fd, sorted(d))
00198 
00199 class WordFunction(Function):
00200     name = 'word'
00201     minargs = 2
00202     maxargs = 2
00203 
00204     __slots__ = Function.__slots__
00205 
00206     def resolve(self, makefile, variables, fd, setting):
00207         n = self._arguments[0].resolvestr(makefile, variables, setting)
00208         # TODO: provide better error if this doesn't convert
00209         n = int(n)
00210         words = list(self._arguments[1].resolvesplit(makefile, variables, setting))
00211         if n < 1 or n > len(words):
00212             return
00213         fd.write(words[n - 1])
00214 
00215 class WordlistFunction(Function):
00216     name = 'wordlist'
00217     minargs = 3
00218     maxargs = 3
00219 
00220     __slots__ = Function.__slots__
00221 
00222     def resolve(self, makefile, variables, fd, setting):
00223         nfrom = self._arguments[0].resolvestr(makefile, variables, setting)
00224         nto = self._arguments[1].resolvestr(makefile, variables, setting)
00225         # TODO: provide better errors if this doesn't convert
00226         nfrom = int(nfrom)
00227         nto = int(nto)
00228 
00229         words = list(self._arguments[2].resolvesplit(makefile, variables, setting))
00230 
00231         if nfrom < 1:
00232             nfrom = 1
00233         if nto < 1:
00234             nto = 1
00235 
00236         util.joiniter(fd, words[nfrom - 1:nto])
00237 
00238 class WordsFunction(Function):
00239     name = 'words'
00240     minargs = 1
00241     maxargs = 1
00242 
00243     __slots__ = Function.__slots__
00244 
00245     def resolve(self, makefile, variables, fd, setting):
00246         fd.write(str(len(self._arguments[0].resolvesplit(makefile, variables, setting))))
00247 
00248 class FirstWordFunction(Function):
00249     name = 'firstword'
00250     minargs = 1
00251     maxargs = 1
00252 
00253     __slots__ = Function.__slots__
00254 
00255     def resolve(self, makefile, variables, fd, setting):
00256         l = self._arguments[0].resolvesplit(makefile, variables, setting)
00257         if len(l):
00258             fd.write(l[0])
00259 
00260 class LastWordFunction(Function):
00261     name = 'lastword'
00262     minargs = 1
00263     maxargs = 1
00264 
00265     __slots__ = Function.__slots__
00266 
00267     def resolve(self, makefile, variables, fd, setting):
00268         l = self._arguments[0].resolvesplit(makefile, variables, setting)
00269         if len(l):
00270             fd.write(l[-1])
00271 
00272 def pathsplit(path, default='./'):
00273     """
00274     Splits a path into dirpart, filepart on the last slash. If there is no slash, dirpart
00275     is ./
00276     """
00277     dir, slash, file = util.strrpartition(path, '/')
00278     if dir == '':
00279         return default, file
00280 
00281     return dir + slash, file
00282 
00283 class DirFunction(Function):
00284     name = 'dir'
00285     minargs = 1
00286     maxargs = 1
00287 
00288     def resolve(self, makefile, variables, fd, setting):
00289         fd.write(' '.join([pathsplit(path)[0]
00290                            for path in self._arguments[0].resolvesplit(makefile, variables, setting)]))
00291 
00292 class NotDirFunction(Function):
00293     name = 'notdir'
00294     minargs = 1
00295     maxargs = 1
00296 
00297     __slots__ = Function.__slots__
00298 
00299     def resolve(self, makefile, variables, fd, setting):
00300         fd.write(' '.join([pathsplit(path)[1]
00301                            for path in self._arguments[0].resolvesplit(makefile, variables, setting)]))
00302 
00303 class SuffixFunction(Function):
00304     name = 'suffix'
00305     minargs = 1
00306     maxargs = 1
00307 
00308     __slots__ = Function.__slots__
00309 
00310     @staticmethod
00311     def suffixes(words):
00312         for w in words:
00313             dir, file = pathsplit(w)
00314             base, dot, suffix = util.strrpartition(file, '.')
00315             if base != '':
00316                 yield dot + suffix
00317 
00318     def resolve(self, makefile, variables, fd, setting):
00319         util.joiniter(fd, self.suffixes(self._arguments[0].resolvesplit(makefile, variables, setting)))
00320 
00321 class BasenameFunction(Function):
00322     name = 'basename'
00323     minargs = 1
00324     maxargs = 1
00325 
00326     __slots__ = Function.__slots__
00327 
00328     @staticmethod
00329     def basenames(words):
00330         for w in words:
00331             dir, file = pathsplit(w, '')
00332             base, dot, suffix = util.strrpartition(file, '.')
00333             if dot == '':
00334                 base = suffix
00335 
00336             yield dir + base
00337 
00338     def resolve(self, makefile, variables, fd, setting):
00339         util.joiniter(fd, self.basenames(self._arguments[0].resolvesplit(makefile, variables, setting)))
00340 
00341 class AddSuffixFunction(Function):
00342     name = 'addprefix'
00343     minargs = 2
00344     maxargs = 2
00345 
00346     __slots__ = Function.__slots__
00347 
00348     def resolve(self, makefile, variables, fd, setting):
00349         suffix = self._arguments[0].resolvestr(makefile, variables, setting)
00350 
00351         fd.write(' '.join([w + suffix for w in self._arguments[1].resolvesplit(makefile, variables, setting)]))
00352 
00353 class AddPrefixFunction(Function):
00354     name = 'addsuffix'
00355     minargs = 2
00356     maxargs = 2
00357 
00358     def resolve(self, makefile, variables, fd, setting):
00359         prefix = self._arguments[0].resolvestr(makefile, variables, setting)
00360 
00361         fd.write(' '.join([prefix + w for w in self._arguments[1].resolvesplit(makefile, variables, setting)]))
00362 
00363 class JoinFunction(Function):
00364     name = 'join'
00365     minargs = 2
00366     maxargs = 2
00367 
00368     __slots__ = Function.__slots__
00369 
00370     @staticmethod
00371     def iterjoin(l1, l2):
00372         for i in xrange(0, max(len(l1), len(l2))):
00373             i1 = i < len(l1) and l1[i] or ''
00374             i2 = i < len(l2) and l2[i] or ''
00375             yield i1 + i2
00376 
00377     def resolve(self, makefile, variables, fd, setting):
00378         list1 = list(self._arguments[0].resolvesplit(makefile, variables, setting))
00379         list2 = list(self._arguments[1].resolvesplit(makefile, variables, setting))
00380 
00381         util.joiniter(fd, self.iterjoin(list1, list2))
00382 
00383 class WildcardFunction(Function):
00384     name = 'wildcard'
00385     minargs = 1
00386     maxargs = 1
00387 
00388     __slots__ = Function.__slots__
00389 
00390     def resolve(self, makefile, variables, fd, setting):
00391         patterns = self._arguments[0].resolvesplit(makefile, variables, setting)
00392 
00393         fd.write(' '.join([x.replace('\\','/')
00394                            for p in patterns
00395                            for x in glob(makefile.workdir, p)]))
00396 
00397     __slots__ = Function.__slots__
00398 
00399 class RealpathFunction(Function):
00400     name = 'realpath'
00401     minargs = 1
00402     maxargs = 1
00403 
00404     def resolve(self, makefile, variables, fd, setting):
00405         fd.write(' '.join([os.path.realpath(os.path.join(makefile.workdir, path)).replace('\\', '/')
00406                            for path in self._arguments[0].resolvesplit(makefile, variables, setting)]))
00407 
00408 class AbspathFunction(Function):
00409     name = 'abspath'
00410     minargs = 1
00411     maxargs = 1
00412 
00413     __slots__ = Function.__slots__
00414 
00415     def resolve(self, makefile, variables, fd, setting):
00416         assert os.path.isabs(makefile.workdir)
00417         fd.write(' '.join([util.normaljoin(makefile.workdir, path).replace('\\', '/')
00418                            for path in self._arguments[0].resolvesplit(makefile, variables, setting)]))
00419 
00420 class IfFunction(Function):
00421     name = 'if'
00422     minargs = 1
00423     maxargs = 3
00424 
00425     __slots__ = Function.__slots__
00426 
00427     def setup(self):
00428         Function.setup(self)
00429         self._arguments[0].lstrip()
00430         self._arguments[0].rstrip()
00431 
00432     def resolve(self, makefile, variables, fd, setting):
00433         condition = self._arguments[0].resolvestr(makefile, variables, setting)
00434 
00435         if len(condition):
00436             self._arguments[1].resolve(makefile, variables, fd, setting)
00437         elif len(self._arguments) > 2:
00438             return self._arguments[2].resolve(makefile, variables, fd, setting)
00439 
00440 class OrFunction(Function):
00441     name = 'or'
00442     minargs = 1
00443     maxargs = 0
00444 
00445     __slots__ = Function.__slots__
00446 
00447     def resolve(self, makefile, variables, fd, setting):
00448         for arg in self._arguments:
00449             r = arg.resolvestr(makefile, variables, setting)
00450             if r != '':
00451                 fd.write(r)
00452                 return
00453 
00454 class AndFunction(Function):
00455     name = 'and'
00456     minargs = 1
00457     maxargs = 0
00458 
00459     __slots__ = Function.__slots__
00460 
00461     def resolve(self, makefile, variables, fd, setting):
00462         r = ''
00463 
00464         for arg in self._arguments:
00465             r = arg.resolvestr(makefile, variables, setting)
00466             if r == '':
00467                 return
00468 
00469         fd.write(r)
00470 
00471 class ForEachFunction(Function):
00472     name = 'foreach'
00473     minargs = 3
00474     maxargs = 3
00475 
00476     __slots__ = Function.__slots__
00477 
00478     def resolve(self, makefile, variables, fd, setting):
00479         vname = self._arguments[0].resolvestr(makefile, variables, setting)
00480         e = self._arguments[2]
00481 
00482         v = data.Variables(parent=variables)
00483         firstword = True
00484 
00485         for w in self._arguments[1].resolvesplit(makefile, variables, setting):
00486             if firstword:
00487                 firstword = False
00488             else:
00489                 fd.write(' ')
00490 
00491             v.set(vname, data.Variables.FLAVOR_SIMPLE, data.Variables.SOURCE_AUTOMATIC, w)
00492             e.resolve(makefile, v, fd, setting)
00493 
00494 class CallFunction(Function):
00495     name = 'call'
00496     minargs = 1
00497     maxargs = 0
00498 
00499     __slots__ = Function.__slots__
00500 
00501     def resolve(self, makefile, variables, fd, setting):
00502         vname = self._arguments[0].resolvestr(makefile, variables, setting)
00503         if vname in setting:
00504             raise data.DataError("Recursively setting variable '%s'" % (vname,))
00505 
00506         v = data.Variables(parent=variables)
00507         v.set('0', data.Variables.FLAVOR_SIMPLE, data.Variables.SOURCE_AUTOMATIC, vname)
00508         for i in xrange(1, len(self._arguments)):
00509             param = self._arguments[i].resolvestr(makefile, variables, setting)
00510             v.set(str(i), data.Variables.FLAVOR_SIMPLE, data.Variables.SOURCE_AUTOMATIC, param)
00511 
00512         flavor, source, e = variables.get(vname)
00513 
00514         if e is None:
00515             return
00516 
00517         if flavor == data.Variables.FLAVOR_SIMPLE:
00518             log.warning("%s: calling variable '%s' which is simply-expanded" % (self.loc, vname))
00519 
00520         # but we'll do it anyway
00521         e.resolve(makefile, v, fd, setting + [vname])
00522 
00523 class ValueFunction(Function):
00524     name = 'value'
00525     minargs = 1
00526     maxargs = 1
00527 
00528     __slots__ = Function.__slots__
00529 
00530     def resolve(self, makefile, variables, fd, setting):
00531         varname = self._arguments[0].resolvestr(makefile, variables, setting)
00532 
00533         flavor, source, value = variables.get(varname, expand=False)
00534         if value is not None:
00535             fd.write(value)
00536 
00537 class EvalFunction(Function):
00538     name = 'eval'
00539     minargs = 1
00540     maxargs = 1
00541 
00542     def resolve(self, makefile, variables, fd, setting):
00543         if makefile.parsingfinished:
00544             # GNU make allows variables to be set by recursive expansion during
00545             # command execution. This seems really dumb to me, so I don't!
00546             raise data.DataError("$(eval) not allowed via recursive expansion after parsing is finished", self.loc)
00547 
00548         stmts = parser.parsestring(self._arguments[0].resolvestr(makefile, variables, setting),
00549                                    'evaluation from %s' % self.loc)
00550         stmts.execute(makefile)
00551 
00552 class OriginFunction(Function):
00553     name = 'origin'
00554     minargs = 1
00555     maxargs = 1
00556 
00557     __slots__ = Function.__slots__
00558 
00559     def resolve(self, makefile, variables, fd, setting):
00560         vname = self._arguments[0].resolvestr(makefile, variables, setting)
00561 
00562         flavor, source, value = variables.get(vname)
00563         if source is None:
00564             r = 'undefined'
00565         elif source == data.Variables.SOURCE_OVERRIDE:
00566             r = 'override'
00567 
00568         elif source == data.Variables.SOURCE_MAKEFILE:
00569             r = 'file'
00570         elif source == data.Variables.SOURCE_ENVIRONMENT:
00571             r = 'environment'
00572         elif source == data.Variables.SOURCE_COMMANDLINE:
00573             r = 'command line'
00574         elif source == data.Variables.SOURCE_AUTOMATIC:
00575             r = 'automatic'
00576         elif source == data.Variables.SOURCE_IMPLICIT:
00577             r = 'default'
00578 
00579         fd.write(r)
00580 
00581 class FlavorFunction(Function):
00582     name = 'flavor'
00583     minargs = 1
00584     maxargs = 1
00585 
00586     __slots__ = Function.__slots__
00587 
00588     def resolve(self, makefile, variables, fd, setting):
00589         varname = self._arguments[0].resolvestr(makefile, variables, setting)
00590         
00591         flavor, source, value = variables.get(varname)
00592         if flavor is None:
00593             r = 'undefined'
00594         elif flavor == data.Variables.FLAVOR_RECURSIVE:
00595             r = 'recursive'
00596         elif flavor == data.Variables.FLAVOR_SIMPLE:
00597             r = 'simple'
00598         fd.write(r)
00599 
00600 class ShellFunction(Function):
00601     name = 'shell'
00602     minargs = 1
00603     maxargs = 1
00604 
00605     __slots__ = Function.__slots__
00606 
00607     def resolve(self, makefile, variables, fd, setting):
00608         #TODO: call this once up-front somewhere and save the result?
00609         shell, msys = util.checkmsyscompat()
00610         cline = self._arguments[0].resolvestr(makefile, variables, setting)
00611 
00612         log.debug("%s: running shell command '%s'" % (self.loc, cline))
00613         if msys:
00614             cline = [shell, "-c", cline]
00615         p = subprocess.Popen(cline, shell=not msys, stdout=subprocess.PIPE, cwd=makefile.workdir)
00616         stdout, stderr = p.communicate()
00617 
00618         stdout = stdout.replace('\r\n', '\n')
00619         if stdout.endswith('\n'):
00620             stdout = stdout[:-1]
00621         stdout = stdout.replace('\n', ' ')
00622 
00623         fd.write(stdout)
00624 
00625 class ErrorFunction(Function):
00626     name = 'error'
00627     minargs = 1
00628     maxargs = 1
00629 
00630     __slots__ = Function.__slots__
00631 
00632     def resolve(self, makefile, variables, fd, setting):
00633         v = self._arguments[0].resolvestr(makefile, variables, setting)
00634         raise data.DataError(v, self.loc)
00635 
00636 class WarningFunction(Function):
00637     name = 'warning'
00638     minargs = 1
00639     maxargs = 1
00640 
00641     __slots__ = Function.__slots__
00642 
00643     def resolve(self, makefile, variables, fd, setting):
00644         v = self._arguments[0].resolvestr(makefile, variables, setting)
00645         log.warning(v)
00646 
00647 class InfoFunction(Function):
00648     name = 'info'
00649     minargs = 1
00650     maxargs = 1
00651 
00652     __slots__ = Function.__slots__
00653 
00654     def resolve(self, makefile, variables, fd, setting):
00655         v = self._arguments[0].resolvestr(makefile, variables, setting)
00656         print v
00657 
00658 functionmap = {
00659     'subst': SubstFunction,
00660     'patsubst': PatSubstFunction,
00661     'strip': StripFunction,
00662     'findstring': FindstringFunction,
00663     'filter': FilterFunction,
00664     'filter-out': FilteroutFunction,
00665     'sort': SortFunction,
00666     'word': WordFunction,
00667     'wordlist': WordlistFunction,
00668     'words': WordsFunction,
00669     'firstword': FirstWordFunction,
00670     'lastword': LastWordFunction,
00671     'dir': DirFunction,
00672     'notdir': NotDirFunction,
00673     'suffix': SuffixFunction,
00674     'basename': BasenameFunction,
00675     'addsuffix': AddSuffixFunction,
00676     'addprefix': AddPrefixFunction,
00677     'join': JoinFunction,
00678     'wildcard': WildcardFunction,
00679     'realpath': RealpathFunction,
00680     'abspath': AbspathFunction,
00681     'if': IfFunction,
00682     'or': OrFunction,
00683     'and': AndFunction,
00684     'foreach': ForEachFunction,
00685     'call': CallFunction,
00686     'value': ValueFunction,
00687     'eval': EvalFunction,
00688     'origin': OriginFunction,
00689     'flavor': FlavorFunction,
00690     'shell': ShellFunction,
00691     'error': ErrorFunction,
00692     'warning': WarningFunction,
00693     'info': InfoFunction,
00694 }
00695 
00696 import data