12eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenbergerimport re
22eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenbergerfrom SCons.Script import *  # the usual scons stuff you get in a SConscript
32eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenbergerimport collections
42eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger
52eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenbergerdef generate(env):
62eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger    """
72eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger    Add builders and construction variables for the
82eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger    SubstInFile tool.
92eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger
102eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger    Adds SubstInFile builder, which substitutes the keys->values of SUBST_DICT
112eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger    from the source to the target.
122eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger    The values of SUBST_DICT first have any construction variables expanded
132eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger    (its keys are not expanded).
142eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger    If a value of SUBST_DICT is a python callable function, it is called and
152eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger    the result is expanded as the value.
162eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger    If there's more than one source and more than one target, each target gets
172eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger    substituted from the corresponding source.
182eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger    """
192eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger    def do_subst_in_file(targetfile, sourcefile, dict):
202eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger        """Replace all instances of the keys of dict with their values.
212eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger        For example, if dict is {'%VERSION%': '1.2345', '%BASE%': 'MyProg'},
222eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger        then all instances of %VERSION% in the file will be replaced with 1.2345 etc.
232eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger        """
242eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger        try:
252eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger            f = open(sourcefile, 'rb')
262eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger            contents = f.read()
272eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger            f.close()
282eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger        except:
292eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger            raise SCons.Errors.UserError("Can't read source file %s"%sourcefile)
302eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger        for (k,v) in list(dict.items()):
312eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger            contents = re.sub(k, v, contents)
322eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger        try:
332eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger            f = open(targetfile, 'wb')
342eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger            f.write(contents)
352eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger            f.close()
362eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger        except:
372eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger            raise SCons.Errors.UserError("Can't write target file %s"%targetfile)
382eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger        return 0 # success
392eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger
402eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger    def subst_in_file(target, source, env):
412eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger        if 'SUBST_DICT' not in env:
422eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger            raise SCons.Errors.UserError("SubstInFile requires SUBST_DICT to be set.")
432eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger        d = dict(env['SUBST_DICT']) # copy it
442eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger        for (k,v) in list(d.items()):
452eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger            if isinstance(v, collections.Callable):
462eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger                d[k] = env.subst(v()).replace('\\','\\\\')
472eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger            elif SCons.Util.is_String(v):
482eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger                d[k] = env.subst(v).replace('\\','\\\\')
492eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger            else:
502eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger                raise SCons.Errors.UserError("SubstInFile: key %s: %s must be a string or callable"%(k, repr(v)))
512eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger        for (t,s) in zip(target, source):
522eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger            return do_subst_in_file(str(t), str(s), d)
532eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger
542eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger    def subst_in_file_string(target, source, env):
552eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger        """This is what gets printed on the console."""
562eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger        return '\n'.join(['Substituting vars from %s into %s'%(str(s), str(t))
572eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger                          for (t,s) in zip(target, source)])
582eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger
592eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger    def subst_emitter(target, source, env):
602eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger        """Add dependency from substituted SUBST_DICT to target.
612eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger        Returns original target, source tuple unchanged.
622eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger        """
632eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger        d = env['SUBST_DICT'].copy() # copy it
642eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger        for (k,v) in list(d.items()):
652eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger            if isinstance(v, collections.Callable):
662eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger                d[k] = env.subst(v())
672eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger            elif SCons.Util.is_String(v):
682eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger                d[k]=env.subst(v)
692eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger        Depends(target, SCons.Node.Python.Value(d))
702eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger        return target, source
712eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger
722eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger##    env.Append(TOOLS = 'substinfile')       # this should be automaticaly done by Scons ?!?
732eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger    subst_action = SCons.Action.Action( subst_in_file, subst_in_file_string )
742eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger    env['BUILDERS']['SubstInFile'] = Builder(action=subst_action, emitter=subst_emitter)
752eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger
762eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenbergerdef exists(env):
772eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger    """
782eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger    Make sure tool exists.
792eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger    """
802eb3b4d7ea1a3cbb0f8775de3b6d92c0a6152f9fDerek Sollenberger    return True
81