def gvar_names(name_filter = lambda x : True, categories = None): """Return list of GVar names :Parameters: name_filter : callable callable object (e.g. lambda) of type ``name_filter(name) -> boolean`` used to filter-out unwanted variables; only these variables are processed, for which name_filter returns ``True`` categories : string return only variable names for given categories, it may be ``'programs'`` for variables representing programs (``CC``, ``CXX`` and such), ``'flags'`` for variables representing program flags (``CFLAGS``, ``CCFLAGS`` etc.) or ``None`` (default) to not filter by categories. :Returns: the list of standard GNU directory variable names """ if is_Sequence(name_filter): seq = name_filter name_filter = lambda x : x in seq if categories and not is_Sequence(categories): categories = [ categories ] if categories: lst = [] if 'programs' in categories: lst.extend(__prog_var_tuples) if 'flags' in categories: lst.extend(__flag_var_tuples) else: lst = __prog_var_tuples + __flag_var_tuples return filter(name_filter, zip(*lst)[0])
def _action(target, source, env): # prepare the line separator linesep = env['LINESEPARATOR'] if linesep is None: linesep = LINESEP # os.linesep elif is_String(linesep): pass elif isinstance(linesep, Value): linesep = linesep.get_text_contents() else: raise SCons.Errors.UserError( 'unexpected type/class for LINESEPARATOR: %s' % repr(linesep), None) if 'b' in TEXTFILE_FILE_WRITE_MODE: linesep = to_bytes(linesep) # create a dictionary to use for the substitutions if 'SUBST_DICT' not in env: subs = None # no substitutions else: subst_dict = env['SUBST_DICT'] if is_Dict(subst_dict): subst_dict = list(subst_dict.items()) elif is_Sequence(subst_dict): pass else: raise SCons.Errors.UserError('SUBST_DICT must be dict or sequence') subs = [] for (k, value) in subst_dict: if callable(value): value = value() if is_String(value): value = env.subst(value) else: value = str(value) subs.append((k, value)) # write the file try: if SCons.Util.PY3: target_file = open(target[0].get_path(), TEXTFILE_FILE_WRITE_MODE, newline='', encoding="utf8") else: target_file = open(target[0].get_path(), TEXTFILE_FILE_WRITE_MODE) except (OSError, IOError): raise SCons.Errors.UserError("Can't write target file %s" % target[0]) # separate lines by 'linesep' only if linesep is not empty lsep = None for line in source: if lsep: target_file.write(lsep) target_file.write(_do_subst(line, subs)) lsep = linesep target_file.close()
def sub_match(match, val=val, matchlist=matchlist): a = match.group(1) if a in matchlist: a = val if is_Sequence(a): return ' '.join(map(str, a)) else: return str(a)
def _convert_list_R(newlist, sources): for elem in sources: if is_Sequence(elem): _convert_list_R(newlist, elem) elif isinstance(elem, Node): newlist.append(elem) else: newlist.append(Value(elem))
def scons_subst_once(strSubst, env, key): """Perform single (non-recursive) substitution of a single construction variable keyword. This is used when setting a variable when copying or overriding values in an Environment. We want to capture (expand) the old value before we override it, so people can do things like: env2 = env.Clone(CCFLAGS = '$CCFLAGS -g') We do this with some straightforward, brute-force code here... """ if isinstance(strSubst, str) and strSubst.find('$') < 0: return strSubst matchlist = ['$' + key, '${' + key + '}'] val = env.get(key, '') def sub_match(match, val=val, matchlist=matchlist): a = match.group(1) if a in matchlist: a = val if is_Sequence(a): return ' '.join(map(str, a)) else: return str(a) if is_Sequence(strSubst): result = [] for arg in strSubst: if is_String(arg): if arg in matchlist: arg = val if is_Sequence(arg): result.extend(arg) else: result.append(arg) else: result.append(_dollar_exps.sub(sub_match, arg)) else: result.append(arg) return result elif is_String(strSubst): return _dollar_exps.sub(sub_match, strSubst) else: return strSubst
def scons_subst_once(strSubst, env, key): """Perform single (non-recursive) substitution of a single construction variable keyword. This is used when setting a variable when copying or overriding values in an Environment. We want to capture (expand) the old value before we override it, so people can do things like: env2 = env.Clone(CCFLAGS = '$CCFLAGS -g') We do this with some straightforward, brute-force code here... """ if type(strSubst) == types.StringType and string.find(strSubst, "$") < 0: return strSubst matchlist = ["$" + key, "${" + key + "}"] val = env.get(key, "") def sub_match(match, val=val, matchlist=matchlist): a = match.group(1) if a in matchlist: a = val if is_Sequence(a): return string.join(map(str, a)) else: return str(a) if is_Sequence(strSubst): result = [] for arg in strSubst: if is_String(arg): if arg in matchlist: arg = val if is_Sequence(arg): result.extend(arg) else: result.append(arg) else: result.append(_dollar_exps.sub(sub_match, arg)) else: result.append(arg) return result elif is_String(strSubst): return _dollar_exps.sub(sub_match, strSubst) else: return strSubst
def _action(target, source, env): # prepare the line separator linesep = env['LINESEPARATOR'] if linesep is None: linesep = LINESEP # os.linesep elif is_String(linesep): pass elif isinstance(linesep, Value): linesep = linesep.get_text_contents() else: raise SCons.Errors.UserError('unexpected type/class for LINESEPARATOR: %s' % repr(linesep), None) if 'b' in TEXTFILE_FILE_WRITE_MODE: linesep = to_bytes(linesep) # create a dictionary to use for the substitutions if 'SUBST_DICT' not in env: subs = None # no substitutions else: subst_dict = env['SUBST_DICT'] if is_Dict(subst_dict): subst_dict = list(subst_dict.items()) elif is_Sequence(subst_dict): pass else: raise SCons.Errors.UserError('SUBST_DICT must be dict or sequence') subs = [] for (k, value) in subst_dict: if callable(value): value = value() if is_String(value): value = env.subst(value) else: value = str(value) subs.append((k, value)) # write the file try: if SCons.Util.PY3: target_file = open(target[0].get_path(), TEXTFILE_FILE_WRITE_MODE, newline='') else: target_file = open(target[0].get_path(), TEXTFILE_FILE_WRITE_MODE) except (OSError, IOError): raise SCons.Errors.UserError("Can't write target file %s" % target[0]) # separate lines by 'linesep' only if linesep is not empty lsep = None for line in source: if lsep: target_file.write(lsep) target_file.write(_do_subst(line, subs)) lsep = linesep target_file.close()
def _gen_nodelist(self): mylist = self.list if mylist is None: mylist = [] elif not is_Sequence(mylist): mylist = [mylist] # The map(self.func) call is what actually turns # a list into appropriate proxies. self.nodelist = SCons.Util.NodeList(list(map(self.func, mylist))) self._create_nodelist = self._return_nodelist return self.nodelist
def declare_gvars(defaults = {}, name_filter=lambda x : True, env_key_transform=default_env_key_transform, var_key_transform=default_var_key_transform): """Return the variables representing particular programs as ``GVar`` variable declarations `_GVarDecls` (see `SConsGnu.GVars`). :Parameters: defaults : dict User-specified default values for the GVars being declared. You'll usually put your SCons Environment object env here. name_filter : callable callable object (e.g. lambda) of type ``name_filter(name) -> boolean`` used to filter-out unwanted variables; only these variables are processed, for which name_filter returns ``True`` env_key_transform : callable function or lambda used to transform canonical ``GVar`` names to keys used for corresponding construction variables in a SCons environment (default: `default_env_key_transform`) var_key_transform : callable function of lambda used to trasform canonical ``GVar`` names to keys used for corresponding SCons command-line variables ``variable=value`` (default: `default_var_key_transform`) :Returns: a dictionary-like object of type `SConsGnu.GVar._GVarDecls` """ from SCons.Variables.PathVariable import PathVariable from SConsGnu.GVars import GVarDeclsU, _undef def _prog_callback(name, desc, default = _undef): try: default = defaults[name] except KeyError: pass decl = { 'env_key' : env_key_transform(name), 'var_key' : var_key_transform(name), 'default' : default, 'help' : desc } return name, decl def _flag_callback(name, desc, default = _undef): try: default = defaults[name] except KeyError: pass decl = { 'env_key' : env_key_transform(name), 'var_key' : var_key_transform(name), 'default' : default, 'converter': _flag_converter, 'help' : desc } return name, decl if is_Sequence(name_filter): seq = name_filter name_filter = lambda x : x in seq return GVarDeclsU( __map_prog_var_tuples(_prog_callback, name_filter) + __map_flag_var_tuples(_flag_callback, name_filter) )
def _action(target, source, env): # prepare the line separator linesep = env['LINESEPARATOR'] if linesep is None: linesep = os.linesep elif is_String(linesep): pass elif isinstance(linesep, Value): linesep = linesep.get_text_contents() else: raise SCons.Errors.UserError( 'unexpected type/class for LINESEPARATOR: %s' % repr(linesep), None) # create a dictionary to use for the substitutions if 'SUBST_DICT' not in env: subs = None # no substitutions else: d = env['SUBST_DICT'] if is_Dict(d): d = list(d.items()) elif is_Sequence(d): pass else: raise SCons.Errors.UserError('SUBST_DICT must be dict or sequence') subs = [] for (k,v) in d: if callable(v): v = v() if is_String(v): v = env.subst(v) else: v = str(v) subs.append((k,v)) # write the file try: fd = open(target[0].get_path(), "wb") except (OSError, IOError, e): raise SCons.Errors.UserError("Can't write target file %s" % target[0]) # separate lines by 'linesep' only if linesep is not empty lsep = None for s in source: if lsep: fd.write(lsep) fd.write(_do_subst(s, subs)) lsep = linesep fd.close()
def _action(target, source, env): # prepare the line separator linesep = env['LINESEPARATOR'] if linesep is None: linesep = os.linesep elif is_String(linesep): pass elif isinstance(linesep, Value): linesep = linesep.get_text_contents() else: raise SCons.Errors.UserError( 'unexpected type/class for LINESEPARATOR: %s' % repr(linesep), None) # create a dictionary to use for the substitutions if 'SUBST_DICT' not in env: subs = None # no substitutions else: d = env['SUBST_DICT'] if is_Dict(d): d = list(d.items()) elif is_Sequence(d): pass else: raise SCons.Errors.UserError('SUBST_DICT must be dict or sequence') subs = [] for (k, v) in d: if callable(v): v = v() if is_String(v): v = env.subst(v) else: v = str(v) subs.append((k, v)) # write the file try: fd = open(target[0].get_path(), "wb") except (OSError, IOError) as e: raise SCons.Errors.UserError("Can't write target file %s" % target[0]) # separate lines by 'linesep' only if linesep is not empty lsep = None for s in source: if lsep: fd.write(lsep) fd.write(_do_subst(s, subs)) lsep = linesep fd.close()
def gvar_names(name_filter = lambda x : False): """Return list of standard GNU directory variable names By default this function returns empty list, you should provide custom **name_filter** to get any results. To retrieve all defined variables, use ``name_filter = lambda x : True``. :Parameters: name_filter : callable callable object (e.g. lambda) of type ``name_filter(name) -> boolean`` used to filter-out unwanted variables; only these variables are processed, for which name_filter returns ``True`` :Returns: the list of standard GNU directory variable names """ if is_Sequence(name_filter): seq = name_filter name_filter = lambda x : x in seq return filter(name_filter, zip(*__std_var_triples)[0])
def ProcessFlags(env, flags): # pylint: disable=too-many-branches if not flags: return if isinstance(flags, list): flags = " ".join(flags) parsed_flags = env.ParseFlags(str(flags)) for flag in parsed_flags.pop("CPPDEFINES"): if not is_Sequence(flag): env.Append(CPPDEFINES=flag) continue _key, _value = flag[:2] if '\"' in _value: _value = _value.replace('\"', '\\\"') elif _value.isdigit(): _value = int(_value) elif _value.replace(".", "", 1).isdigit(): _value = float(_value) env.Append(CPPDEFINES=(_key, _value)) env.Append(**parsed_flags) # fix relative CPPPATH & LIBPATH for k in ("CPPPATH", "LIBPATH"): for i, p in enumerate(env.get(k, [])): if isdir(p): env[k][i] = realpath(p) # fix relative path for "-include" for i, f in enumerate(env.get("CCFLAGS", [])): if isinstance(f, tuple) and f[0] == "-include": env['CCFLAGS'][i] = (f[0], env.File(realpath(f[1].get_path()))) # Cancel any previous definition of name, either built in or # provided with a -D option // Issue #191 undefines = [ u for u in env.get("CCFLAGS", []) if isinstance(u, basestring) and u.startswith("-U") ] if undefines: for undef in undefines: env['CCFLAGS'].remove(undef) env.Append(_CPPDEFFLAGS=" %s" % " ".join(undefines))
def _action(target, source, env): # prepare the line separator linesep = env["LINESEPARATOR"] if linesep is None: linesep = os.linesep elif is_String(linesep): pass elif isinstance(linesep, Value): linesep = linesep.get_text_contents() else: raise SCons.Errors.UserError("unexpected type/class for LINESEPARATOR: %s" % repr(linesep), None) # create a dictionary to use for the substitutions if "SUBST_DICT" not in env: subs = None # no substitutions else: d = env["SUBST_DICT"] if is_Dict(d): d = list(d.items()) elif is_Sequence(d): pass else: raise SCons.Errors.UserError("SUBST_DICT must be dict or sequence") subs = [] for (k, v) in d: if callable(v): v = v() if is_String(v): v = env.subst(v) else: v = str(v) subs.append((k, v)) # write the file try: fd = open(target[0].get_path(), "wb") except (OSError, IOError), e: raise SCons.Errors.UserError("Can't write target file %s" % target[0])
def expand(self, s, lvars): """Expand a single "token" as necessary, returning an appropriate string containing the expansion. This handles expanding different types of things (strings, lists, callables) appropriately. It calls the wrapper substitute() method to re-expand things as necessary, so that the results of expansions of side-by-side strings still get re-evaluated separately, not smushed together. """ if is_String(s): try: s0, s1 = s[:2] except (IndexError, ValueError): return s if s0 != '$': return s if s1 == '$': return '$' elif s1 in '()': return s else: key = s[1:] if key[0] == '{' or '.' in key: if key[0] == '{': key = key[1:-1] try: s = eval(key, self.gvars, lvars) except KeyboardInterrupt: raise except Exception as e: if e.__class__ in AllowableExceptions: return '' raise_exception(e, lvars['TARGETS'], s) else: if key in lvars: s = lvars[key] elif key in self.gvars: s = self.gvars[key] elif not NameError in AllowableExceptions: raise_exception(NameError(key), lvars['TARGETS'], s) else: return '' # Before re-expanding the result, handle # recursive expansion by copying the local # variable dictionary and overwriting a null # string for the value of the variable name # we just expanded. # # This could potentially be optimized by only # copying lvars when s contains more expansions, # but lvars is usually supposed to be pretty # small, and deeply nested variable expansions # are probably more the exception than the norm, # so it should be tolerable for now. lv = lvars.copy() var = key.split('.')[0] lv[var] = '' return self.substitute(s, lv) elif is_Sequence(s): def func(l, conv=self.conv, substitute=self.substitute, lvars=lvars): return conv(substitute(l, lvars)) return list(map(func, s)) elif callable(s): try: s = s(target=lvars['TARGETS'], source=lvars['SOURCES'], env=self.env, for_signature=(self.mode != SUBST_CMD)) except TypeError: # This probably indicates that it's a callable # object that doesn't match our calling arguments # (like an Action). if self.mode == SUBST_RAW: return s s = self.conv(s) return self.substitute(s, lvars) elif s is None: return '' else: return s
def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None): """Substitute construction variables in a string (or list or other object) and separate the arguments into a command list. The companion scons_subst() function (above) handles basic substitutions within strings, so see that function instead if that's what you're looking for. """ class ListSubber(collections.UserList): """A class to construct the results of a scons_subst_list() call. Like StringSubber, this class binds a specific construction environment, mode, target and source with two methods (substitute() and expand()) that handle the expansion. In addition, however, this class is used to track the state of the result(s) we're gathering so we can do the appropriate thing whenever we have to append another word to the result--start a new line, start a new word, append to the current word, etc. We do this by setting the "append" attribute to the right method so that our wrapper methods only need ever call ListSubber.append(), and the rest of the object takes care of doing the right thing internally. """ def __init__(self, env, mode, conv, gvars): collections.UserList.__init__(self, []) self.env = env self.mode = mode self.conv = conv self.gvars = gvars if self.mode == SUBST_RAW: self.add_strip = lambda x: self.append(x) else: self.add_strip = lambda x: None self.in_strip = None self.next_line() def expand(self, s, lvars, within_list): """Expand a single "token" as necessary, appending the expansion to the current result. This handles expanding different types of things (strings, lists, callables) appropriately. It calls the wrapper substitute() method to re-expand things as necessary, so that the results of expansions of side-by-side strings still get re-evaluated separately, not smushed together. """ if is_String(s): try: s0, s1 = s[:2] except (IndexError, ValueError): self.append(s) return if s0 != '$': self.append(s) return if s1 == '$': self.append('$') elif s1 == '(': self.open_strip('$(') elif s1 == ')': self.close_strip('$)') else: key = s[1:] if key[0] == '{' or key.find('.') >= 0: if key[0] == '{': key = key[1:-1] try: s = eval(key, self.gvars, lvars) except KeyboardInterrupt: raise except Exception, e: if e.__class__ in AllowableExceptions: return raise_exception(e, lvars['TARGETS'], s) else: if key in lvars: s = lvars[key] elif key in self.gvars: s = self.gvars[key] elif not NameError in AllowableExceptions: raise_exception(NameError(), lvars['TARGETS'], s) else: return # Before re-expanding the result, handle # recursive expansion by copying the local # variable dictionary and overwriting a null # string for the value of the variable name # we just expanded. lv = lvars.copy() var = key.split('.')[0] lv[var] = '' self.substitute(s, lv, 0) self.this_word() elif is_Sequence(s): for a in s: self.substitute(a, lvars, 1) self.next_word()
def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None): """Expand a string or list containing construction variable substitutions. This is the work-horse function for substitutions in file names and the like. The companion scons_subst_list() function (below) handles separating command lines into lists of arguments, so see that function if that's what you're looking for. """ if isinstance(strSubst, str) and strSubst.find('$') < 0: return strSubst class StringSubber(object): """A class to construct the results of a scons_subst() call. This binds a specific construction environment, mode, target and source with two methods (substitute() and expand()) that handle the expansion. """ def __init__(self, env, mode, conv, gvars): self.env = env self.mode = mode self.conv = conv self.gvars = gvars def expand(self, s, lvars): """Expand a single "token" as necessary, returning an appropriate string containing the expansion. This handles expanding different types of things (strings, lists, callables) appropriately. It calls the wrapper substitute() method to re-expand things as necessary, so that the results of expansions of side-by-side strings still get re-evaluated separately, not smushed together. """ if is_String(s): try: s0, s1 = s[:2] except (IndexError, ValueError): return s if s0 != '$': return s if s1 == '$': # In this case keep the double $'s which we'll later # swap for a single dollar sign as we need to retain # this information to properly avoid matching "$("" when # the actual text was "$$("" (or "$)"" when "$$)"" ) return '$$' elif s1 in '()': return s else: key = s[1:] if key[0] == '{' or '.' in key: if key[0] == '{': key = key[1:-1] try: s = eval(key, self.gvars, lvars) except KeyboardInterrupt: raise except Exception as e: if e.__class__ in AllowableExceptions: return '' raise_exception(e, lvars['TARGETS'], s) else: if key in lvars: s = lvars[key] elif key in self.gvars: s = self.gvars[key] elif NameError not in AllowableExceptions: raise_exception(NameError(key), lvars['TARGETS'], s) else: return '' # Before re-expanding the result, handle # recursive expansion by copying the local # variable dictionary and overwriting a null # string for the value of the variable name # we just expanded. # # This could potentially be optimized by only # copying lvars when s contains more expansions, # but lvars is usually supposed to be pretty # small, and deeply nested variable expansions # are probably more the exception than the norm, # so it should be tolerable for now. lv = lvars.copy() var = key.split('.')[0] lv[var] = '' return self.substitute(s, lv) elif is_Sequence(s): def func(l, conv=self.conv, substitute=self.substitute, lvars=lvars): return conv(substitute(l, lvars)) return list(map(func, s)) elif callable(s): try: s = s(target=lvars['TARGETS'], source=lvars['SOURCES'], env=self.env, for_signature=(self.mode != SUBST_CMD)) except TypeError: # This probably indicates that it's a callable # object that doesn't match our calling arguments # (like an Action). if self.mode == SUBST_RAW: return s s = self.conv(s) return self.substitute(s, lvars) elif s is None: return '' else: return s def substitute(self, args, lvars): """Substitute expansions in an argument or list of arguments. This serves as a wrapper for splitting up a string into separate tokens. """ if is_String(args) and not isinstance(args, CmdStringHolder): args = str(args) # In case it's a UserString. try: def sub_match(match): return self.conv(self.expand(match.group(1), lvars)) result = _dollar_exps.sub(sub_match, args) except TypeError: # If the internal conversion routine doesn't return # strings (it could be overridden to return Nodes, for # example), then the 1.5.2 re module will throw this # exception. Back off to a slower, general-purpose # algorithm that works for all data types. args = _separate_args.findall(args) result = [] for a in args: result.append(self.conv(self.expand(a, lvars))) if len(result) == 1: result = result[0] else: result = ''.join(map(str, result)) return result else: return self.expand(args, lvars) if conv is None: conv = _strconv[mode] # Doing this every time is a bit of a waste, since the Executor # has typically already populated the OverrideEnvironment with # $TARGET/$SOURCE variables. We're keeping this (for now), though, # because it supports existing behavior that allows us to call # an Action directly with an arbitrary target+source pair, which # we use in Tool/tex.py to handle calling $BIBTEX when necessary. # If we dropped that behavior (or found another way to cover it), # we could get rid of this call completely and just rely on the # Executor setting the variables. if 'TARGET' not in lvars: d = subst_dict(target, source) if d: lvars = lvars.copy() lvars.update(d) # We're (most likely) going to eval() things. If Python doesn't # find a __builtins__ value in the global dictionary used for eval(), # it copies the current global values for you. Avoid this by # setting it explicitly and then deleting, so we don't pollute the # construction environment Dictionary(ies) that are typically used # for expansion. gvars['__builtins__'] = __builtins__ ss = StringSubber(env, mode, conv, gvars) result = ss.substitute(strSubst, lvars) try: del gvars['__builtins__'] except KeyError: pass res = result if is_String(result): # Remove $(-$) pairs and any stuff in between, # if that's appropriate. remove = _regex_remove[mode] if remove: if mode == SUBST_SIG: result = _list_remove[mode](remove.split(result)) if result is None: raise SCons.Errors.UserError("Unbalanced $(/$) in: " + res) result = ' '.join(result) else: result = remove.sub('', result) if mode != SUBST_RAW: # Compress strings of white space characters into # a single space. result = _space_sep.sub(' ', result).strip() # Now replace escaped $'s currently "$$" # This is needed because we now retain $$ instead of # replacing them during substition to avoid # improperly trying to escape "$$(" as being "$(" result = result.replace('$$', '$') elif is_Sequence(result): remove = _list_remove[mode] if remove: result = remove(result) if result is None: raise SCons.Errors.UserError("Unbalanced $(/$) in: " + str(res)) return result
def expand(self, s, lvars, within_list): """Expand a single "token" as necessary, appending the expansion to the current result. This handles expanding different types of things (strings, lists, callables) appropriately. It calls the wrapper substitute() method to re-expand things as necessary, so that the results of expansions of side-by-side strings still get re-evaluated separately, not smushed together. """ if is_String(s): try: s0, s1 = s[:2] except (IndexError, ValueError): self.append(s) return if s0 != '$': self.append(s) return if s1 == '$': self.append('$') elif s1 == '(': self.open_strip('$(') elif s1 == ')': self.close_strip('$)') else: key = s[1:] if key[0] == '{' or key.find('.') >= 0: if key[0] == '{': key = key[1:-1] try: s = eval(key, self.gvars, lvars) except KeyboardInterrupt: raise except Exception as e: if e.__class__ in AllowableExceptions: return raise_exception(e, lvars['TARGETS'], s) else: if key in lvars: s = lvars[key] elif key in self.gvars: s = self.gvars[key] elif NameError not in AllowableExceptions: raise_exception(NameError(), lvars['TARGETS'], s) else: return # Before re-expanding the result, handle # recursive expansion by copying the local # variable dictionary and overwriting a null # string for the value of the variable name # we just expanded. lv = lvars.copy() var = key.split('.')[0] lv[var] = '' self.substitute(s, lv, 0) self.this_word() elif is_Sequence(s): for a in s: self.substitute(a, lvars, 1) self.next_word() elif callable(s): try: s = s(target=lvars['TARGETS'], source=lvars['SOURCES'], env=self.env, for_signature=(self.mode != SUBST_CMD)) except TypeError: # This probably indicates that it's a callable # object that doesn't match our calling arguments # (like an Action). if self.mode == SUBST_RAW: self.append(s) return s = self.conv(s) self.substitute(s, lvars, within_list) elif s is None: self.this_word() else: self.append(s)
def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None): """Expand a string or list containing construction variable substitutions. This is the work-horse function for substitutions in file names and the like. The companion scons_subst_list() function (below) handles separating command lines into lists of arguments, so see that function if that's what you're looking for. """ if isinstance(strSubst, str) and strSubst.find('$') < 0: return strSubst class StringSubber(object): """A class to construct the results of a scons_subst() call. This binds a specific construction environment, mode, target and source with two methods (substitute() and expand()) that handle the expansion. """ def __init__(self, env, mode, conv, gvars): self.env = env self.mode = mode self.conv = conv self.gvars = gvars def expand(self, s, lvars): """Expand a single "token" as necessary, returning an appropriate string containing the expansion. This handles expanding different types of things (strings, lists, callables) appropriately. It calls the wrapper substitute() method to re-expand things as necessary, so that the results of expansions of side-by-side strings still get re-evaluated separately, not smushed together. """ if is_String(s): try: s0, s1 = s[:2] except (IndexError, ValueError): return s if s0 != '$': return s if s1 == '$': return '$' elif s1 in '()': return s else: key = s[1:] if key[0] == '{' or key.find('.') >= 0: if key[0] == '{': key = key[1:-1] try: s = eval(key, self.gvars, lvars) except KeyboardInterrupt: raise except Exception, e: if e.__class__ in AllowableExceptions: return '' raise_exception(e, lvars['TARGETS'], s) else: if key in lvars: s = lvars[key] elif key in self.gvars: s = self.gvars[key] elif not NameError in AllowableExceptions: raise_exception(NameError(key), lvars['TARGETS'], s) else: return '' # Before re-expanding the result, handle # recursive expansion by copying the local # variable dictionary and overwriting a null # string for the value of the variable name # we just expanded. # # This could potentially be optimized by only # copying lvars when s contains more expansions, # but lvars is usually supposed to be pretty # small, and deeply nested variable expansions # are probably more the exception than the norm, # so it should be tolerable for now. lv = lvars.copy() var = key.split('.')[0] lv[var] = '' return self.substitute(s, lv) elif is_Sequence(s): def func(l, conv=self.conv, substitute=self.substitute, lvars=lvars): return conv(substitute(l, lvars)) return list(map(func, s))
def expand(self, s, lvars): """Expand a single "token" as necessary, returning an appropriate string containing the expansion. This handles expanding different types of things (strings, lists, callables) appropriately. It calls the wrapper substitute() method to re-expand things as necessary, so that the results of expansions of side-by-side strings still get re-evaluated separately, not smushed together. """ if is_String(s): try: s0, s1 = s[:2] except (IndexError, ValueError): return s if s0 != '$': return s if s1 == '$': # In this case keep the double $'s which we'll later # swap for a single dollar sign as we need to retain # this information to properly avoid matching "$("" when # the actual text was "$$("" (or "$)"" when "$$)"" ) return '$$' elif s1 in '()': return s else: key = s[1:] if key[0] == '{' or '.' in key: if key[0] == '{': key = key[1:-1] # Store for error messages if we fail to expand the # value old_s = s s = None if key in lvars: s = lvars[key] elif key in self.gvars: s = self.gvars[key] else: try: s = eval(key, self.gvars, lvars) except KeyboardInterrupt: raise except Exception as e: if e.__class__ in AllowableExceptions: return '' raise_exception(e, lvars['TARGETS'], old_s) if s is None and NameError not in AllowableExceptions: raise_exception(NameError(key), lvars['TARGETS'], old_s) elif s is None: return '' # Before re-expanding the result, handle # recursive expansion by copying the local # variable dictionary and overwriting a null # string for the value of the variable name # we just expanded. # # This could potentially be optimized by only # copying lvars when s contains more expansions, # but lvars is usually supposed to be pretty # small, and deeply nested variable expansions # are probably more the exception than the norm, # so it should be tolerable for now. lv = lvars.copy() var = key.split('.')[0] lv[var] = '' return self.substitute(s, lv) elif is_Sequence(s): def func(l, conv=self.conv, substitute=self.substitute, lvars=lvars): return conv(substitute(l, lvars)) return list(map(func, s)) elif callable(s): # SCons has the unusual Null class where any __getattr__ call returns it's self, # which does not work the signature module, and the Null class returns an empty # string if called on, so we make an exception in this condition for Null class if (isinstance(s, SCons.Util.Null) or set(signature(s).parameters.keys()) == set( ['target', 'source', 'env', 'for_signature'])): s = s(target=lvars['TARGETS'], source=lvars['SOURCES'], env=self.env, for_signature=(self.mode != SUBST_CMD)) else: # This probably indicates that it's a callable # object that doesn't match our calling arguments # (like an Action). if self.mode == SUBST_RAW: return s s = self.conv(s) return self.substitute(s, lvars) elif s is None: return '' else: return s
try: del gvars['__builtins__'] except KeyError: pass if is_String(result): # Remove $(-$) pairs and any stuff in between, # if that's appropriate. remove = _regex_remove[mode] if remove: result = remove.sub('', result) if mode != SUBST_RAW: # Compress strings of white space characters into # a single space. result = _space_sep.sub(' ', result).strip() elif is_Sequence(result): remove = _list_remove[mode] if remove: result = remove(result) return result def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None):
def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None): """Expand a string or list containing construction variable substitutions. This is the work-horse function for substitutions in file names and the like. The companion scons_subst_list() function (below) handles separating command lines into lists of arguments, so see that function if that's what you're looking for. """ if (isinstance(strSubst, str) and '$' not in strSubst) or isinstance( strSubst, CmdStringHolder): return strSubst if conv is None: conv = _strconv[mode] # Doing this every time is a bit of a waste, since the Executor # has typically already populated the OverrideEnvironment with # $TARGET/$SOURCE variables. We're keeping this (for now), though, # because it supports existing behavior that allows us to call # an Action directly with an arbitrary target+source pair, which # we use in Tool/tex.py to handle calling $BIBTEX when necessary. # If we dropped that behavior (or found another way to cover it), # we could get rid of this call completely and just rely on the # Executor setting the variables. if 'TARGET' not in lvars: d = subst_dict(target, source) if d: lvars = lvars.copy() lvars.update(d) # We're (most likely) going to eval() things. If Python doesn't # find a __builtins__ value in the global dictionary used for eval(), # it copies the current global values for you. Avoid this by # setting it explicitly and then deleting, so we don't pollute the # construction environment Dictionary(ies) that are typically used # for expansion. gvars['__builtins__'] = __builtins__ ss = StringSubber(env, mode, conv, gvars) result = ss.substitute(strSubst, lvars) try: del gvars['__builtins__'] except KeyError: pass res = result if is_String(result): # Remove $(-$) pairs and any stuff in between, # if that's appropriate. remove = _regex_remove[mode] if remove: if mode == SUBST_SIG: result = _list_remove[mode](remove.split(result)) if result is None: raise SCons.Errors.UserError("Unbalanced $(/$) in: " + res) result = ' '.join(result) else: result = remove.sub('', result) if mode != SUBST_RAW: # Compress strings of white space characters into # a single space. result = _space_sep.sub(' ', result).strip() # Now replace escaped $'s currently "$$" # This is needed because we now retain $$ instead of # replacing them during substition to avoid # improperly trying to escape "$$(" as being "$(" result = result.replace('$$', '$') elif is_Sequence(result): remove = _list_remove[mode] if remove: result = remove(result) if result is None: raise SCons.Errors.UserError("Unbalanced $(/$) in: " + str(res)) return result
def expand(self, s, lvars, within_list): """Expand a single "token" as necessary, appending the expansion to the current result. This handles expanding different types of things (strings, lists, callables) appropriately. It calls the wrapper substitute() method to re-expand things as necessary, so that the results of expansions of side-by-side strings still get re-evaluated separately, not smushed together. """ if is_String(s): try: s0, s1 = s[:2] except (IndexError, ValueError): self.append(s) return if s0 != '$': self.append(s) return if s1 == '$': self.append('$') elif s1 == '(': self.open_strip('$(') elif s1 == ')': self.close_strip('$)') else: key = s[1:] if key[0] == '{' or key.find('.') >= 0: if key[0] == '{': key = key[1:-1] # Store for error messages if we fail to expand the # value old_s = s s = None if key in lvars: s = lvars[key] elif key in self.gvars: s = self.gvars[key] else: try: s = eval(key, self.gvars, lvars) except KeyboardInterrupt: raise except Exception as e: if e.__class__ in AllowableExceptions: return raise_exception(e, lvars['TARGETS'], old_s) if s is None and NameError not in AllowableExceptions: raise_exception(NameError(), lvars['TARGETS'], old_s) elif s is None: return # If the string is already full expanded there's no # need to continue recursion. if self.expanded(s): self.append(s) return # Before re-expanding the result, handle # recursive expansion by copying the local # variable dictionary and overwriting a null # string for the value of the variable name # we just expanded. lv = lvars.copy() var = key.split('.')[0] lv[var] = '' self.substitute(s, lv, 0) self.this_word() elif is_Sequence(s): for a in s: self.substitute(a, lvars, 1) self.next_word() elif callable(s): # SCons has the unusual Null class where any __getattr__ call returns it's self, # which does not work the signature module, and the Null class returns an empty # string if called on, so we make an exception in this condition for Null class if (isinstance(s, SCons.Util.Null) or set(signature(s).parameters.keys()) == set( ['target', 'source', 'env', 'for_signature'])): s = s(target=lvars['TARGETS'], source=lvars['SOURCES'], env=self.env, for_signature=(self.mode != SUBST_CMD)) else: # This probably indicates that it's a callable # object that doesn't match our calling arguments # (like an Action). if self.mode == SUBST_RAW: self.append(s) return s = self.conv(s) self.substitute(s, lvars, within_list) elif s is None: self.this_word() else: self.append(s)
def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None): """Substitute construction variables in a string (or list or other object) and separate the arguments into a command list. The companion scons_subst() function (above) handles basic substitutions within strings, so see that function instead if that's what you're looking for. """ # try: # Subst_List_Strings[strSubst] = Subst_List_Strings[strSubst] + 1 # except KeyError: # Subst_List_Strings[strSubst] = 1 # import SCons.Debug # SCons.Debug.caller_trace(1) class ListSubber(collections.UserList): """A class to construct the results of a scons_subst_list() call. Like StringSubber, this class binds a specific construction environment, mode, target and source with two methods (substitute() and expand()) that handle the expansion. In addition, however, this class is used to track the state of the result(s) we're gathering so we can do the appropriate thing whenever we have to append another word to the result--start a new line, start a new word, append to the current word, etc. We do this by setting the "append" attribute to the right method so that our wrapper methods only need ever call ListSubber.append(), and the rest of the object takes care of doing the right thing internally. """ def __init__(self, env, mode, conv, gvars): collections.UserList.__init__(self, []) self.env = env self.mode = mode self.conv = conv self.gvars = gvars if self.mode == SUBST_RAW: self.add_strip = lambda x: self.append(x) else: self.add_strip = lambda x: None self.in_strip = None self.next_line() def expand(self, s, lvars, within_list): """Expand a single "token" as necessary, appending the expansion to the current result. This handles expanding different types of things (strings, lists, callables) appropriately. It calls the wrapper substitute() method to re-expand things as necessary, so that the results of expansions of side-by-side strings still get re-evaluated separately, not smushed together. """ if is_String(s): try: s0, s1 = s[:2] except (IndexError, ValueError): self.append(s) return if s0 != '$': self.append(s) return if s1 == '$': self.append('$') elif s1 == '(': self.open_strip('$(') elif s1 == ')': self.close_strip('$)') else: key = s[1:] if key[0] == '{' or key.find('.') >= 0: if key[0] == '{': key = key[1:-1] try: s = eval(key, self.gvars, lvars) except KeyboardInterrupt: raise except Exception, e: if e.__class__ in AllowableExceptions: return raise_exception(e, lvars['TARGETS'], s) else: if key in lvars: s = lvars[key] elif key in self.gvars: s = self.gvars[key] elif not NameError in AllowableExceptions: raise_exception(NameError(), lvars['TARGETS'], s) else: return # Before re-expanding the result, handle # recursive expansion by copying the local # variable dictionary and overwriting a null # string for the value of the variable name # we just expanded. lv = lvars.copy() var = key.split('.')[0] lv[var] = '' self.substitute(s, lv, 0) self.this_word() elif is_Sequence(s): for a in s: self.substitute(a, lvars, 1) self.next_word()
def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None): """Expand a string or list containing construction variable substitutions. This is the work-horse function for substitutions in file names and the like. The companion scons_subst_list() function (below) handles separating command lines into lists of arguments, so see that function if that's what you're looking for. """ if isinstance(strSubst, str) and strSubst.find('$') < 0: return strSubst class StringSubber(object): """A class to construct the results of a scons_subst() call. This binds a specific construction environment, mode, target and source with two methods (substitute() and expand()) that handle the expansion. """ def __init__(self, env, mode, conv, gvars): self.env = env self.mode = mode self.conv = conv self.gvars = gvars def expand(self, s, lvars): """Expand a single "token" as necessary, returning an appropriate string containing the expansion. This handles expanding different types of things (strings, lists, callables) appropriately. It calls the wrapper substitute() method to re-expand things as necessary, so that the results of expansions of side-by-side strings still get re-evaluated separately, not smushed together. """ if is_String(s): try: s0, s1 = s[:2] except (IndexError, ValueError): return s if s0 != '$': return s if s1 == '$': return '$' elif s1 in '()': return s else: key = s[1:] if key[0] == '{' or '.' in key: if key[0] == '{': key = key[1:-1] try: s = eval(key, self.gvars, lvars) except KeyboardInterrupt: raise except Exception as e: if e.__class__ in AllowableExceptions: return '' raise_exception(e, lvars['TARGETS'], s) else: if key in lvars: s = lvars[key] elif key in self.gvars: s = self.gvars[key] elif not NameError in AllowableExceptions: raise_exception(NameError(key), lvars['TARGETS'], s) else: return '' # Before re-expanding the result, handle # recursive expansion by copying the local # variable dictionary and overwriting a null # string for the value of the variable name # we just expanded. # # This could potentially be optimized by only # copying lvars when s contains more expansions, # but lvars is usually supposed to be pretty # small, and deeply nested variable expansions # are probably more the exception than the norm, # so it should be tolerable for now. lv = lvars.copy() var = key.split('.')[0] lv[var] = '' return self.substitute(s, lv) elif is_Sequence(s): def func(l, conv=self.conv, substitute=self.substitute, lvars=lvars): return conv(substitute(l, lvars)) return list(map(func, s)) elif callable(s): try: s = s(target=lvars['TARGETS'], source=lvars['SOURCES'], env=self.env, for_signature=(self.mode != SUBST_CMD)) except TypeError: # This probably indicates that it's a callable # object that doesn't match our calling arguments # (like an Action). if self.mode == SUBST_RAW: return s s = self.conv(s) return self.substitute(s, lvars) elif s is None: return '' else: return s def substitute(self, args, lvars): """Substitute expansions in an argument or list of arguments. This serves as a wrapper for splitting up a string into separate tokens. """ if is_String(args) and not isinstance(args, CmdStringHolder): args = str(args) # In case it's a UserString. try: def sub_match(match): return self.conv(self.expand(match.group(1), lvars)) result = _dollar_exps.sub(sub_match, args) except TypeError: # If the internal conversion routine doesn't return # strings (it could be overridden to return Nodes, for # example), then the 1.5.2 re module will throw this # exception. Back off to a slower, general-purpose # algorithm that works for all data types. args = _separate_args.findall(args) result = [] for a in args: result.append(self.conv(self.expand(a, lvars))) if len(result) == 1: result = result[0] else: result = ''.join(map(str, result)) return result else: return self.expand(args, lvars) if conv is None: conv = _strconv[mode] # Doing this every time is a bit of a waste, since the Executor # has typically already populated the OverrideEnvironment with # $TARGET/$SOURCE variables. We're keeping this (for now), though, # because it supports existing behavior that allows us to call # an Action directly with an arbitrary target+source pair, which # we use in Tool/tex.py to handle calling $BIBTEX when necessary. # If we dropped that behavior (or found another way to cover it), # we could get rid of this call completely and just rely on the # Executor setting the variables. if 'TARGET' not in lvars: d = subst_dict(target, source) if d: lvars = lvars.copy() lvars.update(d) # We're (most likely) going to eval() things. If Python doesn't # find a __builtins__ value in the global dictionary used for eval(), # it copies the current global values for you. Avoid this by # setting it explicitly and then deleting, so we don't pollute the # construction environment Dictionary(ies) that are typically used # for expansion. gvars['__builtins__'] = __builtins__ ss = StringSubber(env, mode, conv, gvars) result = ss.substitute(strSubst, lvars) try: del gvars['__builtins__'] except KeyError: pass res = result if is_String(result): # Remove $(-$) pairs and any stuff in between, # if that's appropriate. remove = _regex_remove[mode] if remove: if mode == SUBST_SIG: result = _list_remove[mode](remove.split(result)) if result is None: raise SCons.Errors.UserError("Unbalanced $(/$) in: " + res) result = ' '.join(result) else: result = remove.sub('', result) if mode != SUBST_RAW: # Compress strings of white space characters into # a single space. result = _space_sep.sub(' ', result).strip() elif is_Sequence(result): remove = _list_remove[mode] if remove: result = remove(result) if result is None: raise SCons.Errors.UserError("Unbalanced $(/$) in: " + str(res)) return result
try: del gvars['__builtins__'] except KeyError: pass if is_String(result): # Remove $(-$) pairs and any stuff in between, # if that's appropriate. remove = _regex_remove[mode] if remove: result = remove.sub('', result) if mode != SUBST_RAW: # Compress strings of white space characters into # a single space. result = _space_sep.sub(' ', result).strip() elif is_Sequence(result): remove = _list_remove[mode] if remove: result = remove(result) return result #Subst_List_Strings = {} def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={}, lvars={}, conv=None): """Substitute construction variables in a string (or list or other object) and separate the arguments into a command list. The companion scons_subst() function (above) handles basic substitutions within strings, so see that function instead if that's what you're looking for.
def expand(self, s, lvars, within_list): """Expand a single "token" as necessary, appending the expansion to the current result. This handles expanding different types of things (strings, lists, callables) appropriately. It calls the wrapper substitute() method to re-expand things as necessary, so that the results of expansions of side-by-side strings still get re-evaluated separately, not smushed together. """ if is_String(s): try: s0, s1 = s[:2] except (IndexError, ValueError): self.append(s) return if s0 != '$': self.append(s) return if s1 == '$': self.append('$') elif s1 == '(': self.open_strip('$(') elif s1 == ')': self.close_strip('$)') else: key = s[1:] if key[0] == '{' or key.find('.') >= 0: if key[0] == '{': key = key[1:-1] try: s = eval(key, self.gvars, lvars) except KeyboardInterrupt: raise except Exception as e: if e.__class__ in AllowableExceptions: return raise_exception(e, lvars['TARGETS'], s) else: if key in lvars: s = lvars[key] elif key in self.gvars: s = self.gvars[key] elif not NameError in AllowableExceptions: raise_exception(NameError(), lvars['TARGETS'], s) else: return # Before re-expanding the result, handle # recursive expansion by copying the local # variable dictionary and overwriting a null # string for the value of the variable name # we just expanded. lv = lvars.copy() var = key.split('.')[0] lv[var] = '' self.substitute(s, lv, 0) self.this_word() elif is_Sequence(s): for a in s: self.substitute(a, lvars, 1) self.next_word() elif callable(s): try: s = s(target=lvars['TARGETS'], source=lvars['SOURCES'], env=self.env, for_signature=(self.mode != SUBST_CMD)) except TypeError: # This probably indicates that it's a callable # object that doesn't match our calling arguments # (like an Action). if self.mode == SUBST_RAW: self.append(s) return s = self.conv(s) self.substitute(s, lvars, within_list) elif s is None: self.this_word() else: self.append(s)