Example #1
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='')
        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()
Example #2
0
        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)
Example #3
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 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)
def _arg2builder(env, arg):
    """Convert an argument to a builder object it refers.

    :Parameters:
        env : SCons.Environment.Environment
            a SCons Environment object
        arg
            an argument to be converted to a real builder, may be builder name
            or a builder object.

    What we need in several places of this tool is a builder which has an
    emitter. The user, however, will likely provide just a builder's name, not
    the builder object. The purpose of this function is to resolve these names
    to real builder objects. It also unwraps any CompositeBuilders and so
    on so the returned object is an instance of SCons.Builder.BuilderBase.
    """
    from SCons.Util import is_String
    import SCons.Builder
    if is_String(arg):
        arg = env['BUILDERS'].get(arg)
        if arg is None:
            return arg
    if not isinstance(arg, SCons.Builder.BuilderBase):
        try:
            arg = arg.builder
        except AttributeError:
            arg = None
    return arg
Example #5
0
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()
Example #6
0
    def execute(self, target, source, env):
        """Execute a command action.

        This will handle lists of commands as well as individual commands,
        because construction variable substitution may turn a single
        "command" into a list.  This means that this class can actually
        handle lists of commands, even though that's not how we use it
        externally.
        """
        from SCons.Subst import escape_list
        from SCons.Util import is_String, is_List, flatten

        try:
            shell = env['SHELL']
        except KeyError:
            raise SCons.Errors.UserError('Missing SHELL construction variable.')

        try:
            spawn = env['SPAWN']
        except KeyError:
            raise SCons.Errors.UserError('Missing SPAWN construction variable.')

        escape = env.get('ESCAPE', lambda x: x)

        try:
            ENV = env['ENV']
        except KeyError:
            global default_ENV
            if not default_ENV:
                import SCons.Environment
                default_ENV = SCons.Environment.Environment()['ENV']
            ENV = default_ENV

        # Ensure that the ENV values are all strings:
        for key, value in ENV.items():
            if not is_String(value):
                if is_List(value):
                    # If the value is a list, then we assume it is a
                    # path list, because that's a pretty common list-like
                    # value to stick in an environment variable:
                    value = flatten(value)
                    ENV[key] = string.join(map(str, value), os.pathsep)
                else:
                    # If it isn't a string or a list, then we just coerce
                    # it to a string, which is the proper way to handle
                    # Dir and File instances and will produce something
                    # reasonable for just about everything else:
                    ENV[key] = str(value)

        cmd_list, ignore, silent = self.process(target, map(rfile, source), env)

        # Use len() to filter out any "command" that's zero-length.
        for cmd_line in filter(len, cmd_list):
            # Escape the command line for the interpreter we are using.
            cmd_line = escape_list(cmd_line, escape)
            result = spawn(shell, escape, cmd_line[0], cmd_line, ENV)
            if not ignore and result:
                return result
        return 0
Example #7
0
        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()
Example #8
0
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
Example #9
0
 def add_new_word(self, x):
     if not self.in_strip or self.mode != SUBST_SIG:
         literal = self.literal(x)
         x = self.conv(x)
         if is_String(x):
             x = CmdStringHolder(x, literal)
         self[-1].append(x)
     self.append = self.add_to_current_word
Example #10
0
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])
Example #11
0
        def add_to_current_word(self, x):
            """Append the string x to the end of the current last word
            in the result.  If that is not possible, then just add
            it as a new word.  Make sure the entire concatenated string
            inherits the object attributes of x (in particular, the
            escape function) by wrapping it as CmdStringHolder."""

            if not self.in_strip or self.mode != SUBST_SIG:
                try:
                    current_word = self[-1][-1]
                except IndexError:
                    self.add_new_word(x)
                else:
                    # All right, this is a hack and it should probably
                    # be refactored out of existence in the future.
                    # The issue is that we want to smoosh words together
                    # and make one file name that gets escaped if
                    # we're expanding something like foo$EXTENSION,
                    # but we don't want to smoosh them together if
                    # it's something like >$TARGET, because then we'll
                    # treat the '>' like it's part of the file name.
                    # So for now, just hard-code looking for the special
                    # command-line redirection characters...
                    try:
                        last_char = str(current_word)[-1]
                    except IndexError:
                        last_char = '\0'
                    if last_char in '<>|':
                        self.add_new_word(x)
                    else:
                        y = current_word + x

                        # We used to treat a word appended to a literal
                        # as a literal itself, but this caused problems
                        # with interpreting quotes around space-separated
                        # targets on command lines.  Removing this makes
                        # none of the "substantive" end-to-end tests fail,
                        # so we'll take this out but leave it commented
                        # for now in case there's a problem not covered
                        # by the test cases and we need to resurrect this.
                        #literal1 = self.literal(self[-1][-1])
                        #literal2 = self.literal(x)
                        y = self.conv(y)
                        if is_String(y):
                            #y = CmdStringHolder(y, literal1 or literal2)
                            y = CmdStringHolder(y, None)
                        self[-1][-1] = y
Example #12
0
        def add_to_current_word(self, x):
            """Append the string x to the end of the current last word
            in the result.  If that is not possible, then just add
            it as a new word.  Make sure the entire concatenated string
            inherits the object attributes of x (in particular, the
            escape function) by wrapping it as CmdStringHolder."""

            if not self.in_strip or self.mode != SUBST_SIG:
                try:
                    current_word = self[-1][-1]
                except IndexError:
                    self.add_new_word(x)
                else:
                    # All right, this is a hack and it should probably
                    # be refactored out of existence in the future.
                    # The issue is that we want to smoosh words together
                    # and make one file name that gets escaped if
                    # we're expanding something like foo$EXTENSION,
                    # but we don't want to smoosh them together if
                    # it's something like >$TARGET, because then we'll
                    # treat the '>' like it's part of the file name.
                    # So for now, just hard-code looking for the special
                    # command-line redirection characters...
                    try:
                        last_char = str(current_word)[-1]
                    except IndexError:
                        last_char = '\0'
                    if last_char in '<>|':
                        self.add_new_word(x)
                    else:
                        y = current_word + x

                        # We used to treat a word appended to a literal
                        # as a literal itself, but this caused problems
                        # with interpreting quotes around space-separated
                        # targets on command lines.  Removing this makes
                        # none of the "substantive" end-to-end tests fail,
                        # so we'll take this out but leave it commented
                        # for now in case there's a problem not covered
                        # by the test cases and we need to resurrect this.
                        #literal1 = self.literal(self[-1][-1])
                        #literal2 = self.literal(x)
                        y = self.conv(y)
                        if is_String(y):
                            #y = CmdStringHolder(y, literal1 or literal2)
                            y = CmdStringHolder(y, None)
                        self[-1][-1] = y
Example #13
0
        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 string.find(key, '.') >= 0:
                        if key[0] == '{':
                            key = key[1:-1]
                        try:
                            s = eval(key, self.gvars, lvars)
                        except AttributeError, e:
                            raise SCons.Errors.UserError, \
                                  "Error trying to evaluate `%s': %s" % (s, e)
                        except (IndexError, NameError, TypeError):
                            return
                        except SyntaxError, e:
                            if self.target:
                                raise SCons.Errors.BuildError, (
                                    self.target[0],
                                    "Syntax error `%s' trying to evaluate `%s'"
                                    % (e, s))
                            else:
                                raise SCons.Errors.UserError, "Syntax error `%s' trying to evaluate `%s'" % (
                                    e, s)
Example #14
0
 def test_is_String(self):
     assert is_String("")
     assert is_String(UserString(''))
     try:
         class mystr(str):
             pass
     except TypeError:
         pass
     else:
         assert is_String(mystr(''))
     assert not is_String({})
     assert not is_String([])
     assert not is_String(())
Example #15
0
        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 string.find(key, '.') >= 0:
                        if key[0] == '{':
                            key = key[1:-1]
                        try:
                            s = eval(key, self.gvars, lvars)
                        except AttributeError, e:
                            raise SCons.Errors.UserError, \
                                  "Error trying to evaluate `%s': %s" % (s, e)
                        except (IndexError, NameError, TypeError):
                            return
                        except SyntaxError,e:
                            if self.target:
                                raise SCons.Errors.BuildError, (self.target[0], "Syntax error `%s' trying to evaluate `%s'" % (e,s))
                            else:
                                raise SCons.Errors.UserError, "Syntax error `%s' trying to evaluate `%s'" % (e,s)
Example #16
0
        def substitute(self, args, lvars, within_list):
            """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 = _separate_args.findall(args)
                for a in args:
                    if a[0] in ' \t\n\r\f\v':
                        if '\n' in a:
                            self.next_line()
                        elif within_list:
                            self.append(a)
                        else:
                            self.next_word()
                    else:
                        self.expand(a, lvars, within_list)
            else:
                self.expand(args, lvars, within_list)
Example #17
0
        def substitute(self, args, lvars, within_list):
            """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 = _separate_args.findall(args)
                for a in args:
                    if a[0] in ' \t\n\r\f\v':
                        if '\n' in a:
                            self.next_line()
                        elif within_list:
                            self.append(a)
                        else:
                            self.next_word()
                    else:
                        self.expand(a, lvars, within_list)
            else:
                self.expand(args, lvars, within_list)
Example #18
0
def setup_scons_entities(env):
    try:
        shell = env['SHELL']
    except KeyError:
        raise SCons.Errors.UserError('Missing SHELL construction variable.')

    try:
        spawn = env['SPAWN']
    except KeyError:
        raise SCons.Errors.UserError('Missing SPAWN construction variable.')
    else:
        if type(spawn) == 'str':
            spawn = env.subst(spawn, raw=1, conv=lambda x: x)

    escape = env.get('ESCAPE', lambda x: x)

    try:
        ENV = env['ENV']
    except KeyError:
        import SCons.Environment
        ENV = SCons.Environment.Environment()['ENV']

    # Ensure that the ENV values are all strings:
    for key, value in list(ENV.items()):
        if not is_String(value):
            if is_List(value):
                # If the value is a list, then we assume it is a
                # path list, because that's a pretty common list-like
                # value to stick in an environment variable:
                value = flatten_sequence(value)
                ENV[key] = os.pathsep.join(map(str, value))
            else:
                # If it isn't a string or a list, then we just coerce
                # it to a string, which is the proper way to handle
                # Dir and File instances and will produce something
                # reasonable for just about everything else:
                ENV[key] = str(value)

    return (shell, spawn, escape, ENV.copy())
def _action(target, source, env):
    # prepare the line seperator
    linesep = source.pop(0)
    if linesep == None:
        linesep = os.linesep
    elif not is_String(linesep):
        if not isinstance(linesep, Value):
            raise TypeError, \
                  'unexpected type/class for LineSeperator: %s' % repr(linesep)
        else:
            linesep = linesep.get_contents()
    # write the file
    fd = open(target[0].get_path(), "w")
    # seperate lines by 'linesep' only if linesep is not empty
    if linesep:
        lsep = ''
        for s in source:
            fd.writelines((lsep, s.get_contents()))
            lsep = linesep
    else:
        for s in source:
            fd.write(s.get_contents())
    fd.close()
Example #20
0
def get_command(env, node, action):  # pylint: disable=too-many-branches
    """Get the command to execute for node."""
    if node.env:
        sub_env = node.env
    else:
        sub_env = env

    executor = node.get_executor()
    if executor is not None:
        tlist = executor.get_all_targets()
        slist = executor.get_all_sources()
    else:
        if hasattr(node, "target_peers"):
            tlist = node.target_peers
        else:
            tlist = [node]
        slist = node.sources

    # Retrieve the repository file for all sources
    slist = [rfile(s) for s in slist]

    # Get the dependencies for all targets
    implicit = list({dep for tgt in tlist for dep in get_dependencies(tgt)})

    # Generate a real CommandAction
    if isinstance(action, SCons.Action.CommandGeneratorAction):
        # pylint: disable=protected-access
        action = action._generate(tlist, slist, sub_env, 1, executor=executor)

    rule = "CMD"

    # Actions like CommandAction have a method called process that is
    # used by SCons to generate the cmd_line they need to run. So
    # check if it's a thing like CommandAction and call it if we can.
    if hasattr(action, "process"):
        cmd_list, _, _ = action.process(tlist,
                                        slist,
                                        sub_env,
                                        executor=executor)

        # Despite being having "list" in it's name this member is not
        # actually a list. It's the pre-subst'd string of the command. We
        # use it to determine if the command we generated needs to use a
        # custom Ninja rule. By default this redirects CC/CXX commands to
        # CMD_W_DEPS but the user can inject custom Ninja rules and tie
        # them to commands by using their pre-subst'd string.
        rule = __NINJA_RULE_MAPPING.get(action.cmd_list, "CMD")

        cmd = _string_from_cmd_list(cmd_list[0])
    else:
        # Anything else works with genstring, this is most commonly hit by
        # ListActions which essentially call process on all of their
        # commands and concatenate it for us.
        genstring = action.genstring(tlist, slist, sub_env)

        # Detect if we have a custom rule for this
        # "ListActionCommandAction" type thing.
        rule = __NINJA_RULE_MAPPING.get(genstring, "CMD")

        if executor is not None:
            cmd = sub_env.subst(genstring, executor=executor)
        else:
            cmd = sub_env.subst(genstring, target=tlist, source=slist)

        # Since we're only enabling Ninja for developer builds right
        # now we skip all Manifest related work on Windows as it's not
        # necessary. We shouldn't have gotten here but on Windows
        # SCons has a ListAction which shows as a
        # CommandGeneratorAction for linking. That ListAction ends
        # with a FunctionAction (embedManifestExeCheck,
        # embedManifestDllCheck) that simply say "does
        # target[0].manifest exist?" if so execute the real command
        # action underlying me, otherwise do nothing.
        #
        # Eventually we'll want to find a way to translate this to
        # Ninja but for now, and partially because the existing Ninja
        # generator does so, we just disable it all together.
        cmd = cmd.replace("\n", " && ").strip()
        if env["PLATFORM"] == "win32" and ("embedManifestExeCheck" in cmd
                                           or "embedManifestDllCheck" in cmd):
            cmd = " && ".join(cmd.split(" && ")[0:-1])

        if cmd.endswith("&&"):
            cmd = cmd[0:-2].strip()

    outputs = get_outputs(node)
    if rule == "CMD_W_DEPS":
        # When using a depfile Ninja can only have a single output but
        # SCons will usually have emitted an output for every thing a
        # command will create because it's caching is much more
        # complex than Ninja's. This includes things like DWO
        # files. Here we make sure that Ninja only ever sees one
        # target when using a depfile. It will still have a command
        # that will create all of the outputs but most targets don't
        # depend direclty on DWO files and so this assumption is
        # safe to make.
        outputs = outputs[0:1]

    command_env = getattr(node.attributes, "NINJA_ENV_ENV", "")
    # If win32 and rule == CMD_W_DEPS then we don't want to calculate
    # an environment for this command. It's a compile command and
    # compiledb doesn't support shell syntax on Windows. We need the
    # shell syntax to use environment variables on Windows so we just
    # skip this platform / rule combination to keep the compiledb
    # working.
    #
    # On POSIX we can still set environment variables even for compile
    # commands so we do so.
    if not command_env and not (env["PLATFORM"] == "win32"
                                and rule == "CMD_W_DEPS"):
        ENV = get_default_ENV(sub_env)

        # This is taken wholesale from SCons/Action.py
        #
        # Ensure that the ENV values are all strings:
        for key, value in ENV.items():
            if not is_String(value):
                if is_List(value):
                    # If the value is a list, then we assume it is a
                    # path list, because that's a pretty common list-like
                    # value to stick in an environment variable:
                    value = flatten_sequence(value)
                    value = os.pathsep.join(map(str, value))
                else:
                    # If it isn't a string or a list, then we just coerce
                    # it to a string, which is the proper way to handle
                    # Dir and File instances and will produce something
                    # reasonable for just about everything else:
                    value = str(value)

            if env["PLATFORM"] == "win32":
                command_env += "set '{}={}' && ".format(key, value)
            else:
                command_env += "{}={} ".format(key, value)

        setattr(node.attributes, "NINJA_ENV_ENV", command_env)

    ninja_build = {
        "outputs": outputs,
        "implicit": implicit,
        "rule": rule,
        "variables": {
            "cmd": command_env + cmd
        },
    }

    # Don't use sub_env here because we require that NINJA_POOL be set
    # on a per-builder call basis to prevent accidental strange
    # behavior like env['NINJA_POOL'] = 'console' and sub_env can be
    # the global Environment object if node.env is None.
    # Example:
    #
    # Allowed:
    #
    #     env.Command("ls", NINJA_POOL="ls_pool")
    #
    # Not allowed and ignored:
    #
    #     env["NINJA_POOL"] = "ls_pool"
    #     env.Command("ls")
    #
    if node.env and node.env.get("NINJA_POOL", None) is not None:
        ninja_build["pool"] = node.env["NINJA_POOL"]

    return ninja_build
Example #21
0
def _node(env, path):
    if is_String(path):
        return env.File(path)
    else:
        return path
Example #22
0
    def execute(self, target, source, env):
        """Execute a command action.

        This will handle lists of commands as well as individual commands,
        because construction variable substitution may turn a single
        "command" into a list.  This means that this class can actually
        handle lists of commands, even though that's not how we use it
        externally.
        """
        from SCons.Subst import escape_list
        from SCons.Util import is_String, is_List, flatten

        try:
            shell = env['SHELL']
        except KeyError:
            raise SCons.Errors.UserError(
                'Missing SHELL construction variable.')

        try:
            spawn = env['SPAWN']
        except KeyError:
            raise SCons.Errors.UserError(
                'Missing SPAWN construction variable.')

        escape = env.get('ESCAPE', lambda x: x)

        try:
            ENV = env['ENV']
        except KeyError:
            global default_ENV
            if not default_ENV:
                import SCons.Environment
                default_ENV = SCons.Environment.Environment()['ENV']
            ENV = default_ENV

        # Ensure that the ENV values are all strings:
        for key, value in ENV.items():
            if not is_String(value):
                if is_List(value):
                    # If the value is a list, then we assume it is a
                    # path list, because that's a pretty common list-like
                    # value to stick in an environment variable:
                    value = flatten(value)
                    ENV[key] = string.join(map(str, value), os.pathsep)
                else:
                    # If it isn't a string or a list, then we just coerce
                    # it to a string, which is the proper way to handle
                    # Dir and File instances and will produce something
                    # reasonable for just about everything else:
                    ENV[key] = str(value)

        cmd_list, ignore, silent = self.process(target, map(rfile, source),
                                                env)

        # Use len() to filter out any "command" that's zero-length.
        for cmd_line in filter(len, cmd_list):
            # Escape the command line for the interpreter we are using.
            cmd_line = escape_list(cmd_line, escape)
            result = spawn(shell, escape, cmd_line[0], cmd_line, ENV)
            if not ignore and result:
                return result
        return 0
Example #23
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 _node(env, path):
    if is_String(path):
        return env.File(path)
    else:
        return path
Example #25
0
    # 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

    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
Example #26
0
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
Example #27
0
        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)
Example #28
0
        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)
Example #29
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
Example #30
0
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
Example #31
0
    # 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

    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
Example #32
0
    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)
Example #33
0
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
Example #34
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 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)
Example #35
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 == '$':
                # 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