SubstInFileBuilder2

Michael Mera edited this page Nov 16, 2015 · 2 revisions

UPDATE: SCons 1.3.0 and later ships with the Substfile builder which should probably be used instead.

This is a substitution builder much like the original SubstInFileBuilder builder. This builder directly uses the environment as the substitution dictionary and emits only the keys a given target uses as dependencies of that target. The values in the environment to be substituted should either be strings or functions that return to a string. An environment substitution will occur on the result first before it is replaced in the file.

Example Usage:

#!python
env = Environment()
TOOL_SUBST(env)

def f():
    return "Test"

env['NAME'] = 'John Doe'
env['VERSION'] = '2.10.0'
env['FULLNAME'] = '$NAME $VERSION'
env['FUNCTION'] = f

env.SubstInFile('output', 'input')

Sample input file:

Name: @NAME@
Version: @VERSION@
Fullname: @FULLNAME@
Function: @FUNCTION@
Email: johndoe@@nowhere.com

Ouput of that file:

Name: John Doe
Version: 2.10.0
Fullname: John Doe 2.10.0
Function: Test
Email: johndoe@nowhere.com

The builder code:

#!python
# File:         subst.py
# Author:       Brian A. Vanderburg II
# Purpose:      SCons substitution in file mechanism
# Copyright:    This file is placed in the public domain.
# Notice:       Portions of this file are based on the original
#               SubstInFile builder.
########################################################################


# Requirements
########################################################################
import re

from SCons.Script import *
import SCons.Errors


# This will replace any occurance of @keyname@ with the value of the
# key from the environment dictionary.  @@ will produce a single @
########################################################################

_searchre = re.compile('@(.*?)@')

def subst_file(target, source, data):
    # subfunction
    def subfn(mo, data=data):
        key = mo.group(1)
        if key == '':
            return '@'
        return data[key]

    # read file
    f = open(source, 'rU')
    try:
        contents = f.read()
    finally:
        f.close()

    # substitute
    contents = _searchre.sub(subfn, contents)

    # Write file
    f = open(target, 'wt')
    try:
        f.write(contents)
    finally:
        f.close()

def subst_keys(source):
    keys = []

    # subfunction
    def subfn(mo):
        key = mo.group(1)
        if key != '':
            keys.append(key)
        return ''

    # read file
    f = open(source, 'rU')
    try:
        contents = f.read()
    finally:
        f.close()

    # determine keys
    _searchre.sub(subfn, contents)

    return keys

def subst_in_file(target, source, env):
    # What keys do the sources use
    keys = []

    for s in source:
        skeys = subst_keys(str(s))
        for k in skeys:
            if not k in keys:
                keys.append(k)

    # Get these keys from the environment
    d = dict()
    for k in keys:
        try:
            v = env[k]
        except:
            raise SCons.Errors.UserError('SubstInFile key not found in environment: ' + k)

        if callable(v):
            d[k] = env.subst(v())
        elif SCons.Util.is_String(v):
            d[k] = env.subst(v)
        else:
            raise SCons.Errors.UserError('SubstInFile key must be a string or callable: ' + k)

    # Substitute in the files
    for (t, s) in zip(target, source):
        subst_file(str(t), str(s), d)

    return 0


def subst_string(target, source, env):
    items = ['Substituting vars from %s to %s' % (str(s), str(t))
             for (t, s) in zip(target, source)]

    return '\n'.join(items)

def subst_emitter(target, source, env):
    for (t, s) in zip(target, source):
        # Get keys used
        keys = subst_keys(str(s))

        d = dict()
        for k in keys:
            try:
                v = env[k]
            except:
                raise SCons.Errors.UserError('SubstInFile key not found in environment: ' + k)

            if callable(v):
                d[k] = env.subst(v())
            elif SCons.Util.is_String(v):
                d[k] = env.subst(v)

        # Only the current target depends on this dictionary
        Depends(t, SCons.Node.Python.Value(d))

    return target, source

# create builders
def TOOL_SUBST(env):
    subst_in_file_action = SCons.Action.Action(subst_in_file, subst_string)
    env['BUILDERS']['SubstInFile'] = Builder(action=subst_in_file_action,
                                             emitter=subst_emitter)
Clone this wiki locally
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.