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