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