fixed a few typos and changed all references to c++ project file to be MSVC (entity...
[scons.git] / SCons / Defaults.py
blob32e9da3f251192ead7d805ae11df6a357688e071
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 os.symlink(os.readlink(src), dest)
295 return 0
297 return copy_func(dest, os.path.realpath(src))
299 elif os.path.isfile(src):
300 shutil.copy2(src, dest)
301 return 0
303 else:
304 shutil.copytree(src, dest, symlinks)
305 return 0
308 def copy_strfunc(dest, src, symlinks: bool=True) -> str:
309 """strfunction for the Copy action function."""
310 return f'Copy({get_paths_str(dest)}, {get_paths_str(src)})'
313 Copy = ActionFactory(copy_func, copy_strfunc)
316 def delete_func(dest, must_exist: bool=False) -> None:
317 """Implementation of the Delete action function.
319 Lets the Python :func:`os.unlink` raise an error if *dest* does not exist,
320 unless *must_exist* evaluates false (the default).
322 SCons.Node.FS.invalidate_node_memos(dest)
323 if not is_List(dest):
324 dest = [dest]
325 for entry in dest:
326 entry = str(entry)
327 # os.path.exists returns False with broken links that exist
328 entry_exists = os.path.exists(entry) or os.path.islink(entry)
329 if not entry_exists and not must_exist:
330 continue
331 # os.path.isdir returns True when entry is a link to a dir
332 if os.path.isdir(entry) and not os.path.islink(entry):
333 shutil.rmtree(entry, True)
334 continue
335 os.unlink(entry)
338 def delete_strfunc(dest, must_exist: bool=False) -> str:
339 """strfunction for the Delete action function."""
340 return f'Delete({get_paths_str(dest)})'
343 Delete = ActionFactory(delete_func, delete_strfunc)
346 def mkdir_func(dest) -> None:
347 """Implementation of the Mkdir action function."""
348 SCons.Node.FS.invalidate_node_memos(dest)
349 if not is_List(dest):
350 dest = [dest]
351 for entry in dest:
352 os.makedirs(str(entry), exist_ok=True)
355 Mkdir = ActionFactory(mkdir_func, lambda _dir: f'Mkdir({get_paths_str(_dir)})')
358 def move_func(dest, src) -> None:
359 """Implementation of the Move action function."""
360 SCons.Node.FS.invalidate_node_memos(dest)
361 SCons.Node.FS.invalidate_node_memos(src)
362 shutil.move(src, dest)
365 Move = ActionFactory(
366 move_func, lambda dest, src: f'Move("{dest}", "{src}")', convert=str
370 def touch_func(dest) -> None:
371 """Implementation of the Touch action function."""
372 SCons.Node.FS.invalidate_node_memos(dest)
373 if not is_List(dest):
374 dest = [dest]
375 for file in dest:
376 file = str(file)
377 mtime = int(time.time())
378 if os.path.exists(file):
379 atime = os.path.getatime(file)
380 else:
381 with open(file, 'w'):
382 atime = mtime
383 os.utime(file, (atime, mtime))
386 Touch = ActionFactory(touch_func, lambda file: f'Touch({get_paths_str(file)})')
389 # Internal utility functions
391 # pylint: disable-msg=too-many-arguments
392 def _concat(prefix, items_iter, suffix, env, f=lambda x: x, target=None, source=None, affect_signature: bool=True):
394 Creates a new list from 'items_iter' by first interpolating each element
395 in the list using the 'env' dictionary and then calling f on the
396 list, and finally calling _concat_ixes to concatenate 'prefix' and
397 'suffix' onto each element of the list.
400 if not items_iter:
401 return items_iter
403 l = f(SCons.PathList.PathList(items_iter).subst_path(env, target, source))
404 if l is not None:
405 items_iter = l
407 if not affect_signature:
408 value = ['$(']
409 else:
410 value = []
411 value += _concat_ixes(prefix, items_iter, suffix, env)
413 if not affect_signature:
414 value += ["$)"]
416 return value
417 # pylint: enable-msg=too-many-arguments
420 def _concat_ixes(prefix, items_iter, suffix, env):
422 Creates a new list from 'items_iter' by concatenating the 'prefix' and
423 'suffix' arguments onto each element of the list. A trailing space
424 on 'prefix' or leading space on 'suffix' will cause them to be put
425 into separate list elements rather than being concatenated.
428 result = []
430 # ensure that prefix and suffix are strings
431 prefix = str(env.subst(prefix, SCons.Subst.SUBST_RAW))
432 suffix = str(env.subst(suffix, SCons.Subst.SUBST_RAW))
434 for x in flatten(items_iter):
435 if isinstance(x, SCons.Node.FS.File):
436 result.append(x)
437 continue
438 x = str(x)
439 if x:
441 if prefix:
442 if prefix[-1] == ' ':
443 result.append(prefix[:-1])
444 elif x[:len(prefix)] != prefix:
445 x = prefix + x
447 result.append(x)
449 if suffix:
450 if suffix[0] == ' ':
451 result.append(suffix[1:])
452 elif x[-len(suffix):] != suffix:
453 result[-1] = result[-1] + suffix
455 return result
458 def _stripixes(
459 prefix: str,
460 items,
461 suffix: str,
462 stripprefixes: List[str],
463 stripsuffixes: List[str],
464 env,
465 literal_prefix: str = "",
466 c: Callable[[list], list] = None,
467 ) -> list:
468 """Returns a list with text added to items after first stripping them.
470 A companion to :func:`_concat_ixes`, used by tools (like the GNU
471 linker) that need to turn something like ``libfoo.a`` into ``-lfoo``.
472 *stripprefixes* and *stripsuffixes* are stripped from *items*.
473 Calls function *c* to postprocess the result.
475 Args:
476 prefix: string to prepend to elements
477 items: string or iterable to transform
478 suffix: string to append to elements
479 stripprefixes: prefix string(s) to strip from elements
480 stripsuffixes: suffix string(s) to strip from elements
481 env: construction environment for variable interpolation
482 c: optional function to perform a transformation on the list.
483 The default is `None`, which will select :func:`_concat_ixes`.
485 if not items:
486 return items
488 if not callable(c):
489 env_c = env['_concat']
490 if env_c != _concat and callable(env_c):
491 # There's a custom _concat() method in the construction
492 # environment, and we've allowed people to set that in
493 # the past (see test/custom-concat.py), so preserve the
494 # backwards compatibility.
495 c = env_c
496 else:
497 c = _concat_ixes
499 stripprefixes = list(map(env.subst, flatten(stripprefixes)))
500 stripsuffixes = list(map(env.subst, flatten(stripsuffixes)))
502 # This is a little funky: if literal_prefix is the same as os.pathsep
503 # (e.g. both ':'), the normal conversion to a PathList will drop the
504 # literal_prefix prefix. Tell it not to split in that case, which *should*
505 # be okay because if we come through here, we're normally processing
506 # library names and won't have strings like "path:secondpath:thirdpath"
507 # which is why PathList() otherwise wants to split strings.
508 do_split = not literal_prefix == os.pathsep
510 stripped = []
511 for l in SCons.PathList.PathList(items, do_split).subst_path(env, None, None):
512 if isinstance(l, SCons.Node.FS.File):
513 stripped.append(l)
514 continue
516 if not is_String(l):
517 l = str(l)
519 if literal_prefix and l.startswith(literal_prefix):
520 stripped.append(l)
521 continue
523 for stripprefix in stripprefixes:
524 lsp = len(stripprefix)
525 if l[:lsp] == stripprefix:
526 l = l[lsp:]
527 # Do not strip more than one prefix
528 break
530 for stripsuffix in stripsuffixes:
531 lss = len(stripsuffix)
532 if l[-lss:] == stripsuffix:
533 l = l[:-lss]
534 # Do not strip more than one suffix
535 break
537 stripped.append(l)
539 return c(prefix, stripped, suffix, env)
542 def processDefines(defs) -> List[str]:
543 """Return list of strings for preprocessor defines from *defs*.
545 Resolves the different forms ``CPPDEFINES`` can be assembled in:
546 if the Append/Prepend routines are used beyond a initial setting it
547 will be a deque, but if written to only once (Environment initializer,
548 or direct write) it can be a multitude of types.
550 Any prefix/suffix is handled elsewhere (usually :func:`_concat_ixes`).
552 .. versionchanged:: 4.5.0
553 Bare tuples are now treated the same as tuple-in-sequence, assumed
554 to describe a valued macro. Bare strings are now split on space.
555 A dictionary is no longer sorted before handling.
557 dlist = []
558 if is_List(defs):
559 for define in defs:
560 if define is None:
561 continue
562 elif is_Sequence(define):
563 if len(define) > 2:
564 raise SCons.Errors.UserError(
565 f"Invalid tuple in CPPDEFINES: {define!r}, "
566 "must be a tuple with only two elements"
568 name, *value = define
569 if value and value[0] is not None:
570 # TODO: do we need to quote value if it contains space?
571 dlist.append(f"{name}={value[0]}")
572 else:
573 dlist.append(str(define[0]))
574 elif is_Dict(define):
575 for macro, value in define.items():
576 if value is not None:
577 # TODO: do we need to quote value if it contains space?
578 dlist.append(f"{macro}={value}")
579 else:
580 dlist.append(str(macro))
581 elif is_String(define):
582 dlist.append(str(define))
583 else:
584 raise SCons.Errors.UserError(
585 f"CPPDEFINES entry {define!r} is not a tuple, list, "
586 "dict, string or None."
588 elif is_Tuple(defs):
589 if len(defs) > 2:
590 raise SCons.Errors.UserError(
591 f"Invalid tuple in CPPDEFINES: {defs!r}, "
592 "must be a tuple with only two elements"
594 name, *value = defs
595 if value and value[0] is not None:
596 # TODO: do we need to quote value if it contains space?
597 dlist.append(f"{name}={value[0]}")
598 else:
599 dlist.append(str(define[0]))
600 elif is_Dict(defs):
601 for macro, value in defs.items():
602 if value is None:
603 dlist.append(str(macro))
604 else:
605 dlist.append(f"{macro}={value}")
606 elif is_String(defs):
607 return defs.split()
608 else:
609 dlist.append(str(defs))
611 return dlist
614 def _defines(prefix, defs, suffix, env, target=None, source=None, c=_concat_ixes):
615 """A wrapper around :func:`_concat_ixes` that turns a list or string
616 into a list of C preprocessor command-line definitions.
618 return c(prefix, env.subst_list(processDefines(defs), target=target, source=source), suffix, env)
621 class NullCmdGenerator:
622 """Callable class for use as a no-effect command generator.
624 The ``__call__`` method for this class simply returns the thing
625 you instantiated it with. Example usage::
627 env["DO_NOTHING"] = NullCmdGenerator
628 env["LINKCOM"] = "${DO_NOTHING('$LINK $SOURCES $TARGET')}"
631 def __init__(self, cmd) -> None:
632 self.cmd = cmd
634 def __call__(self, target, source, env, for_signature=None):
635 return self.cmd
638 class Variable_Method_Caller:
639 """A class for finding a construction variable on the stack and
640 calling one of its methods.
642 Used to support "construction variables" appearing in string
643 ``eval``s that actually stand in for methods--specifically, the use
644 of "RDirs" in a call to :func:`_concat` that should actually execute the
645 ``TARGET.RDirs`` method.
647 Historical note: This was formerly supported by creating a little
648 "build dictionary" that mapped RDirs to the method, but this got
649 in the way of Memoizing construction environments, because we had to
650 create new environment objects to hold the variables.
653 def __init__(self, variable, method) -> None:
654 self.variable = variable
655 self.method = method
657 def __call__(self, *args, **kw):
658 try:
659 1 // 0
660 except ZeroDivisionError:
661 # Don't start iterating with the current stack-frame to
662 # prevent creating reference cycles (f_back is safe).
663 frame = sys.exc_info()[2].tb_frame.f_back
664 variable = self.variable
665 while frame:
666 if variable in frame.f_locals:
667 v = frame.f_locals[variable]
668 if v:
669 method = getattr(v, self.method)
670 return method(*args, **kw)
671 frame = frame.f_back
672 return None
675 def __libversionflags(env, version_var, flags_var):
677 if version_var is not empty, returns env[flags_var], otherwise returns None
678 :param env:
679 :param version_var:
680 :param flags_var:
681 :return:
683 try:
684 if env.subst('$' + version_var):
685 return env[flags_var]
686 except KeyError:
687 pass
688 return None
691 def __lib_either_version_flag(env, version_var1, version_var2, flags_var):
693 if $version_var1 or $version_var2 is not empty, returns env[flags_var], otherwise returns None
694 :param env:
695 :param version_var1:
696 :param version_var2:
697 :param flags_var:
698 :return:
700 try:
701 if env.subst('$' + version_var1) or env.subst('$' + version_var2):
702 return env[flags_var]
703 except KeyError:
704 pass
705 return None
711 ConstructionEnvironment = {
712 'BUILDERS': {},
713 'SCANNERS': [SCons.Tool.SourceFileScanner],
714 'CONFIGUREDIR': '#/.sconf_temp',
715 'CONFIGURELOG': '#/config.log',
716 'CPPSUFFIXES': SCons.Tool.CSuffixes,
717 'DSUFFIXES': SCons.Tool.DSuffixes,
718 'ENV': {},
719 'IDLSUFFIXES': SCons.Tool.IDLSuffixes,
720 '_concat': _concat,
721 '_defines': _defines,
722 '_stripixes': _stripixes,
723 '_LIBFLAGS': '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}',
725 '_LIBDIRFLAGS': '${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE, affect_signature=False)}',
726 '_CPPINCFLAGS': '${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE, affect_signature=False)}',
728 '_CPPDEFFLAGS': '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__, TARGET, SOURCE)}',
730 '__libversionflags': __libversionflags,
731 '__SHLIBVERSIONFLAGS': '${__libversionflags(__env__,"SHLIBVERSION","_SHLIBVERSIONFLAGS")}',
732 '__LDMODULEVERSIONFLAGS': '${__libversionflags(__env__,"LDMODULEVERSION","_LDMODULEVERSIONFLAGS")}',
733 '__DSHLIBVERSIONFLAGS': '${__libversionflags(__env__,"DSHLIBVERSION","_DSHLIBVERSIONFLAGS")}',
734 '__lib_either_version_flag': __lib_either_version_flag,
736 'TEMPFILE': NullCmdGenerator,
737 'TEMPFILEARGJOIN': ' ',
738 'TEMPFILEARGESCFUNC': SCons.Subst.quote_spaces,
739 'Dir': Variable_Method_Caller('TARGET', 'Dir'),
740 'Dirs': Variable_Method_Caller('TARGET', 'Dirs'),
741 'File': Variable_Method_Caller('TARGET', 'File'),
742 'RDirs': Variable_Method_Caller('TARGET', 'RDirs'),
745 # Local Variables:
746 # tab-width:4
747 # indent-tabs-mode:nil
748 # End:
749 # vim: set expandtab tabstop=4 shiftwidth=4: