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