This is a substitution builder much like the original SubstInFile 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:
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:
1 # File: subst.py
2 # Author: Brian A. Vanderburg II
3 # Purpose: SCons substitution in file mechanism
4 # Copyright: This file is placed in the public domain.
5 # Notice: Portions of this file are based on the original
6 # SubstInFile builder.
7 ########################################################################
8
9
10 # Requirements
11 ########################################################################
12 import re
13
14 from SCons.Script import *
15 import SCons.Errors
16
17
18 # This will replace any occurance of @keyname@ with the value of the
19 # key from the environment dictionary. @@ will produce a single @
20 ########################################################################
21
22 _searchre = re.compile('@(.*?)@')
23
24 def subst_file(target, source, data):
25 # Sub function
26 def subfn(mo, data=data):
27 key = mo.group(1)
28 if key == '':
29 return '@'
30 return data[key]
31
32 # Read file
33 f = open(source, 'rU')
34 try:
35 contents = f.read()
36 finally:
37 f.close()
38
39 # Substitute
40 contents = _searchre.sub(subfn, contents)
41
42 # Write file
43 f = open(target, 'wt')
44 try:
45 f.write(contents)
46 finally:
47 f.close()
48
49 def subst_keys(source):
50 keys = []
51
52 # Sub function
53 def subfn(mo):
54 key = mo.group(1)
55 if key != '':
56 keys.append(key)
57 return ''
58
59 # Read file
60 f = open(source, 'rU')
61 try:
62 contents = f.read()
63 finally:
64 f.close()
65
66 # Determine keys
67 _searchre.sub(subfn, contents)
68
69 return keys
70
71 def subst_in_file(target, source, env):
72 # What keys do the sources use
73 keys = []
74
75 for s in source:
76 skeys = subst_keys(str(s))
77 for k in skeys:
78 if not k in keys:
79 keys.append(k)
80
81 # Get these keys from the environment
82 d = dict()
83 for k in keys:
84 try:
85 v = env[k]
86 except:
87 raise SCons.Errors.UserError('SubstInFile key not found in environment: ' + k)
88
89 if callable(v):
90 d[k] = env.subst(v())
91 elif SCons.Util.is_String(v):
92 d[k] = env.subst(v)
93 else:
94 raise SCons.Errors.UserError('SubstInFile key must be a string or callable: ' + k)
95
96 # Substitute in the files
97 for (t, s) in zip(target, source):
98 subst_file(str(t), str(s), d)
99
100 return 0
101
102
103 def subst_string(target, source, env):
104 items = ['Substituting vars from %s to %s' % (str(s), str(t))
105 for (t, s) in zip(target, source)]
106
107 return '\n'.join(items)
108
109 def subst_emitter(target, source, env):
110 for (t, s) in zip(target, source):
111 # Get keys used
112 keys = subst_keys(str(s))
113
114 d = dict()
115 for k in keys:
116 try:
117 v = env[k]
118 except:
119 raise SCons.Errors.UserError('SubstInFile key not found in environment: ' + k)
120
121 if callable(v):
122 d[k] = env.subst(v())
123 elif SCons.Util.is_String(v):
124 d[k] = env.subst(v)
125
126 # Only the current target depends on this dictionary
127 Depends(t, SCons.Node.Python.Value(d))
128
129 return target, source
130
131 # Create builders
132 def TOOL_SUBST(env):
133 subst_in_file_action = SCons.Action.Action(subst_in_file, subst_string)
134 env['BUILDERS']['SubstInFile'] = Builder(action=subst_in_file_action,
135 emitter=subst_emitter)
