Accumulated documentation changes.
[scons.git] / src / engine / SCons / Defaults.py
1 """SCons.Defaults
2
3 Builders and other things for the local site.  Here's where we'll
4 duplicate the functionality of autoconf until we move it into the
5 installation procedure or use something like qmconf.
6
7 The code that reads the registry to find MSVC components was borrowed
8 from distutils.msvccompiler.
9
10 """
11
12 #
13 # __COPYRIGHT__
14 #
15 # Permission is hereby granted, free of charge, to any person obtaining
16 # a copy of this software and associated documentation files (the
17 # "Software"), to deal in the Software without restriction, including
18 # without limitation the rights to use, copy, modify, merge, publish,
19 # distribute, sublicense, and/or sell copies of the Software, and to
20 # permit persons to whom the Software is furnished to do so, subject to
21 # the following conditions:
22 #
23 # The above copyright notice and this permission notice shall be included
24 # in all copies or substantial portions of the Software.
25 #
26 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
27 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
28 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 #
34
35 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
36
37
38
39 import os
40 import os.path
41 import shutil
42 import stat
43 import string
44 import time
45 import types
46 import sys
47
48 import SCons.Action
49 import SCons.Builder
50 import SCons.Environment
51 import SCons.Tool
52 import SCons.Sig
53
54 # A placeholder for a default Environment (for fetching source files
55 # from source code management systems and the like).  This must be
56 # initialized later, after the top-level directory is set by the calling
57 # interface.
58 _default_env = None
59
60 # Lazily instantiate the default environment so the overhead of creating
61 # it doesn't apply when it's not needed.
62 def DefaultEnvironment(*args, **kw):
63     global _default_env
64     if not _default_env:
65         _default_env = apply(SCons.Environment.Environment, args, kw)
66         _default_env._build_signature = 1
67         _default_env._calc_module = SCons.Sig.default_module
68     return _default_env
69
70 # Emitters for setting the shared attribute on object files,
71 # and an action for checking that all of the source files
72 # going into a shared library are, in fact, shared.
73 def StaticObjectEmitter(target, source, env):
74     for tgt in target:
75         tgt.attributes.shared = None
76     return (target, source)
77
78 def SharedObjectEmitter(target, source, env):
79     for tgt in target:
80         tgt.attributes.shared = 1
81     return (target, source)
82
83 def SharedFlagChecker(source, target, env):
84     same = env.subst('$STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME')
85     if same == '0' or same == '' or same == 'False':
86         for src in source:
87             try:
88                 shared = src.attributes.shared
89             except AttributeError:
90                 shared = None
91             if not shared:
92                 raise SCons.Errors.UserError, "Source file: %s is static and is not compatible with shared target: %s" % (src, target[0])
93
94 SharedCheck = SCons.Action.Action(SharedFlagChecker, None)
95
96 # Some people were using these variable name before we made
97 # SourceFileScanner part of the public interface.  Don't break their
98 # SConscript files until we've given them some fair warning and a
99 # transition period.
100 CScan = SCons.Tool.CScanner
101 DScan = SCons.Tool.DScanner
102 ObjSourceScan = SCons.Tool.SourceFileScanner
103 ProgScan = SCons.Tool.ProgramScanner
104
105 # This isn't really a tool scanner, so it doesn't quite belong with
106 # the rest of those in Tool/__init__.py, but I'm not sure where else it
107 # should go.  Leave it here for now.
108 import SCons.Scanner.Dir
109 DirScanner = SCons.Scanner.Dir.DirScanner()
110
111 # Actions for common languages.
112 CAction = SCons.Action.Action("$CCCOM", "$CCCOMSTR")
113 ShCAction = SCons.Action.Action("$SHCCCOM", "$SHCCCOMSTR")
114 CXXAction = SCons.Action.Action("$CXXCOM", "$CXXCOMSTR")
115 ShCXXAction = SCons.Action.Action("$SHCXXCOM", "$SHCXXCOMSTR")
116
117 ASAction = SCons.Action.Action("$ASCOM", "$ASCOMSTR")
118 ASPPAction = SCons.Action.Action("$ASPPCOM", "$ASPPCOMSTR")
119
120 LinkAction = SCons.Action.Action("$LINKCOM", "$LINKCOMSTR")
121 ShLinkAction = SCons.Action.Action("$SHLINKCOM", "$SHLINKCOMSTR")
122
123 LdModuleLinkAction = SCons.Action.Action("$LDMODULECOM", "$LDMODULECOMSTR")
124
125 def DVI():
126     """Common function to generate a DVI file Builder."""
127     return SCons.Builder.Builder(action = {},
128                                  # The suffix is not configurable via a
129                                  # construction variable like $DVISUFFIX
130                                  # because the output file name is
131                                  # hard-coded within TeX.
132                                  suffix = '.dvi')
133
134 def PDF():
135     """A function for generating the PDF Builder."""
136     return SCons.Builder.Builder(action = { },
137                                  prefix = '$PDFPREFIX',
138                                  suffix = '$PDFSUFFIX')
139
140 # Common tasks that we allow users to perform in platform-independent
141 # ways by creating ActionFactory instances.
142 ActionFactory = SCons.Action.ActionFactory
143
144 Chmod = ActionFactory(os.chmod,
145                       lambda dest, mode: 'Chmod("%s", 0%o)' % (dest, mode))
146
147 def copy_func(dest, src):
148     if os.path.isfile(src):
149         return shutil.copy(src, dest)
150     else:
151         return shutil.copytree(src, dest, 1)
152
153 Copy = ActionFactory(copy_func,
154                      lambda dest, src: 'Copy("%s", "%s")' % (dest, src))
155
156 def delete_func(entry, must_exist=0):
157     if not must_exist and not os.path.exists(entry):
158         return None
159     if not os.path.exists(entry) or os.path.isfile(entry):
160         return os.unlink(entry)
161     else:
162         return shutil.rmtree(entry, 1)
163
164 def delete_strfunc(entry, must_exist=0):
165     return 'Delete("%s")' % entry
166
167 Delete = ActionFactory(delete_func, delete_strfunc)
168
169 Mkdir = ActionFactory(os.makedirs,
170                       lambda dir: 'Mkdir("%s")' % dir)
171
172 Move = ActionFactory(lambda dest, src: os.rename(src, dest),
173                      lambda dest, src: 'Move("%s", "%s")' % (dest, src))
174
175 def touch_func(file):
176     mtime = int(time.time())
177     if os.path.exists(file):
178         atime = os.path.getatime(file)
179     else:
180         open(file, 'w')
181         atime = mtime
182     return os.utime(file, (atime, mtime))
183
184 Touch = ActionFactory(touch_func,
185                       lambda file: 'Touch("%s")' % file)
186
187 # Internal utility functions
188 def copyFunc(dest, source, env):
189     """Install a source file into a destination by copying it (and its
190     permission/mode bits)."""
191     shutil.copy2(source, dest)
192     st = os.stat(source)
193     os.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
194     return 0
195
196 def _concat(prefix, list, suffix, env, f=lambda x: x, target=None):
197     """Creates a new list from 'list' by first interpolating each
198     element in the list using the 'env' dictionary and then calling f
199     on the list, and finally concatenating 'prefix' and 'suffix' onto
200     each element of the list. A trailing space on 'prefix' or leading
201     space on 'suffix' will cause them to be put into separate list
202     elements rather than being concatenated."""
203     
204     if not list:
205         return list
206
207     if SCons.Util.is_List(list):
208         list = SCons.Util.flatten(list)
209     list = f(env.subst_path(list, target=target))
210
211     result = []
212
213     # ensure that prefix and suffix are strings
214     prefix = str(env.subst(prefix, SCons.Util.SUBST_RAW))
215     suffix = str(env.subst(suffix, SCons.Util.SUBST_RAW))
216
217     for x in list:
218         if isinstance(x, SCons.Node.FS.File):
219             result.append(x)
220             continue
221         x = str(x)
222         if x:
223
224             if prefix:
225                 if prefix[-1] == ' ':
226                     result.append(prefix[:-1])
227                 elif x[:len(prefix)] != prefix:
228                     x = prefix + x
229
230             result.append(x)
231
232             if suffix:
233                 if suffix[0] == ' ':
234                     result.append(suffix[1:])
235                 elif x[-len(suffix):] != suffix:
236                     result[-1] = result[-1]+suffix
237
238     return result
239
240 def _stripixes(prefix, list, suffix, stripprefix, stripsuffix, env, c=None):
241     """This is a wrapper around _concat() that checks for the existence
242     of prefixes or suffixes on list elements and strips them where it
243     finds them.  This is used by tools (like the GNU linker) that need
244     to turn something like 'libfoo.a' into '-lfoo'."""
245     
246     if not callable(c):
247         if callable(env["_concat"]):
248             c = env["_concat"]
249         else:
250             c = _concat
251     def f(list, sp=stripprefix, ss=stripsuffix):
252         result = []
253         for l in list:
254             if isinstance(l, SCons.Node.FS.File):
255                 result.append(l)
256                 continue
257             if not SCons.Util.is_String(l):
258                 l = str(l)
259             if l[:len(sp)] == sp:
260                 l = l[len(sp):]
261             if l[-len(ss):] == ss:
262                 l = l[:-len(ss)]
263             result.append(l)
264         return result
265     return c(prefix, list, suffix, env, f)
266
267 def _defines(prefix, defs, suffix, env, c=_concat):
268     """A wrapper around _concat that turns a list or string
269     into a list of C preprocessor command-line definitions.
270     """
271     if SCons.Util.is_List(defs):
272         l = []
273         for d in defs:
274             if SCons.Util.is_List(d) or type(d) is types.TupleType:
275                 l.append(str(d[0]) + '=' + str(d[1]))
276             else:
277                 l.append(str(d))
278     elif SCons.Util.is_Dict(defs):
279         # The items in a dictionary are stored in random order, but
280         # if the order of the command-line options changes from
281         # invocation to invocation, then the signature of the command
282         # line will change and we'll get random unnecessary rebuilds.
283         # Consequently, we have to sort the keys to ensure a
284         # consistent order...
285         l = []
286         keys = defs.keys()
287         keys.sort()
288         for k in keys:
289             v = defs[k]
290             if v is None:
291                 l.append(str(k))
292             else:
293                 l.append(str(k) + '=' + str(v))
294     else:
295         l = [str(defs)]
296     return c(prefix, l, suffix, env)
297     
298 class NullCmdGenerator:
299     """This is a callable class that can be used in place of other
300     command generators if you don't want them to do anything.
301
302     The __call__ method for this class simply returns the thing
303     you instantiated it with.
304
305     Example usage:
306     env["DO_NOTHING"] = NullCmdGenerator
307     env["LINKCOM"] = "${DO_NOTHING('$LINK $SOURCES $TARGET')}"
308     """
309
310     def __init__(self, cmd):
311         self.cmd = cmd
312
313     def __call__(self, target, source, env, for_signature=None):
314         return self.cmd
315
316 class Variable_Method_Caller:
317     """A class for finding a construction variable on the stack and
318     calling one of its methods.
319
320     We use this to support "construction variables" in our string
321     eval()s that actually stand in for methods--specifically, use
322     of "RDirs" in call to _concat that should actually execute the
323     "TARGET.RDirs" method.  (We used to support this by creating a little
324     "build dictionary" that mapped RDirs to the method, but this got in
325     the way of Memoizing construction environments, because we had to
326     create new environment objects to hold the variables.)
327     """
328     def __init__(self, variable, method):
329         self.variable = variable
330         self.method = method
331     def __call__(self, *args, **kw):
332         try: 1/0
333         except ZeroDivisionError: frame = sys.exc_info()[2].tb_frame
334         variable = None
335         while frame:
336             try:
337                 variable = frame.f_locals[self.variable]
338             except KeyError:
339                 pass
340             frame = frame.f_back
341         if variable is None:
342             return None
343         method = getattr(variable, self.method)
344         return apply(method, args, kw)
345
346 ConstructionEnvironment = {
347     'BUILDERS'      : {},
348     'SCANNERS'      : [],
349     'CPPSUFFIXES'   : SCons.Tool.CSuffixes,
350     'DSUFFIXES'     : SCons.Tool.DSuffixes,
351     'IDLSUFFIXES'   : SCons.Tool.IDLSuffixes,
352     'PDFPREFIX'     : '',
353     'PDFSUFFIX'     : '.pdf',
354     'PSPREFIX'      : '',
355     'PSSUFFIX'      : '.ps',
356     'ENV'           : {},
357     'INSTALL'       : copyFunc,
358     '_concat'       : _concat,
359     '_defines'      : _defines,
360     '_stripixes'    : _stripixes,
361     '_LIBFLAGS'     : '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}',
362     '_LIBDIRFLAGS'  : '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET)} $)',
363     '_CPPINCFLAGS'  : '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET)} $)',
364     '_CPPDEFFLAGS'  : '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}',
365     'TEMPFILE'      : NullCmdGenerator,
366     'Dir'           : Variable_Method_Caller('TARGET', 'Dir'),
367     'File'          : Variable_Method_Caller('TARGET', 'File'),
368     'RDirs'         : Variable_Method_Caller('TARGET', 'RDirs'),
369 }