Merged revisions 2302-2362,2364-2452 via svnmerge from
[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 time
44 import types
45 import sys
46
47 import SCons.Action
48 import SCons.Builder
49 import SCons.CacheDir
50 import SCons.Environment
51 import SCons.PathList
52 import SCons.Subst
53 import SCons.Tool
54
55 # A placeholder for a default Environment (for fetching source files
56 # from source code management systems and the like).  This must be
57 # initialized later, after the top-level directory is set by the calling
58 # interface.
59 _default_env = None
60
61 # Lazily instantiate the default environment so the overhead of creating
62 # it doesn't apply when it's not needed.
63 def _fetch_DefaultEnvironment(*args, **kw):
64     """
65     Returns the already-created default construction environment.
66     """
67     global _default_env
68     return _default_env
69
70 def DefaultEnvironment(*args, **kw):
71     """
72     Initial public entry point for creating the default construction
73     Environment.
74
75     After creating the environment, we overwrite our name
76     (DefaultEnvironment) with the _fetch_DefaultEnvironment() function,
77     which more efficiently returns the initialized default construction
78     environment without checking for its existence.
79
80     (This function still exists with its _default_check because someone
81     else (*cough* Script/__init__.py *cough*) may keep a reference
82     to this function.  So we can't use the fully functional idiom of
83     having the name originally be a something that *only* creates the
84     construction environment and then overwrites the name.)
85     """
86     global _default_env
87     if not _default_env:
88         import SCons.Util
89         _default_env = apply(SCons.Environment.Environment, args, kw)
90         _default_env.TargetSignatures('source')
91         if SCons.Util.md5:
92             _default_env.SourceSignatures('MD5')
93         else:
94             _default_env.SourceSignatures('timestamp')
95         global DefaultEnvironment
96         DefaultEnvironment = _fetch_DefaultEnvironment
97         _default_env._CacheDir = SCons.CacheDir.Null()
98     return _default_env
99
100 # Emitters for setting the shared attribute on object files,
101 # and an action for checking that all of the source files
102 # going into a shared library are, in fact, shared.
103 def StaticObjectEmitter(target, source, env):
104     for tgt in target:
105         tgt.attributes.shared = None
106     return (target, source)
107
108 def SharedObjectEmitter(target, source, env):
109     for tgt in target:
110         tgt.attributes.shared = 1
111     return (target, source)
112
113 def SharedFlagChecker(source, target, env):
114     same = env.subst('$STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME')
115     if same == '0' or same == '' or same == 'False':
116         for src in source:
117             try:
118                 shared = src.attributes.shared
119             except AttributeError:
120                 shared = None
121             if not shared:
122                 raise SCons.Errors.UserError, "Source file: %s is static and is not compatible with shared target: %s" % (src, target[0])
123
124 SharedCheck = SCons.Action.Action(SharedFlagChecker, None)
125
126 # Some people were using these variable name before we made
127 # SourceFileScanner part of the public interface.  Don't break their
128 # SConscript files until we've given them some fair warning and a
129 # transition period.
130 CScan = SCons.Tool.CScanner
131 DScan = SCons.Tool.DScanner
132 LaTeXScan = SCons.Tool.LaTeXScanner
133 ObjSourceScan = SCons.Tool.SourceFileScanner
134 ProgScan = SCons.Tool.ProgramScanner
135
136 # These aren't really tool scanners, so they don't quite belong with
137 # the rest of those in Tool/__init__.py, but I'm not sure where else
138 # they should go.  Leave them here for now.
139 import SCons.Scanner.Dir
140 DirScanner = SCons.Scanner.Dir.DirScanner()
141 DirEntryScanner = SCons.Scanner.Dir.DirEntryScanner()
142
143 # Actions for common languages.
144 CAction = SCons.Action.Action("$CCCOM", "$CCCOMSTR")
145 ShCAction = SCons.Action.Action("$SHCCCOM", "$SHCCCOMSTR")
146 CXXAction = SCons.Action.Action("$CXXCOM", "$CXXCOMSTR")
147 ShCXXAction = SCons.Action.Action("$SHCXXCOM", "$SHCXXCOMSTR")
148
149 ASAction = SCons.Action.Action("$ASCOM", "$ASCOMSTR")
150 ASPPAction = SCons.Action.Action("$ASPPCOM", "$ASPPCOMSTR")
151
152 LinkAction = SCons.Action.Action("$LINKCOM", "$LINKCOMSTR")
153 ShLinkAction = SCons.Action.Action("$SHLINKCOM", "$SHLINKCOMSTR")
154
155 LdModuleLinkAction = SCons.Action.Action("$LDMODULECOM", "$LDMODULECOMSTR")
156
157 # Common tasks that we allow users to perform in platform-independent
158 # ways by creating ActionFactory instances.
159 ActionFactory = SCons.Action.ActionFactory
160
161 Chmod = ActionFactory(os.chmod,
162                       lambda dest, mode: 'Chmod("%s", 0%o)' % (dest, mode))
163
164 def copy_func(dest, src):
165     if SCons.Util.is_List(src) and os.path.isdir(dest):
166         for file in src:
167             shutil.copy(file, dest)
168         return 0
169     elif os.path.isfile(src):
170         return shutil.copy(src, dest)
171     else:
172         return shutil.copytree(src, dest, 1)
173
174 Copy = ActionFactory(copy_func,
175                      lambda dest, src: 'Copy("%s", "%s")' % (dest, src))
176
177 def delete_func(entry, must_exist=0):
178     if not must_exist and not os.path.exists(entry):
179         return None
180     if not os.path.exists(entry) or os.path.isfile(entry):
181         return os.unlink(entry)
182     else:
183         return shutil.rmtree(entry, 1)
184
185 def delete_strfunc(entry, must_exist=0):
186     return 'Delete("%s")' % entry
187
188 Delete = ActionFactory(delete_func, delete_strfunc)
189
190 Mkdir = ActionFactory(os.makedirs,
191                       lambda dir: 'Mkdir("%s")' % dir)
192
193 Move = ActionFactory(lambda dest, src: os.rename(src, dest),
194                      lambda dest, src: 'Move("%s", "%s")' % (dest, src))
195
196 def touch_func(file):
197     mtime = int(time.time())
198     if os.path.exists(file):
199         atime = os.path.getatime(file)
200     else:
201         open(file, 'w')
202         atime = mtime
203     return os.utime(file, (atime, mtime))
204
205 Touch = ActionFactory(touch_func,
206                       lambda file: 'Touch("%s")' % file)
207
208 # Internal utility functions
209
210 def _concat(prefix, list, suffix, env, f=lambda x: x, target=None, source=None):
211     """
212     Creates a new list from 'list' by first interpolating each element
213     in the list using the 'env' dictionary and then calling f on the
214     list, and finally calling _concat_ixes to concatenate 'prefix' and
215     'suffix' onto each element of the list.
216     """
217     if not list:
218         return list
219
220     if SCons.Util.is_List(list):
221         list = SCons.Util.flatten(list)
222
223     l = f(SCons.PathList.PathList(list).subst_path(env, target, source))
224     if not l is None:
225         list = l
226
227     return _concat_ixes(prefix, list, suffix, env)
228
229 def _concat_ixes(prefix, list, suffix, env):
230     """
231     Creates a new list from 'list' by concatenating the 'prefix' and
232     'suffix' arguments onto each element of the list.  A trailing space
233     on 'prefix' or leading space on 'suffix' will cause them to be put
234     into separate list elements rather than being concatenated.
235     """
236
237     result = []
238
239     # ensure that prefix and suffix are strings
240     prefix = str(env.subst(prefix, SCons.Subst.SUBST_RAW))
241     suffix = str(env.subst(suffix, SCons.Subst.SUBST_RAW))
242
243     for x in list:
244         if isinstance(x, SCons.Node.FS.File):
245             result.append(x)
246             continue
247         x = str(x)
248         if x:
249
250             if prefix:
251                 if prefix[-1] == ' ':
252                     result.append(prefix[:-1])
253                 elif x[:len(prefix)] != prefix:
254                     x = prefix + x
255
256             result.append(x)
257
258             if suffix:
259                 if suffix[0] == ' ':
260                     result.append(suffix[1:])
261                 elif x[-len(suffix):] != suffix:
262                     result[-1] = result[-1]+suffix
263
264     return result
265
266 def _stripixes(prefix, list, suffix, stripprefix, stripsuffix, env, c=None):
267     """
268     This is a wrapper around _concat()/_concat_ixes() that checks for the
269     existence of prefixes or suffixes on list elements and strips them
270     where it finds them.  This is used by tools (like the GNU linker)
271     that need to turn something like 'libfoo.a' into '-lfoo'.
272     """
273     
274     if not list:
275         return list
276
277     if not callable(c):
278         env_c = env['_concat']
279         if env_c != _concat and callable(env_c):
280             # There's a custom _concat() method in the construction
281             # environment, and we've allowed people to set that in
282             # the past (see test/custom-concat.py), so preserve the
283             # backwards compatibility.
284             c = env_c
285         else:
286             c = _concat_ixes
287     
288     if SCons.Util.is_List(list):
289         list = SCons.Util.flatten(list)
290
291     lsp = len(stripprefix)
292     lss = len(stripsuffix)
293     stripped = []
294     for l in SCons.PathList.PathList(list).subst_path(env, None, None):
295         if isinstance(l, SCons.Node.FS.File):
296             stripped.append(l)
297             continue
298         if not SCons.Util.is_String(l):
299             l = str(l)
300         if l[:lsp] == stripprefix:
301             l = l[lsp:]
302         if l[-lss:] == stripsuffix:
303             l = l[:-lss]
304         stripped.append(l)
305
306     return c(prefix, stripped, suffix, env)
307
308 def _defines(prefix, defs, suffix, env, c=_concat_ixes):
309     """A wrapper around _concat_ixes that turns a list or string
310     into a list of C preprocessor command-line definitions.
311     """
312     if SCons.Util.is_List(defs):
313         l = []
314         for d in defs:
315             if SCons.Util.is_List(d) or type(d) is types.TupleType:
316                 l.append(str(d[0]) + '=' + str(d[1]))
317             else:
318                 l.append(str(d))
319     elif SCons.Util.is_Dict(defs):
320         # The items in a dictionary are stored in random order, but
321         # if the order of the command-line options changes from
322         # invocation to invocation, then the signature of the command
323         # line will change and we'll get random unnecessary rebuilds.
324         # Consequently, we have to sort the keys to ensure a
325         # consistent order...
326         l = []
327         keys = defs.keys()
328         keys.sort()
329         for k in keys:
330             v = defs[k]
331             if v is None:
332                 l.append(str(k))
333             else:
334                 l.append(str(k) + '=' + str(v))
335     else:
336         l = [str(defs)]
337     return c(prefix, env.subst_path(l), suffix, env)
338     
339 class NullCmdGenerator:
340     """This is a callable class that can be used in place of other
341     command generators if you don't want them to do anything.
342
343     The __call__ method for this class simply returns the thing
344     you instantiated it with.
345
346     Example usage:
347     env["DO_NOTHING"] = NullCmdGenerator
348     env["LINKCOM"] = "${DO_NOTHING('$LINK $SOURCES $TARGET')}"
349     """
350
351     def __init__(self, cmd):
352         self.cmd = cmd
353
354     def __call__(self, target, source, env, for_signature=None):
355         return self.cmd
356
357 class Variable_Method_Caller:
358     """A class for finding a construction variable on the stack and
359     calling one of its methods.
360
361     We use this to support "construction variables" in our string
362     eval()s that actually stand in for methods--specifically, use
363     of "RDirs" in call to _concat that should actually execute the
364     "TARGET.RDirs" method.  (We used to support this by creating a little
365     "build dictionary" that mapped RDirs to the method, but this got in
366     the way of Memoizing construction environments, because we had to
367     create new environment objects to hold the variables.)
368     """
369     def __init__(self, variable, method):
370         self.variable = variable
371         self.method = method
372     def __call__(self, *args, **kw):
373         try: 1/0
374         except ZeroDivisionError: frame = sys.exc_info()[2].tb_frame
375         variable = self.variable
376         while frame:
377             if frame.f_locals.has_key(variable):
378                 v = frame.f_locals[variable]
379                 if v:
380                     method = getattr(v, self.method)
381                     return apply(method, args, kw)
382             frame = frame.f_back
383         return None
384
385 ConstructionEnvironment = {
386     'BUILDERS'      : {},
387     'SCANNERS'      : [],
388     'CONFIGUREDIR'  : '#/.sconf_temp',
389     'CONFIGURELOG'  : '#/config.log',
390     'CPPSUFFIXES'   : SCons.Tool.CSuffixes,
391     'DSUFFIXES'     : SCons.Tool.DSuffixes,
392     'ENV'           : {},
393     'IDLSUFFIXES'   : SCons.Tool.IDLSuffixes,
394     'LATEXSUFFIXES' : SCons.Tool.LaTeXSuffixes,
395     '_concat'       : _concat,
396     '_defines'      : _defines,
397     '_stripixes'    : _stripixes,
398     '_LIBFLAGS'     : '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}',
399     '_LIBDIRFLAGS'  : '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)',
400     '_CPPINCFLAGS'  : '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)',
401     '_CPPDEFFLAGS'  : '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}',
402     'TEMPFILE'      : NullCmdGenerator,
403     'Dir'           : Variable_Method_Caller('TARGET', 'Dir'),
404     'Dirs'          : Variable_Method_Caller('TARGET', 'Dirs'),
405     'File'          : Variable_Method_Caller('TARGET', 'File'),
406     'RDirs'         : Variable_Method_Caller('TARGET', 'RDirs'),
407 }