Merged revisions 1826-1882 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.Environment
50 import SCons.PathList
51 import SCons.Sig
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 DefaultEnvironment(*args, **kw):
64     global _default_env
65     if not _default_env:
66         _default_env = apply(SCons.Environment.Environment, args, kw)
67         _default_env._build_signature = 1
68         _default_env._calc_module = SCons.Sig.default_module
69     return _default_env
70
71 # Emitters for setting the shared attribute on object files,
72 # and an action for checking that all of the source files
73 # going into a shared library are, in fact, shared.
74 def StaticObjectEmitter(target, source, env):
75     for tgt in target:
76         tgt.attributes.shared = None
77     return (target, source)
78
79 def SharedObjectEmitter(target, source, env):
80     for tgt in target:
81         tgt.attributes.shared = 1
82     return (target, source)
83
84 def SharedFlagChecker(source, target, env):
85     same = env.subst('$STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME')
86     if same == '0' or same == '' or same == 'False':
87         for src in source:
88             try:
89                 shared = src.attributes.shared
90             except AttributeError:
91                 shared = None
92             if not shared:
93                 raise SCons.Errors.UserError, "Source file: %s is static and is not compatible with shared target: %s" % (src, target[0])
94
95 SharedCheck = SCons.Action.Action(SharedFlagChecker, None)
96
97 # Some people were using these variable name before we made
98 # SourceFileScanner part of the public interface.  Don't break their
99 # SConscript files until we've given them some fair warning and a
100 # transition period.
101 CScan = SCons.Tool.CScanner
102 DScan = SCons.Tool.DScanner
103 LaTeXScan = SCons.Tool.LaTeXScanner
104 ObjSourceScan = SCons.Tool.SourceFileScanner
105 ProgScan = SCons.Tool.ProgramScanner
106
107 # This isn't really a tool scanner, so it doesn't quite belong with
108 # the rest of those in Tool/__init__.py, but I'm not sure where else it
109 # should go.  Leave it here for now.
110 import SCons.Scanner.Dir
111 DirScanner = SCons.Scanner.Dir.DirScanner()
112 DirEntryScanner = SCons.Scanner.Dir.DirEntryScanner()
113
114 # Actions for common languages.
115 CAction = SCons.Action.Action("$CCCOM", "$CCCOMSTR")
116 ShCAction = SCons.Action.Action("$SHCCCOM", "$SHCCCOMSTR")
117 CXXAction = SCons.Action.Action("$CXXCOM", "$CXXCOMSTR")
118 ShCXXAction = SCons.Action.Action("$SHCXXCOM", "$SHCXXCOMSTR")
119
120 ASAction = SCons.Action.Action("$ASCOM", "$ASCOMSTR")
121 ASPPAction = SCons.Action.Action("$ASPPCOM", "$ASPPCOMSTR")
122
123 LinkAction = SCons.Action.Action("$LINKCOM", "$LINKCOMSTR")
124 ShLinkAction = SCons.Action.Action("$SHLINKCOM", "$SHLINKCOMSTR")
125
126 LdModuleLinkAction = SCons.Action.Action("$LDMODULECOM", "$LDMODULECOMSTR")
127
128 # Common tasks that we allow users to perform in platform-independent
129 # ways by creating ActionFactory instances.
130 ActionFactory = SCons.Action.ActionFactory
131
132 Chmod = ActionFactory(os.chmod,
133                       lambda dest, mode: 'Chmod("%s", 0%o)' % (dest, mode))
134
135 def copy_func(dest, src):
136     if os.path.isfile(src):
137         return shutil.copy(src, dest)
138     else:
139         return shutil.copytree(src, dest, 1)
140
141 Copy = ActionFactory(copy_func,
142                      lambda dest, src: 'Copy("%s", "%s")' % (dest, src))
143
144 def delete_func(entry, must_exist=0):
145     if not must_exist and not os.path.exists(entry):
146         return None
147     if not os.path.exists(entry) or os.path.isfile(entry):
148         return os.unlink(entry)
149     else:
150         return shutil.rmtree(entry, 1)
151
152 def delete_strfunc(entry, must_exist=0):
153     return 'Delete("%s")' % entry
154
155 Delete = ActionFactory(delete_func, delete_strfunc)
156
157 Mkdir = ActionFactory(os.makedirs,
158                       lambda dir: 'Mkdir("%s")' % dir)
159
160 Move = ActionFactory(lambda dest, src: os.rename(src, dest),
161                      lambda dest, src: 'Move("%s", "%s")' % (dest, src))
162
163 def touch_func(file):
164     mtime = int(time.time())
165     if os.path.exists(file):
166         atime = os.path.getatime(file)
167     else:
168         open(file, 'w')
169         atime = mtime
170     return os.utime(file, (atime, mtime))
171
172 Touch = ActionFactory(touch_func,
173                       lambda file: 'Touch("%s")' % file)
174
175 # Internal utility functions
176 def installFunc(dest, source, env):
177     """Install a source file or directory into a destination by copying,
178     (including copying permission/mode bits)."""
179
180     if os.path.isdir(source):
181         if os.path.exists(dest):
182             if not os.path.isdir(dest):
183                 raise SCons.Errors.UserError, "cannot overwrite non-directory `%s' with a directory `%s'" % (str(dest), str(source))
184         else:
185             parent = os.path.split(dest)[0]
186             if not os.path.exists(parent):
187                 os.makedirs(parent)
188         shutil.copytree(source, dest)
189     else:
190         shutil.copy2(source, dest)
191         st = os.stat(source)
192         os.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
193
194     return 0
195
196 def installStr(dest, source, env):
197     source = str(source)
198     if os.path.isdir(source):
199         type = 'directory'
200     else:
201         type = 'file'
202     return 'Install %s: "%s" as "%s"' % (type, source, dest)
203
204 def _concat(prefix, list, suffix, env, f=lambda x: x, target=None, source=None):
205     """
206     Creates a new list from 'list' by first interpolating each element
207     in the list using the 'env' dictionary and then calling f on the
208     list, and finally calling _concat_ixes to concatenate 'prefix' and
209     'suffix' onto each element of the list.
210     """
211     if not list:
212         return list
213
214     if SCons.Util.is_List(list):
215         list = SCons.Util.flatten(list)
216
217     l = f(SCons.PathList.PathList(list).subst_path(env, target, source))
218     if not l is None:
219         list = l
220
221     return _concat_ixes(prefix, list, suffix, env)
222
223 def _concat_ixes(prefix, list, suffix, env):
224     """
225     Creates a new list from 'list' by concatenating the 'prefix' and
226     'suffix' arguments onto each element of the list.  A trailing space
227     on 'prefix' or leading space on 'suffix' will cause them to be put
228     into separate list elements rather than being concatenated.
229     """
230
231     result = []
232
233     # ensure that prefix and suffix are strings
234     prefix = str(env.subst(prefix, SCons.Subst.SUBST_RAW))
235     suffix = str(env.subst(suffix, SCons.Subst.SUBST_RAW))
236
237     for x in list:
238         if isinstance(x, SCons.Node.FS.File):
239             result.append(x)
240             continue
241         x = str(x)
242         if x:
243
244             if prefix:
245                 if prefix[-1] == ' ':
246                     result.append(prefix[:-1])
247                 elif x[:len(prefix)] != prefix:
248                     x = prefix + x
249
250             result.append(x)
251
252             if suffix:
253                 if suffix[0] == ' ':
254                     result.append(suffix[1:])
255                 elif x[-len(suffix):] != suffix:
256                     result[-1] = result[-1]+suffix
257
258     return result
259
260 def _stripixes(prefix, list, suffix, stripprefix, stripsuffix, env, c=None):
261     """This is a wrapper around _concat() that checks for the existence
262     of prefixes or suffixes on list elements and strips them where it
263     finds them.  This is used by tools (like the GNU linker) that need
264     to turn something like 'libfoo.a' into '-lfoo'."""
265     
266     if not callable(c):
267         if callable(env["_concat"]):
268             c = env["_concat"]
269         else:
270             c = _concat
271     def f(list, sp=stripprefix, ss=stripsuffix):
272         result = []
273         for l in list:
274             if isinstance(l, SCons.Node.FS.File):
275                 result.append(l)
276                 continue
277             if not SCons.Util.is_String(l):
278                 l = str(l)
279             if l[:len(sp)] == sp:
280                 l = l[len(sp):]
281             if l[-len(ss):] == ss:
282                 l = l[:-len(ss)]
283             result.append(l)
284         return result
285     return c(prefix, list, suffix, env, f)
286
287 # This is an alternate _stripixes() function that passes all of our tests
288 # (as of 21 February 2007), like the current version above.  It's more
289 # straightforward because it does its manipulation directly, not using
290 # the funky f call-back function to _concat().  (In this respect it's
291 # like the updated _defines() function below.)
292 #
293 # The most convoluted thing is that it still uses a custom _concat()
294 # function if one was placed in the construction environment; there's
295 # a specific test for that functionality, but it might be worth getting
296 # rid of.
297 #
298 # Since this work was done while trying to get 0.97 out the door
299 # (just prior to 0.96.96), I decided to be cautious and leave the old
300 # function as is, to minimize the chance of other corner-case regressions.
301 # The updated version is captured here so we can uncomment it and start
302 # using it at a less sensitive time in the development cycle (or when
303 # it's clearly required to fix something).
304 #
305 #def _stripixes(prefix, list, suffix, stripprefix, stripsuffix, env, c=None):
306 #    """
307 #    This is a wrapper around _concat()/_concat_ixes() that checks for the
308 #    existence of prefixes or suffixes on list elements and strips them
309 #    where it finds them.  This is used by tools (like the GNU linker)
310 #    that need to turn something like 'libfoo.a' into '-lfoo'.
311 #    """
312 #    
313 #    if not list:
314 #        return list
315 #
316 #    if not callable(c):
317 #        env_c = env['_concat']
318 #        if env_c != _concat and callable(env_c):
319 #            # There's a custom _concat() method in the construction
320 #            # environment, and we've allowed people to set that in
321 #            # the past (see test/custom-concat.py), so preserve the
322 #            # backwards compatibility.
323 #            c = env_c
324 #        else:
325 #            c = _concat_ixes
326 #    
327 #    if SCons.Util.is_List(list):
328 #        list = SCons.Util.flatten(list)
329 #
330 #    lsp = len(stripprefix)
331 #    lss = len(stripsuffix)
332 #    stripped = []
333 #    for l in SCons.PathList.PathList(list).subst_path(env, None, None):
334 #        if isinstance(l, SCons.Node.FS.File):
335 #            stripped.append(l)
336 #            continue
337 #        if not SCons.Util.is_String(l):
338 #            l = str(l)
339 #        if l[:lsp] == stripprefix:
340 #            l = l[lsp:]
341 #        if l[-lss:] == stripsuffix:
342 #            l = l[:-lss]
343 #        stripped.append(l)
344 #
345 #    return c(prefix, stripped, suffix, env)
346
347 def _defines(prefix, defs, suffix, env, c=_concat_ixes):
348     """A wrapper around _concat_ixes that turns a list or string
349     into a list of C preprocessor command-line definitions.
350     """
351     if SCons.Util.is_List(defs):
352         l = []
353         for d in defs:
354             if SCons.Util.is_List(d) or type(d) is types.TupleType:
355                 l.append(str(d[0]) + '=' + str(d[1]))
356             else:
357                 l.append(str(d))
358     elif SCons.Util.is_Dict(defs):
359         # The items in a dictionary are stored in random order, but
360         # if the order of the command-line options changes from
361         # invocation to invocation, then the signature of the command
362         # line will change and we'll get random unnecessary rebuilds.
363         # Consequently, we have to sort the keys to ensure a
364         # consistent order...
365         l = []
366         keys = defs.keys()
367         keys.sort()
368         for k in keys:
369             v = defs[k]
370             if v is None:
371                 l.append(str(k))
372             else:
373                 l.append(str(k) + '=' + str(v))
374     else:
375         l = [str(defs)]
376     return c(prefix, env.subst_path(l), suffix, env)
377     
378 class NullCmdGenerator:
379     """This is a callable class that can be used in place of other
380     command generators if you don't want them to do anything.
381
382     The __call__ method for this class simply returns the thing
383     you instantiated it with.
384
385     Example usage:
386     env["DO_NOTHING"] = NullCmdGenerator
387     env["LINKCOM"] = "${DO_NOTHING('$LINK $SOURCES $TARGET')}"
388     """
389
390     def __init__(self, cmd):
391         self.cmd = cmd
392
393     def __call__(self, target, source, env, for_signature=None):
394         return self.cmd
395
396 class Variable_Method_Caller:
397     """A class for finding a construction variable on the stack and
398     calling one of its methods.
399
400     We use this to support "construction variables" in our string
401     eval()s that actually stand in for methods--specifically, use
402     of "RDirs" in call to _concat that should actually execute the
403     "TARGET.RDirs" method.  (We used to support this by creating a little
404     "build dictionary" that mapped RDirs to the method, but this got in
405     the way of Memoizing construction environments, because we had to
406     create new environment objects to hold the variables.)
407     """
408     def __init__(self, variable, method):
409         self.variable = variable
410         self.method = method
411     def __call__(self, *args, **kw):
412         try: 1/0
413         except ZeroDivisionError: frame = sys.exc_info()[2].tb_frame
414         variable = self.variable
415         while frame:
416             if frame.f_locals.has_key(variable):
417                 v = frame.f_locals[variable]
418                 if v:
419                     method = getattr(v, self.method)
420                     return apply(method, args, kw)
421             frame = frame.f_back
422         return None
423
424 ConstructionEnvironment = {
425     'BUILDERS'      : {},
426     'SCANNERS'      : [],
427     'CONFIGUREDIR'  : '#/.sconf_temp',
428     'CONFIGURELOG'  : '#/config.log',
429     'CPPSUFFIXES'   : SCons.Tool.CSuffixes,
430     'DSUFFIXES'     : SCons.Tool.DSuffixes,
431     'ENV'           : {},
432     'IDLSUFFIXES'   : SCons.Tool.IDLSuffixes,
433     'INSTALL'       : installFunc,
434     'INSTALLSTR'    : installStr,
435     '_installStr'   : installStr,
436     'LATEXSUFFIXES' : SCons.Tool.LaTeXSuffixes,
437     '_concat'       : _concat,
438     '_defines'      : _defines,
439     '_stripixes'    : _stripixes,
440     '_LIBFLAGS'     : '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}',
441     '_LIBDIRFLAGS'  : '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)',
442     '_CPPINCFLAGS'  : '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)',
443     '_CPPDEFFLAGS'  : '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}',
444     'TEMPFILE'      : NullCmdGenerator,
445     'Dir'           : Variable_Method_Caller('TARGET', 'Dir'),
446     'Dirs'          : Variable_Method_Caller('TARGET', 'Dirs'),
447     'File'          : Variable_Method_Caller('TARGET', 'File'),
448     'RDirs'         : Variable_Method_Caller('TARGET', 'RDirs'),
449 }