| """ |
| Makefile functions. |
| """ |
| |
| import parser, util |
| import subprocess, os, logging, sys |
| from globrelative import glob |
| from cStringIO import StringIO |
| |
| log = logging.getLogger('pymake.data') |
| |
| def emit_expansions(descend, *expansions): |
| """Helper function to emit all expansions within an input set.""" |
| for expansion in expansions: |
| yield expansion |
| |
| if not descend or not isinstance(expansion, list): |
| continue |
| |
| for e, is_func in expansion: |
| if is_func: |
| for exp in e.expansions(True): |
| yield exp |
| else: |
| yield e |
| |
| class Function(object): |
| """ |
| An object that represents a function call. This class is always subclassed |
| with the following methods and attributes: |
| |
| minargs = minimum # of arguments |
| maxargs = maximum # of arguments (0 means unlimited) |
| |
| def resolve(self, makefile, variables, fd, setting) |
| Calls the function |
| calls fd.write() with strings |
| """ |
| |
| __slots__ = ('_arguments', 'loc') |
| |
| def __init__(self, loc): |
| self._arguments = [] |
| self.loc = loc |
| assert self.minargs > 0 |
| |
| def __getitem__(self, key): |
| return self._arguments[key] |
| |
| def setup(self): |
| argc = len(self._arguments) |
| |
| if argc < self.minargs: |
| raise data.DataError("Not enough arguments to function %s, requires %s" % (self.name, self.minargs), self.loc) |
| |
| assert self.maxargs == 0 or argc <= self.maxargs, "Parser screwed up, gave us too many args" |
| |
| def append(self, arg): |
| assert isinstance(arg, (data.Expansion, data.StringExpansion)) |
| self._arguments.append(arg) |
| |
| def to_source(self): |
| """Convert the function back to make file "source" code.""" |
| if not hasattr(self, 'name'): |
| raise Exception("%s must implement to_source()." % self.__class__) |
| |
| # The default implementation simply prints the function name and all |
| # the arguments joined by a comma. |
| # According to the GNU make manual Section 8.1, whitespace around |
| # arguments is *not* part of the argument's value. So, we trim excess |
| # white space so we have consistent behavior. |
| args = [] |
| curly = False |
| for i, arg in enumerate(self._arguments): |
| arg = arg.to_source() |
| |
| if i == 0: |
| arg = arg.lstrip() |
| |
| # Are balanced parens even OK? |
| if arg.count('(') != arg.count(')'): |
| curly = True |
| |
| args.append(arg) |
| |
| if curly: |
| return '${%s %s}' % (self.name, ','.join(args)) |
| |
| return '$(%s %s)' % (self.name, ','.join(args)) |
| |
| def expansions(self, descend=False): |
| """Obtain all expansions contained within this function. |
| |
| By default, only expansions directly part of this function are |
| returned. If descend is True, we will descend into child expansions and |
| return all of the composite parts. |
| |
| This is a generator for pymake.data.BaseExpansion instances. |
| """ |
| # Our default implementation simply returns arguments. More advanced |
| # functions like variable references may need their own implementation. |
| return emit_expansions(descend, *self._arguments) |
| |
| @property |
| def is_filesystem_dependent(self): |
| """Exposes whether this function depends on the filesystem for results. |
| |
| If True, the function touches the filesystem as part of evaluation. |
| |
| This only tests whether the function itself uses the filesystem. If |
| this function has arguments that are functions that touch the |
| filesystem, this will return False. |
| """ |
| return False |
| |
| def __len__(self): |
| return len(self._arguments) |
| |
| def __repr__(self): |
| return "%s<%s>(%r)" % ( |
| self.__class__.__name__, self.loc, |
| ','.join([repr(a) for a in self._arguments]), |
| ) |
| |
| def __eq__(self, other): |
| if not hasattr(self, 'name'): |
| raise Exception("%s must implement __eq__." % self.__class__) |
| |
| if type(self) != type(other): |
| return False |
| |
| if self.name != other.name: |
| return False |
| |
| if len(self._arguments) != len(other._arguments): |
| return False |
| |
| for i in xrange(len(self._arguments)): |
| # According to the GNU make manual Section 8.1, whitespace around |
| # arguments is *not* part of the argument's value. So, we do a |
| # whitespace-agnostic comparison. |
| if i == 0: |
| a = self._arguments[i] |
| a.lstrip() |
| |
| b = other._arguments[i] |
| b.lstrip() |
| |
| if a != b: |
| return False |
| |
| continue |
| |
| if self._arguments[i] != other._arguments[i]: |
| return False |
| |
| return True |
| |
| def __ne__(self, other): |
| return not self.__eq__(other) |
| |
| class VariableRef(Function): |
| AUTOMATIC_VARIABLES = set(['@', '%', '<', '?', '^', '+', '|', '*']) |
| |
| __slots__ = ('vname', 'loc') |
| |
| def __init__(self, loc, vname): |
| self.loc = loc |
| assert isinstance(vname, (data.Expansion, data.StringExpansion)) |
| self.vname = vname |
| |
| def setup(self): |
| assert False, "Shouldn't get here" |
| |
| def resolve(self, makefile, variables, fd, setting): |
| vname = self.vname.resolvestr(makefile, variables, setting) |
| if vname in setting: |
| raise data.DataError("Setting variable '%s' recursively references itself." % (vname,), self.loc) |
| |
| flavor, source, value = variables.get(vname) |
| if value is None: |
| log.debug("%s: variable '%s' was not set" % (self.loc, vname)) |
| return |
| |
| value.resolve(makefile, variables, fd, setting + [vname]) |
| |
| def to_source(self): |
| if isinstance(self.vname, data.StringExpansion): |
| if self.vname.s in self.AUTOMATIC_VARIABLES: |
| return '$%s' % self.vname.s |
| |
| return '$(%s)' % self.vname.s |
| |
| return '$(%s)' % self.vname.to_source() |
| |
| def expansions(self, descend=False): |
| return emit_expansions(descend, self.vname) |
| |
| def __repr__(self): |
| return "VariableRef<%s>(%r)" % (self.loc, self.vname) |
| |
| def __eq__(self, other): |
| if not isinstance(other, VariableRef): |
| return False |
| |
| return self.vname == other.vname |
| |
| class SubstitutionRef(Function): |
| """$(VARNAME:.c=.o) and $(VARNAME:%.c=%.o)""" |
| |
| __slots__ = ('loc', 'vname', 'substfrom', 'substto') |
| |
| def __init__(self, loc, varname, substfrom, substto): |
| self.loc = loc |
| self.vname = varname |
| self.substfrom = substfrom |
| self.substto = substto |
| |
| def setup(self): |
| assert False, "Shouldn't get here" |
| |
| def resolve(self, makefile, variables, fd, setting): |
| vname = self.vname.resolvestr(makefile, variables, setting) |
| if vname in setting: |
| raise data.DataError("Setting variable '%s' recursively references itself." % (vname,), self.loc) |
| |
| substfrom = self.substfrom.resolvestr(makefile, variables, setting) |
| substto = self.substto.resolvestr(makefile, variables, setting) |
| |
| flavor, source, value = variables.get(vname) |
| if value is None: |
| log.debug("%s: variable '%s' was not set" % (self.loc, vname)) |
| return |
| |
| f = data.Pattern(substfrom) |
| if not f.ispattern(): |
| f = data.Pattern('%' + substfrom) |
| substto = '%' + substto |
| |
| fd.write(' '.join([f.subst(substto, word, False) |
| for word in value.resolvesplit(makefile, variables, setting + [vname])])) |
| |
| def to_source(self): |
| return '$(%s:%s=%s)' % ( |
| self.vname.to_source(), |
| self.substfrom.to_source(), |
| self.substto.to_source()) |
| |
| def expansions(self, descend=False): |
| return emit_expansions(descend, self.vname, self.substfrom, |
| self.substto) |
| |
| def __repr__(self): |
| return "SubstitutionRef<%s>(%r:%r=%r)" % ( |
| self.loc, self.vname, self.substfrom, self.substto,) |
| |
| def __eq__(self, other): |
| if not isinstance(other, SubstitutionRef): |
| return False |
| |
| return self.vname == other.vname and self.substfrom == other.substfrom \ |
| and self.substto == other.substto |
| |
| class SubstFunction(Function): |
| name = 'subst' |
| minargs = 3 |
| maxargs = 3 |
| |
| __slots__ = Function.__slots__ |
| |
| def resolve(self, makefile, variables, fd, setting): |
| s = self._arguments[0].resolvestr(makefile, variables, setting) |
| r = self._arguments[1].resolvestr(makefile, variables, setting) |
| d = self._arguments[2].resolvestr(makefile, variables, setting) |
| fd.write(d.replace(s, r)) |
| |
| class PatSubstFunction(Function): |
| name = 'patsubst' |
| minargs = 3 |
| maxargs = 3 |
| |
| __slots__ = Function.__slots__ |
| |
| def resolve(self, makefile, variables, fd, setting): |
| s = self._arguments[0].resolvestr(makefile, variables, setting) |
| r = self._arguments[1].resolvestr(makefile, variables, setting) |
| |
| p = data.Pattern(s) |
| fd.write(' '.join([p.subst(r, word, False) |
| for word in self._arguments[2].resolvesplit(makefile, variables, setting)])) |
| |
| class StripFunction(Function): |
| name = 'strip' |
| minargs = 1 |
| maxargs = 1 |
| |
| __slots__ = Function.__slots__ |
| |
| def resolve(self, makefile, variables, fd, setting): |
| util.joiniter(fd, self._arguments[0].resolvesplit(makefile, variables, setting)) |
| |
| class FindstringFunction(Function): |
| name = 'findstring' |
| minargs = 2 |
| maxargs = 2 |
| |
| __slots__ = Function.__slots__ |
| |
| def resolve(self, makefile, variables, fd, setting): |
| s = self._arguments[0].resolvestr(makefile, variables, setting) |
| r = self._arguments[1].resolvestr(makefile, variables, setting) |
| if r.find(s) == -1: |
| return |
| fd.write(s) |
| |
| class FilterFunction(Function): |
| name = 'filter' |
| minargs = 2 |
| maxargs = 2 |
| |
| __slots__ = Function.__slots__ |
| |
| def resolve(self, makefile, variables, fd, setting): |
| plist = [data.Pattern(p) |
| for p in self._arguments[0].resolvesplit(makefile, variables, setting)] |
| |
| fd.write(' '.join([w for w in self._arguments[1].resolvesplit(makefile, variables, setting) |
| if util.any((p.match(w) for p in plist))])) |
| |
| class FilteroutFunction(Function): |
| name = 'filter-out' |
| minargs = 2 |
| maxargs = 2 |
| |
| __slots__ = Function.__slots__ |
| |
| def resolve(self, makefile, variables, fd, setting): |
| plist = [data.Pattern(p) |
| for p in self._arguments[0].resolvesplit(makefile, variables, setting)] |
| |
| fd.write(' '.join([w for w in self._arguments[1].resolvesplit(makefile, variables, setting) |
| if not util.any((p.match(w) for p in plist))])) |
| |
| class SortFunction(Function): |
| name = 'sort' |
| minargs = 1 |
| maxargs = 1 |
| |
| __slots__ = Function.__slots__ |
| |
| def resolve(self, makefile, variables, fd, setting): |
| d = set(self._arguments[0].resolvesplit(makefile, variables, setting)) |
| util.joiniter(fd, sorted(d)) |
| |
| class WordFunction(Function): |
| name = 'word' |
| minargs = 2 |
| maxargs = 2 |
| |
| __slots__ = Function.__slots__ |
| |
| def resolve(self, makefile, variables, fd, setting): |
| n = self._arguments[0].resolvestr(makefile, variables, setting) |
| # TODO: provide better error if this doesn't convert |
| n = int(n) |
| words = list(self._arguments[1].resolvesplit(makefile, variables, setting)) |
| if n < 1 or n > len(words): |
| return |
| fd.write(words[n - 1]) |
| |
| class WordlistFunction(Function): |
| name = 'wordlist' |
| minargs = 3 |
| maxargs = 3 |
| |
| __slots__ = Function.__slots__ |
| |
| def resolve(self, makefile, variables, fd, setting): |
| nfrom = self._arguments[0].resolvestr(makefile, variables, setting) |
| nto = self._arguments[1].resolvestr(makefile, variables, setting) |
| # TODO: provide better errors if this doesn't convert |
| nfrom = int(nfrom) |
| nto = int(nto) |
| |
| words = list(self._arguments[2].resolvesplit(makefile, variables, setting)) |
| |
| if nfrom < 1: |
| nfrom = 1 |
| if nto < 1: |
| nto = 1 |
| |
| util.joiniter(fd, words[nfrom - 1:nto]) |
| |
| class WordsFunction(Function): |
| name = 'words' |
| minargs = 1 |
| maxargs = 1 |
| |
| __slots__ = Function.__slots__ |
| |
| def resolve(self, makefile, variables, fd, setting): |
| fd.write(str(len(self._arguments[0].resolvesplit(makefile, variables, setting)))) |
| |
| class FirstWordFunction(Function): |
| name = 'firstword' |
| minargs = 1 |
| maxargs = 1 |
| |
| __slots__ = Function.__slots__ |
| |
| def resolve(self, makefile, variables, fd, setting): |
| l = self._arguments[0].resolvesplit(makefile, variables, setting) |
| if len(l): |
| fd.write(l[0]) |
| |
| class LastWordFunction(Function): |
| name = 'lastword' |
| minargs = 1 |
| maxargs = 1 |
| |
| __slots__ = Function.__slots__ |
| |
| def resolve(self, makefile, variables, fd, setting): |
| l = self._arguments[0].resolvesplit(makefile, variables, setting) |
| if len(l): |
| fd.write(l[-1]) |
| |
| def pathsplit(path, default='./'): |
| """ |
| Splits a path into dirpart, filepart on the last slash. If there is no slash, dirpart |
| is ./ |
| """ |
| dir, slash, file = util.strrpartition(path, '/') |
| if dir == '': |
| return default, file |
| |
| return dir + slash, file |
| |
| class DirFunction(Function): |
| name = 'dir' |
| minargs = 1 |
| maxargs = 1 |
| |
| def resolve(self, makefile, variables, fd, setting): |
| fd.write(' '.join([pathsplit(path)[0] |
| for path in self._arguments[0].resolvesplit(makefile, variables, setting)])) |
| |
| class NotDirFunction(Function): |
| name = 'notdir' |
| minargs = 1 |
| maxargs = 1 |
| |
| __slots__ = Function.__slots__ |
| |
| def resolve(self, makefile, variables, fd, setting): |
| fd.write(' '.join([pathsplit(path)[1] |
| for path in self._arguments[0].resolvesplit(makefile, variables, setting)])) |
| |
| class SuffixFunction(Function): |
| name = 'suffix' |
| minargs = 1 |
| maxargs = 1 |
| |
| __slots__ = Function.__slots__ |
| |
| @staticmethod |
| def suffixes(words): |
| for w in words: |
| dir, file = pathsplit(w) |
| base, dot, suffix = util.strrpartition(file, '.') |
| if base != '': |
| yield dot + suffix |
| |
| def resolve(self, makefile, variables, fd, setting): |
| util.joiniter(fd, self.suffixes(self._arguments[0].resolvesplit(makefile, variables, setting))) |
| |
| class BasenameFunction(Function): |
| name = 'basename' |
| minargs = 1 |
| maxargs = 1 |
| |
| __slots__ = Function.__slots__ |
| |
| @staticmethod |
| def basenames(words): |
| for w in words: |
| dir, file = pathsplit(w, '') |
| base, dot, suffix = util.strrpartition(file, '.') |
| if dot == '': |
| base = suffix |
| |
| yield dir + base |
| |
| def resolve(self, makefile, variables, fd, setting): |
| util.joiniter(fd, self.basenames(self._arguments[0].resolvesplit(makefile, variables, setting))) |
| |
| class AddSuffixFunction(Function): |
| name = 'addsuffix' |
| minargs = 2 |
| maxargs = 2 |
| |
| __slots__ = Function.__slots__ |
| |
| def resolve(self, makefile, variables, fd, setting): |
| suffix = self._arguments[0].resolvestr(makefile, variables, setting) |
| |
| fd.write(' '.join([w + suffix for w in self._arguments[1].resolvesplit(makefile, variables, setting)])) |
| |
| class AddPrefixFunction(Function): |
| name = 'addprefix' |
| minargs = 2 |
| maxargs = 2 |
| |
| def resolve(self, makefile, variables, fd, setting): |
| prefix = self._arguments[0].resolvestr(makefile, variables, setting) |
| |
| fd.write(' '.join([prefix + w for w in self._arguments[1].resolvesplit(makefile, variables, setting)])) |
| |
| class JoinFunction(Function): |
| name = 'join' |
| minargs = 2 |
| maxargs = 2 |
| |
| __slots__ = Function.__slots__ |
| |
| @staticmethod |
| def iterjoin(l1, l2): |
| for i in xrange(0, max(len(l1), len(l2))): |
| i1 = i < len(l1) and l1[i] or '' |
| i2 = i < len(l2) and l2[i] or '' |
| yield i1 + i2 |
| |
| def resolve(self, makefile, variables, fd, setting): |
| list1 = list(self._arguments[0].resolvesplit(makefile, variables, setting)) |
| list2 = list(self._arguments[1].resolvesplit(makefile, variables, setting)) |
| |
| util.joiniter(fd, self.iterjoin(list1, list2)) |
| |
| class WildcardFunction(Function): |
| name = 'wildcard' |
| minargs = 1 |
| maxargs = 1 |
| |
| __slots__ = Function.__slots__ |
| |
| def resolve(self, makefile, variables, fd, setting): |
| patterns = self._arguments[0].resolvesplit(makefile, variables, setting) |
| |
| fd.write(' '.join([x.replace('\\','/') |
| for p in patterns |
| for x in glob(makefile.workdir, p)])) |
| |
| @property |
| def is_filesystem_dependent(self): |
| return True |
| |
| class RealpathFunction(Function): |
| name = 'realpath' |
| minargs = 1 |
| maxargs = 1 |
| |
| def resolve(self, makefile, variables, fd, setting): |
| fd.write(' '.join([os.path.realpath(os.path.join(makefile.workdir, path)).replace('\\', '/') |
| for path in self._arguments[0].resolvesplit(makefile, variables, setting)])) |
| |
| def is_filesystem_dependent(self): |
| return True |
| |
| class AbspathFunction(Function): |
| name = 'abspath' |
| minargs = 1 |
| maxargs = 1 |
| |
| __slots__ = Function.__slots__ |
| |
| def resolve(self, makefile, variables, fd, setting): |
| assert os.path.isabs(makefile.workdir) |
| fd.write(' '.join([util.normaljoin(makefile.workdir, path).replace('\\', '/') |
| for path in self._arguments[0].resolvesplit(makefile, variables, setting)])) |
| |
| class IfFunction(Function): |
| name = 'if' |
| minargs = 1 |
| maxargs = 3 |
| |
| __slots__ = Function.__slots__ |
| |
| def setup(self): |
| Function.setup(self) |
| self._arguments[0].lstrip() |
| self._arguments[0].rstrip() |
| |
| def resolve(self, makefile, variables, fd, setting): |
| condition = self._arguments[0].resolvestr(makefile, variables, setting) |
| |
| if len(condition): |
| self._arguments[1].resolve(makefile, variables, fd, setting) |
| elif len(self._arguments) > 2: |
| return self._arguments[2].resolve(makefile, variables, fd, setting) |
| |
| class OrFunction(Function): |
| name = 'or' |
| minargs = 1 |
| maxargs = 0 |
| |
| __slots__ = Function.__slots__ |
| |
| def resolve(self, makefile, variables, fd, setting): |
| for arg in self._arguments: |
| r = arg.resolvestr(makefile, variables, setting) |
| if r != '': |
| fd.write(r) |
| return |
| |
| class AndFunction(Function): |
| name = 'and' |
| minargs = 1 |
| maxargs = 0 |
| |
| __slots__ = Function.__slots__ |
| |
| def resolve(self, makefile, variables, fd, setting): |
| r = '' |
| |
| for arg in self._arguments: |
| r = arg.resolvestr(makefile, variables, setting) |
| if r == '': |
| return |
| |
| fd.write(r) |
| |
| class ForEachFunction(Function): |
| name = 'foreach' |
| minargs = 3 |
| maxargs = 3 |
| |
| __slots__ = Function.__slots__ |
| |
| def resolve(self, makefile, variables, fd, setting): |
| vname = self._arguments[0].resolvestr(makefile, variables, setting) |
| e = self._arguments[2] |
| |
| v = data.Variables(parent=variables) |
| firstword = True |
| |
| for w in self._arguments[1].resolvesplit(makefile, variables, setting): |
| if firstword: |
| firstword = False |
| else: |
| fd.write(' ') |
| |
| # The $(origin) of the local variable must be "automatic" to |
| # conform with GNU make. However, automatic variables have low |
| # priority. So, we must force its assignment to occur. |
| v.set(vname, data.Variables.FLAVOR_SIMPLE, |
| data.Variables.SOURCE_AUTOMATIC, w, force=True) |
| e.resolve(makefile, v, fd, setting) |
| |
| class CallFunction(Function): |
| name = 'call' |
| minargs = 1 |
| maxargs = 0 |
| |
| __slots__ = Function.__slots__ |
| |
| def resolve(self, makefile, variables, fd, setting): |
| vname = self._arguments[0].resolvestr(makefile, variables, setting) |
| if vname in setting: |
| raise data.DataError("Recursively setting variable '%s'" % (vname,)) |
| |
| v = data.Variables(parent=variables) |
| v.set('0', data.Variables.FLAVOR_SIMPLE, data.Variables.SOURCE_AUTOMATIC, vname) |
| for i in xrange(1, len(self._arguments)): |
| param = self._arguments[i].resolvestr(makefile, variables, setting) |
| v.set(str(i), data.Variables.FLAVOR_SIMPLE, data.Variables.SOURCE_AUTOMATIC, param) |
| |
| flavor, source, e = variables.get(vname) |
| |
| if e is None: |
| return |
| |
| if flavor == data.Variables.FLAVOR_SIMPLE: |
| log.warning("%s: calling variable '%s' which is simply-expanded" % (self.loc, vname)) |
| |
| # but we'll do it anyway |
| e.resolve(makefile, v, fd, setting + [vname]) |
| |
| class ValueFunction(Function): |
| name = 'value' |
| minargs = 1 |
| maxargs = 1 |
| |
| __slots__ = Function.__slots__ |
| |
| def resolve(self, makefile, variables, fd, setting): |
| varname = self._arguments[0].resolvestr(makefile, variables, setting) |
| |
| flavor, source, value = variables.get(varname, expand=False) |
| if value is not None: |
| fd.write(value) |
| |
| class EvalFunction(Function): |
| name = 'eval' |
| minargs = 1 |
| maxargs = 1 |
| |
| def resolve(self, makefile, variables, fd, setting): |
| if makefile.parsingfinished: |
| # GNU make allows variables to be set by recursive expansion during |
| # command execution. This seems really dumb to me, so I don't! |
| raise data.DataError("$(eval) not allowed via recursive expansion after parsing is finished", self.loc) |
| |
| stmts = parser.parsestring(self._arguments[0].resolvestr(makefile, variables, setting), |
| 'evaluation from %s' % self.loc) |
| stmts.execute(makefile) |
| |
| class OriginFunction(Function): |
| name = 'origin' |
| minargs = 1 |
| maxargs = 1 |
| |
| __slots__ = Function.__slots__ |
| |
| def resolve(self, makefile, variables, fd, setting): |
| vname = self._arguments[0].resolvestr(makefile, variables, setting) |
| |
| flavor, source, value = variables.get(vname) |
| if source is None: |
| r = 'undefined' |
| elif source == data.Variables.SOURCE_OVERRIDE: |
| r = 'override' |
| |
| elif source == data.Variables.SOURCE_MAKEFILE: |
| r = 'file' |
| elif source == data.Variables.SOURCE_ENVIRONMENT: |
| r = 'environment' |
| elif source == data.Variables.SOURCE_COMMANDLINE: |
| r = 'command line' |
| elif source == data.Variables.SOURCE_AUTOMATIC: |
| r = 'automatic' |
| elif source == data.Variables.SOURCE_IMPLICIT: |
| r = 'default' |
| |
| fd.write(r) |
| |
| class FlavorFunction(Function): |
| name = 'flavor' |
| minargs = 1 |
| maxargs = 1 |
| |
| __slots__ = Function.__slots__ |
| |
| def resolve(self, makefile, variables, fd, setting): |
| varname = self._arguments[0].resolvestr(makefile, variables, setting) |
| |
| flavor, source, value = variables.get(varname) |
| if flavor is None: |
| r = 'undefined' |
| elif flavor == data.Variables.FLAVOR_RECURSIVE: |
| r = 'recursive' |
| elif flavor == data.Variables.FLAVOR_SIMPLE: |
| r = 'simple' |
| fd.write(r) |
| |
| class ShellFunction(Function): |
| name = 'shell' |
| minargs = 1 |
| maxargs = 1 |
| |
| __slots__ = Function.__slots__ |
| |
| def resolve(self, makefile, variables, fd, setting): |
| from process import prepare_command |
| cline = self._arguments[0].resolvestr(makefile, variables, setting) |
| executable, cline = prepare_command(cline, makefile.workdir, self.loc) |
| |
| # subprocess.Popen doesn't use the PATH set in the env argument for |
| # finding the executable on some platforms (but strangely it does on |
| # others!), so set os.environ['PATH'] explicitly. |
| oldpath = os.environ['PATH'] |
| if makefile.env is not None and 'PATH' in makefile.env: |
| os.environ['PATH'] = makefile.env['PATH'] |
| |
| log.debug("%s: running command '%s'" % (self.loc, ' '.join(cline))) |
| try: |
| p = subprocess.Popen(cline, executable=executable, env=makefile.env, shell=False, |
| stdout=subprocess.PIPE, cwd=makefile.workdir) |
| except OSError, e: |
| print >>sys.stderr, "Error executing command %s" % cline[0], e |
| return |
| finally: |
| os.environ['PATH'] = oldpath |
| |
| stdout, stderr = p.communicate() |
| stdout = stdout.replace('\r\n', '\n') |
| if stdout.endswith('\n'): |
| stdout = stdout[:-1] |
| stdout = stdout.replace('\n', ' ') |
| |
| fd.write(stdout) |
| |
| class ErrorFunction(Function): |
| name = 'error' |
| minargs = 1 |
| maxargs = 1 |
| |
| __slots__ = Function.__slots__ |
| |
| def resolve(self, makefile, variables, fd, setting): |
| v = self._arguments[0].resolvestr(makefile, variables, setting) |
| raise data.DataError(v, self.loc) |
| |
| class WarningFunction(Function): |
| name = 'warning' |
| minargs = 1 |
| maxargs = 1 |
| |
| __slots__ = Function.__slots__ |
| |
| def resolve(self, makefile, variables, fd, setting): |
| v = self._arguments[0].resolvestr(makefile, variables, setting) |
| log.warning(v) |
| |
| class InfoFunction(Function): |
| name = 'info' |
| minargs = 1 |
| maxargs = 1 |
| |
| __slots__ = Function.__slots__ |
| |
| def resolve(self, makefile, variables, fd, setting): |
| v = self._arguments[0].resolvestr(makefile, variables, setting) |
| print v |
| |
| functionmap = { |
| 'subst': SubstFunction, |
| 'patsubst': PatSubstFunction, |
| 'strip': StripFunction, |
| 'findstring': FindstringFunction, |
| 'filter': FilterFunction, |
| 'filter-out': FilteroutFunction, |
| 'sort': SortFunction, |
| 'word': WordFunction, |
| 'wordlist': WordlistFunction, |
| 'words': WordsFunction, |
| 'firstword': FirstWordFunction, |
| 'lastword': LastWordFunction, |
| 'dir': DirFunction, |
| 'notdir': NotDirFunction, |
| 'suffix': SuffixFunction, |
| 'basename': BasenameFunction, |
| 'addsuffix': AddSuffixFunction, |
| 'addprefix': AddPrefixFunction, |
| 'join': JoinFunction, |
| 'wildcard': WildcardFunction, |
| 'realpath': RealpathFunction, |
| 'abspath': AbspathFunction, |
| 'if': IfFunction, |
| 'or': OrFunction, |
| 'and': AndFunction, |
| 'foreach': ForEachFunction, |
| 'call': CallFunction, |
| 'value': ValueFunction, |
| 'eval': EvalFunction, |
| 'origin': OriginFunction, |
| 'flavor': FlavorFunction, |
| 'shell': ShellFunction, |
| 'error': ErrorFunction, |
| 'warning': WarningFunction, |
| 'info': InfoFunction, |
| } |
| |
| import data |