minimize tool initialization in tests
[scons.git] / SCons / Defaults.py
bloba5d49fc76ffe4c8581040efa255621566f1b278d
1 # MIT License
3 # Copyright The SCons Foundation
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
13 # The above copyright notice and this permission notice shall be included
14 # in all copies or substantial portions of the Software.
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
17 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
18 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 """Builders and other things for the local site.
27 Here's where we'll duplicate the functionality of autoconf until we
28 move it into the installation procedure or use something like qmconf.
30 The code that reads the registry to find MSVC components was borrowed
31 from distutils.msvccompiler.
32 """
34 import os
35 import shutil
36 import stat
37 import sys
38 import time
39 from typing import List, Callable
41 import SCons.Action
42 import SCons.Builder
43 import SCons.CacheDir
44 import SCons.Environment
45 import SCons.Errors
46 import SCons.PathList
47 import SCons.Scanner.Dir
48 import SCons.Subst
49 import SCons.Tool
50 from SCons.Util import is_List, is_String, is_Sequence, is_Tuple, is_Dict, flatten
52 # A placeholder for a default Environment (for fetching source files
53 # from source code management systems and the like). This must be
54 # initialized later, after the top-level directory is set by the calling
55 # interface.
56 _default_env = None
59 # Lazily instantiate the default environment so the overhead of creating
60 # it doesn't apply when it's not needed.
61 def _fetch_DefaultEnvironment(*args, **kwargs):
62 """Returns the already-created default construction environment."""
63 return _default_env
66 def DefaultEnvironment(*args, **kwargs):
67 """Construct the global ("default") construction environment.
69 The environment is provisioned with the values from *kwargs*.
71 After the environment is created, this function is replaced with
72 a reference to :func:`_fetch_DefaultEnvironment` which efficiently
73 returns the initialized default construction environment without
74 checking for its existence.
76 Historically, some parts of the code held references to this function.
77 Thus it still has the existence check for :data:`_default_env` rather
78 than just blindly creating the environment and overwriting itself.
79 """
80 global _default_env
81 if not _default_env:
82 _default_env = SCons.Environment.Environment(*args, **kwargs)
83 _default_env.Decider('content')
84 global DefaultEnvironment
85 DefaultEnvironment = _fetch_DefaultEnvironment
86 _default_env._CacheDir_path = None
87 return _default_env
90 # Emitters for setting the shared attribute on object files,
91 # and an action for checking that all of the source files
92 # going into a shared library are, in fact, shared.
93 def StaticObjectEmitter(target, source, env):
94 for tgt in target:
95 tgt.attributes.shared = False
96 return target, source
99 def SharedObjectEmitter(target, source, env):
100 for tgt in target:
101 tgt.attributes.shared = 1
102 return target, source
105 def SharedFlagChecker(source, target, env):
106 same = env.subst('$STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME')
107 if same == '0' or same == '' or same == 'False':
108 for src in source:
109 try:
110 shared = src.attributes.shared
111 except AttributeError:
112 shared = False
113 if not shared:
114 raise SCons.Errors.UserError(
115 "Source file: %s is static and is not compatible with shared target: %s" % (src, target[0]))
118 SharedCheck = SCons.Action.Action(SharedFlagChecker, None)
120 # Some people were using these variable name before we made
121 # SourceFileScanner part of the public interface. Don't break their
122 # SConscript files until we've given them some fair warning and a
123 # transition period.
124 CScan = SCons.Tool.CScanner
125 DScan = SCons.Tool.DScanner
126 LaTeXScan = SCons.Tool.LaTeXScanner
127 ObjSourceScan = SCons.Tool.SourceFileScanner
128 ProgScan = SCons.Tool.ProgramScanner
130 # These aren't really tool scanners, so they don't quite belong with
131 # the rest of those in Tool/__init__.py, but I'm not sure where else
132 # they should go. Leave them here for now.
134 DirScanner = SCons.Scanner.Dir.DirScanner()
135 DirEntryScanner = SCons.Scanner.Dir.DirEntryScanner()
137 # Actions for common languages.
138 CAction = SCons.Action.Action("$CCCOM", "$CCCOMSTR")
139 ShCAction = SCons.Action.Action("$SHCCCOM", "$SHCCCOMSTR")
140 CXXAction = SCons.Action.Action("$CXXCOM", "$CXXCOMSTR")
141 ShCXXAction = SCons.Action.Action("$SHCXXCOM", "$SHCXXCOMSTR")
143 DAction = SCons.Action.Action("$DCOM", "$DCOMSTR")
144 ShDAction = SCons.Action.Action("$SHDCOM", "$SHDCOMSTR")
146 ASAction = SCons.Action.Action("$ASCOM", "$ASCOMSTR")
147 ASPPAction = SCons.Action.Action("$ASPPCOM", "$ASPPCOMSTR")
149 LinkAction = SCons.Action.Action("$LINKCOM", "$LINKCOMSTR")
150 ShLinkAction = SCons.Action.Action("$SHLINKCOM", "$SHLINKCOMSTR")
152 LdModuleLinkAction = SCons.Action.Action("$LDMODULECOM", "$LDMODULECOMSTR")
154 # Common tasks that we allow users to perform in platform-independent
155 # ways by creating ActionFactory instances.
156 ActionFactory = SCons.Action.ActionFactory
159 def get_paths_str(dest) -> str:
160 """Generates a string from *dest* for use in a strfunction.
162 If *dest* is a list, manually converts each elem to a string.
164 def quote(arg) -> str:
165 return f'"{arg}"'
167 if is_List(dest):
168 elem_strs = [quote(d) for d in dest]
169 return f'[{", ".join(elem_strs)}]'
170 else:
171 return quote(dest)
174 permission_dic = {
175 'u': {
176 'r': stat.S_IRUSR,
177 'w': stat.S_IWUSR,
178 'x': stat.S_IXUSR
180 'g': {
181 'r': stat.S_IRGRP,
182 'w': stat.S_IWGRP,
183 'x': stat.S_IXGRP
185 'o': {
186 'r': stat.S_IROTH,
187 'w': stat.S_IWOTH,
188 'x': stat.S_IXOTH
193 def chmod_func(dest, mode) -> None:
194 """Implementation of the Chmod action function.
196 *mode* can be either an integer (normally expressed in octal mode,
197 as in 0o755) or a string following the syntax of the POSIX chmod
198 command (for example "ugo+w"). The latter must be converted, since
199 the underlying Python only takes the numeric form.
201 from string import digits
202 SCons.Node.FS.invalidate_node_memos(dest)
203 if not is_List(dest):
204 dest = [dest]
205 if is_String(mode) and 0 not in [i in digits for i in mode]:
206 mode = int(mode, 8)
207 if not is_String(mode):
208 for element in dest:
209 os.chmod(str(element), mode)
210 else:
211 mode = str(mode)
212 for operation in mode.split(","):
213 if "=" in operation:
214 operator = "="
215 elif "+" in operation:
216 operator = "+"
217 elif "-" in operation:
218 operator = "-"
219 else:
220 raise SyntaxError("Could not find +, - or =")
221 operation_list = operation.split(operator)
222 if len(operation_list) != 2:
223 raise SyntaxError("More than one operator found")
224 user = operation_list[0].strip().replace("a", "ugo")
225 permission = operation_list[1].strip()
226 new_perm = 0
227 for u in user:
228 for p in permission:
229 try:
230 new_perm = new_perm | permission_dic[u][p]
231 except KeyError:
232 raise SyntaxError("Unrecognized user or permission format")
233 for element in dest:
234 curr_perm = os.stat(str(element)).st_mode
235 if operator == "=":
236 os.chmod(str(element), new_perm)
237 elif operator == "+":
238 os.chmod(str(element), curr_perm | new_perm)
239 elif operator == "-":
240 os.chmod(str(element), curr_perm & ~new_perm)
243 def chmod_strfunc(dest, mode) -> str:
244 """strfunction for the Chmod action function."""
245 if not is_String(mode):
246 return f'Chmod({get_paths_str(dest)}, {mode:#o})'
247 else:
248 return f'Chmod({get_paths_str(dest)}, "{mode}")'
252 Chmod = ActionFactory(chmod_func, chmod_strfunc)
256 def copy_func(dest, src, symlinks: bool=True) -> int:
257 """Implementation of the Copy action function.
259 Copies *src* to *dest*. If *src* is a list, *dest* must be
260 a directory, or not exist (will be created).
262 Since Python :mod:`shutil` methods, which know nothing about
263 SCons Nodes, will be called to perform the actual copying,
264 args are converted to strings first.
266 If *symlinks* evaluates true, then a symbolic link will be
267 shallow copied and recreated as a symbolic link; otherwise, copying
268 a symbolic link will be equivalent to copying the symbolic link's
269 final target regardless of symbolic link depth.
272 dest = str(dest)
273 src = [str(n) for n in src] if is_List(src) else str(src)
275 SCons.Node.FS.invalidate_node_memos(dest)
276 if is_List(src):
277 # this fails only if dest exists and is not a dir
278 try:
279 os.makedirs(dest, exist_ok=True)
280 except FileExistsError:
281 raise SCons.Errors.BuildError(
282 errstr=(
283 'Error: Copy() called with a list of sources, '
284 'which requires target to be a directory, '
285 f'but "{dest}" is not a directory.'
288 for file in src:
289 shutil.copy2(file, dest)
290 return 0
292 elif os.path.islink(src):
293 if symlinks:
294 try:
295 os.symlink(os.readlink(src), dest)
296 except FileExistsError:
297 raise SCons.Errors.BuildError(
298 errstr=(
299 f'Error: Copy() called to create symlink at "{dest}",'
300 ' but a file already exists at that location.'
303 return 0
305 return copy_func(dest, os.path.realpath(src))
307 elif os.path.isfile(src):
308 shutil.copy2(src, dest)
309 return 0
311 else:
312 shutil.copytree(src, dest, symlinks)
313 return 0
316 def copy_strfunc(dest, src, symlinks: bool=True) -> str:
317 """strfunction for the Copy action function."""
318 return f'Copy({get_paths_str(dest)}, {get_paths_str(src)})'
321 Copy = ActionFactory(copy_func, copy_strfunc)
324 def delete_func(dest, must_exist: bool=False) -> None:
325 """Implementation of the Delete action function.
327 Lets the Python :func:`os.unlink` raise an error if *dest* does not exist,
328 unless *must_exist* evaluates false (the default).
330 SCons.Node.FS.invalidate_node_memos(dest)
331 if not is_List(dest):
332 dest = [dest]
333 for entry in dest:
334 entry = str(entry)
335 # os.path.exists returns False with broken links that exist
336 entry_exists = os.path.exists(entry) or os.path.islink(entry)
337 if not entry_exists and not must_exist:
338 continue
339 # os.path.isdir returns True when entry is a link to a dir
340 if os.path.isdir(entry) and not os.path.islink(entry):
341 shutil.rmtree(entry, True)
342 continue
343 os.unlink(entry)
346 def delete_strfunc(dest, must_exist: bool=False) -> str:
347 """strfunction for the Delete action function."""
348 return f'Delete({get_paths_str(dest)})'
351 Delete = ActionFactory(delete_func, delete_strfunc)
354 def mkdir_func(dest) -> None:
355 """Implementation of the Mkdir action function."""
356 SCons.Node.FS.invalidate_node_memos(dest)
357 if not is_List(dest):
358 dest = [dest]
359 for entry in dest:
360 os.makedirs(str(entry), exist_ok=True)
363 Mkdir = ActionFactory(mkdir_func, lambda _dir: f'Mkdir({get_paths_str(_dir)})')
366 def move_func(dest, src) -> None:
367 """Implementation of the Move action function."""
368 SCons.Node.FS.invalidate_node_memos(dest)
369 SCons.Node.FS.invalidate_node_memos(src)
370 shutil.move(src, dest)
373 Move = ActionFactory(
374 move_func, lambda dest, src: f'Move("{dest}", "{src}")', convert=str
378 def touch_func(dest) -> None:
379 """Implementation of the Touch action function."""
380 SCons.Node.FS.invalidate_node_memos(dest)
381 if not is_List(dest):
382 dest = [dest]
383 for file in dest:
384 file = str(file)
385 mtime = int(time.time())
386 if os.path.exists(file):
387 atime = os.path.getatime(file)
388 else:
389 with open(file, 'w'):
390 atime = mtime
391 os.utime(file, (atime, mtime))
394 Touch = ActionFactory(touch_func, lambda file: f'Touch({get_paths_str(file)})')
397 # Internal utility functions
399 # pylint: disable-msg=too-many-arguments
400 def _concat(prefix, items_iter, suffix, env, f=lambda x: x, target=None, source=None, affect_signature: bool=True):
402 Creates a new list from 'items_iter' by first interpolating each element
403 in the list using the 'env' dictionary and then calling f on the
404 list, and finally calling _concat_ixes to concatenate 'prefix' and
405 'suffix' onto each element of the list.
408 if not items_iter:
409 return items_iter
411 l = f(SCons.PathList.PathList(items_iter).subst_path(env, target, source))
412 if l is not None:
413 items_iter = l
415 if not affect_signature:
416 value = ['$(']
417 else:
418 value = []
419 value += _concat_ixes(prefix, items_iter, suffix, env)
421 if not affect_signature:
422 value += ["$)"]
424 return value
425 # pylint: enable-msg=too-many-arguments
428 def _concat_ixes(prefix, items_iter, suffix, env):
430 Creates a new list from 'items_iter' by concatenating the 'prefix' and
431 'suffix' arguments onto each element of the list. A trailing space
432 on 'prefix' or leading space on 'suffix' will cause them to be put
433 into separate list elements rather than being concatenated.
436 result = []
438 # ensure that prefix and suffix are strings
439 prefix = str(env.subst(prefix, SCons.Subst.SUBST_RAW))
440 suffix = str(env.subst(suffix, SCons.Subst.SUBST_RAW))
442 for x in flatten(items_iter):
443 if isinstance(x, SCons.Node.FS.File):
444 result.append(x)
445 continue
446 x = str(x)
447 if x:
449 if prefix:
450 if prefix[-1] == ' ':
451 result.append(prefix[:-1])
452 elif x[:len(prefix)] != prefix:
453 x = prefix + x
455 result.append(x)
457 if suffix:
458 if suffix[0] == ' ':
459 result.append(suffix[1:])
460 elif x[-len(suffix):] != suffix:
461 result[-1] = result[-1] + suffix
463 return result
466 def _stripixes(
467 prefix: str,
468 items,
469 suffix: str,
470 stripprefixes: List[str],
471 stripsuffixes: List[str],
472 env,
473 literal_prefix: str = "",
474 c: Callable[[list], list] = None,
475 ) -> list:
476 """Returns a list with text added to items after first stripping them.
478 A companion to :func:`_concat_ixes`, used by tools (like the GNU
479 linker) that need to turn something like ``libfoo.a`` into ``-lfoo``.
480 *stripprefixes* and *stripsuffixes* are stripped from *items*.
481 Calls function *c* to postprocess the result.
483 Args:
484 prefix: string to prepend to elements
485 items: string or iterable to transform
486 suffix: string to append to elements
487 stripprefixes: prefix string(s) to strip from elements
488 stripsuffixes: suffix string(s) to strip from elements
489 env: construction environment for variable interpolation
490 c: optional function to perform a transformation on the list.
491 The default is `None`, which will select :func:`_concat_ixes`.
493 if not items:
494 return items
496 if not callable(c):
497 env_c = env['_concat']
498 if env_c != _concat and callable(env_c):
499 # There's a custom _concat() method in the construction
500 # environment, and we've allowed people to set that in
501 # the past (see test/custom-concat.py), so preserve the
502 # backwards compatibility.
503 c = env_c
504 else:
505 c = _concat_ixes
507 stripprefixes = list(map(env.subst, flatten(stripprefixes)))
508 stripsuffixes = list(map(env.subst, flatten(stripsuffixes)))
510 # This is a little funky: if literal_prefix is the same as os.pathsep
511 # (e.g. both ':'), the normal conversion to a PathList will drop the
512 # literal_prefix prefix. Tell it not to split in that case, which *should*
513 # be okay because if we come through here, we're normally processing
514 # library names and won't have strings like "path:secondpath:thirdpath"
515 # which is why PathList() otherwise wants to split strings.
516 do_split = not literal_prefix == os.pathsep
518 stripped = []
519 for l in SCons.PathList.PathList(items, do_split).subst_path(env, None, None):
520 if isinstance(l, SCons.Node.FS.File):
521 stripped.append(l)
522 continue
524 if not is_String(l):
525 l = str(l)
527 if literal_prefix and l.startswith(literal_prefix):
528 stripped.append(l)
529 continue
531 for stripprefix in stripprefixes:
532 lsp = len(stripprefix)
533 if l[:lsp] == stripprefix:
534 l = l[lsp:]
535 # Do not strip more than one prefix
536 break
538 for stripsuffix in stripsuffixes:
539 lss = len(stripsuffix)
540 if l[-lss:] == stripsuffix:
541 l = l[:-lss]
542 # Do not strip more than one suffix
543 break
545 stripped.append(l)
547 return c(prefix, stripped, suffix, env)
550 def processDefines(defs) -> List[str]:
551 """Return list of strings for preprocessor defines from *defs*.
553 Resolves the different forms ``CPPDEFINES`` can be assembled in:
554 if the Append/Prepend routines are used beyond a initial setting it
555 will be a deque, but if written to only once (Environment initializer,
556 or direct write) it can be a multitude of types.
558 Any prefix/suffix is handled elsewhere (usually :func:`_concat_ixes`).
560 .. versionchanged:: 4.5.0
561 Bare tuples are now treated the same as tuple-in-sequence, assumed
562 to describe a valued macro. Bare strings are now split on space.
563 A dictionary is no longer sorted before handling.
565 dlist = []
566 if is_List(defs):
567 for define in defs:
568 if define is None:
569 continue
570 elif is_Sequence(define):
571 if len(define) > 2:
572 raise SCons.Errors.UserError(
573 f"Invalid tuple in CPPDEFINES: {define!r}, "
574 "must be a tuple with only two elements"
576 name, *value = define
577 if value and value[0] is not None:
578 # TODO: do we need to quote value if it contains space?
579 dlist.append(f"{name}={value[0]}")
580 else:
581 dlist.append(str(define[0]))
582 elif is_Dict(define):
583 for macro, value in define.items():
584 if value is not None:
585 # TODO: do we need to quote value if it contains space?
586 dlist.append(f"{macro}={value}")
587 else:
588 dlist.append(str(macro))
589 elif is_String(define):
590 dlist.append(str(define))
591 else:
592 raise SCons.Errors.UserError(
593 f"CPPDEFINES entry {define!r} is not a tuple, list, "
594 "dict, string or None."
596 elif is_Tuple(defs):
597 if len(defs) > 2:
598 raise SCons.Errors.UserError(
599 f"Invalid tuple in CPPDEFINES: {defs!r}, "
600 "must be a tuple with only two elements"
602 name, *value = defs
603 if value and value[0] is not None:
604 # TODO: do we need to quote value if it contains space?
605 dlist.append(f"{name}={value[0]}")
606 else:
607 dlist.append(str(define[0]))
608 elif is_Dict(defs):
609 for macro, value in defs.items():
610 if value is None:
611 dlist.append(str(macro))
612 else:
613 dlist.append(f"{macro}={value}")
614 elif is_String(defs):
615 return defs.split()
616 else:
617 dlist.append(str(defs))
619 return dlist
622 def _defines(prefix, defs, suffix, env, target=None, source=None, c=_concat_ixes):
623 """A wrapper around :func:`_concat_ixes` that turns a list or string
624 into a list of C preprocessor command-line definitions.
626 return c(prefix, env.subst_list(processDefines(defs), target=target, source=source), suffix, env)
629 class NullCmdGenerator:
630 """Callable class for use as a no-effect command generator.
632 The ``__call__`` method for this class simply returns the thing
633 you instantiated it with. Example usage::
635 env["DO_NOTHING"] = NullCmdGenerator
636 env["LINKCOM"] = "${DO_NOTHING('$LINK $SOURCES $TARGET')}"
639 def __init__(self, cmd) -> None:
640 self.cmd = cmd
642 def __call__(self, target, source, env, for_signature=None):
643 return self.cmd
646 class Variable_Method_Caller:
647 """A class for finding a construction variable on the stack and
648 calling one of its methods.
650 Used to support "construction variables" appearing in string
651 ``eval``s that actually stand in for methods--specifically, the use
652 of "RDirs" in a call to :func:`_concat` that should actually execute the
653 ``TARGET.RDirs`` method.
655 Historical note: This was formerly supported by creating a little
656 "build dictionary" that mapped RDirs to the method, but this got
657 in the way of Memoizing construction environments, because we had to
658 create new environment objects to hold the variables.
661 def __init__(self, variable, method) -> None:
662 self.variable = variable
663 self.method = method
665 def __call__(self, *args, **kw):
666 try:
667 1 // 0
668 except ZeroDivisionError:
669 # Don't start iterating with the current stack-frame to
670 # prevent creating reference cycles (f_back is safe).
671 frame = sys.exc_info()[2].tb_frame.f_back
672 variable = self.variable
673 while frame:
674 if variable in frame.f_locals:
675 v = frame.f_locals[variable]
676 if v:
677 method = getattr(v, self.method)
678 return method(*args, **kw)
679 frame = frame.f_back
680 return None
683 def __libversionflags(env, version_var, flags_var):
685 if version_var is not empty, returns env[flags_var], otherwise returns None
686 :param env:
687 :param version_var:
688 :param flags_var:
689 :return:
691 try:
692 if env.subst('$' + version_var):
693 return env[flags_var]
694 except KeyError:
695 pass
696 return None
699 def __lib_either_version_flag(env, version_var1, version_var2, flags_var):
701 if $version_var1 or $version_var2 is not empty, returns env[flags_var], otherwise returns None
702 :param env:
703 :param version_var1:
704 :param version_var2:
705 :param flags_var:
706 :return:
708 try:
709 if env.subst('$' + version_var1) or env.subst('$' + version_var2):
710 return env[flags_var]
711 except KeyError:
712 pass
713 return None
719 ConstructionEnvironment = {
720 'BUILDERS': {},
721 'SCANNERS': [SCons.Tool.SourceFileScanner],
722 'CONFIGUREDIR': '#/.sconf_temp',
723 'CONFIGURELOG': '#/config.log',
724 'CPPSUFFIXES': SCons.Tool.CSuffixes,
725 'DSUFFIXES': SCons.Tool.DSuffixes,
726 'ENV': {},
727 'IDLSUFFIXES': SCons.Tool.IDLSuffixes,
728 '_concat': _concat,
729 '_defines': _defines,
730 '_stripixes': _stripixes,
731 '_LIBFLAGS': '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}',
733 '_LIBDIRFLAGS': '${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE, affect_signature=False)}',
734 '_CPPINCFLAGS': '${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE, affect_signature=False)}',
736 '_CPPDEFFLAGS': '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__, TARGET, SOURCE)}',
738 '__libversionflags': __libversionflags,
739 '__SHLIBVERSIONFLAGS': '${__libversionflags(__env__,"SHLIBVERSION","_SHLIBVERSIONFLAGS")}',
740 '__LDMODULEVERSIONFLAGS': '${__libversionflags(__env__,"LDMODULEVERSION","_LDMODULEVERSIONFLAGS")}',
741 '__DSHLIBVERSIONFLAGS': '${__libversionflags(__env__,"DSHLIBVERSION","_DSHLIBVERSIONFLAGS")}',
742 '__lib_either_version_flag': __lib_either_version_flag,
744 'TEMPFILE': NullCmdGenerator,
745 'TEMPFILEARGJOIN': ' ',
746 'TEMPFILEARGESCFUNC': SCons.Subst.quote_spaces,
747 'Dir': Variable_Method_Caller('TARGET', 'Dir'),
748 'Dirs': Variable_Method_Caller('TARGET', 'Dirs'),
749 'File': Variable_Method_Caller('TARGET', 'File'),
750 'RDirs': Variable_Method_Caller('TARGET', 'RDirs'),
753 # Local Variables:
754 # tab-width:4
755 # indent-tabs-mode:nil
756 # End:
757 # vim: set expandtab tabstop=4 shiftwidth=4: