Commits

Steven Knight  committed dbc9dd4

Add a script to print .sconsign file contents.

  • Participants
  • Parent commits 12fdec3

Comments (0)

Files changed (21)

File HOWTO/new-script.txt

+Steps for adding a new script/utility to the SCons packages.  Assume
+that you're creating a new man page, too.  In the template steps
+below, "newscript" is the name of the new script/utility being added.
+
+    New files:
+
+        doc/man/newscript.1
+
+            New man page; use an existing one as a template.
+
+        src/script/newscript.py
+
+            The new script itself.
+
+        test/*.py
+
+            Appropriate tests for the new script.
+
+    Modified files:
+
+        SConstruct
+
+            In the "scons_script" package description:
+
+                Add "newscript" : "newscript.py" to the "filemap"
+                dictionary.
+
+            In the "scons" package description:
+
+                Add "newscript.1" to the "files" list.
+
+                Add "newscript.1" : "../doc/man/newscript.1" to the
+                "filemap" dictionary.
+
+            Add "newscript" to the scripts[] list
+
+        debian/rules
+
+            Add:
+
+                rm -f debian/scons/usr/bin/newscript
+                cp build/scripts/newscript debian/scons/user/bin/newscript
+                sed '1s|.*|#!/usr/bin/python2.1|' build/scripts/newscript > debian/scons/user/bin/newscript
+                chmod +x debian/scons/user/bin/newscript
+
+                rm -f debian/scons/usr/share/man/man1/newscript.1
+                cp newscript.1 debian/scons/user/share/man/man1/
+
+        doc/SConscript
+
+            Add "newscript" to man_page_list[]
+
+        gentoo/scons.ebuild.in
+
+            Add "doman newscript.1"
+
+        rpm/scons.spec.in
+
+            Add "gzip -c newscript.1" > $RPM_BUILD_ROOT/usr/man/man1/newscript.1.gz
+
+            Add "%doc /usr/man/man1/newscript.1.gz"
+
+        src/CHANGES.txt
+
+            Document the addition of the new script.
+
+        src/script/MANIFEST.in
+
+            Add a line:  "newscript"
+
+        src/setup.py
+
+            Add "script/newscript" to the 'scripts' list.
         'filemap'       : {
                             'LICENSE.txt' : '../LICENSE.txt',
                             'scons'       : 'scons.py',
+                            'sconsign'    : 'sconsign.py',
                            }
 }
 
                             'RELEASE.txt',
                             'os_spawnv_fix.diff',
                             'scons.1',
+                            'sconsign.1',
                             'script/scons.bat',
                             'setup.cfg',
                             'setup.py',
 
         'filemap'       : {
                             'scons.1' : '../doc/man/scons.1',
+                            'sconsign.1' : '../doc/man/sconsign.1',
                           },
 
         'subpkgs'	: [ python_scons, scons_script ],
                            },
 }
 
+scripts = ['scons', 'sconsign']
+
 src_deps = []
 src_files = []
 
         "rm -rf %s" % local,
         "$PYTHON $SETUP_PY install --install-script=%s --install-lib=%s --no-compile" % \
                                                 (cwd_local, cwd_local_slv),
-        "mv %s/scons %s/scons.py" % (local, local),
     ]
 
-    rf = filter(lambda x: x != "scons", raw_files)
+    for script in scripts:
+        commands.append("mv %s/%s %s/%s.py" % (local, script, local, script))
+
+    rf = filter(lambda x: not x in scripts, raw_files)
     rf = map(lambda x, slv=s_l_v: os.path.join(slv, x), rf)
-    rf.append("scons.py")
+    for script in scripts:
+        rf.append("%s.py" % script)
     local_targets = map(lambda x, s=local: os.path.join(s, x), rf)
 
     env.Command(local_targets, build_src_files, commands)

File debian/rules

 
 	mkdir -p debian/scons/usr/bin/
 	rm -f debian/scons/usr/bin/scons
+	rm -f debian/scons/usr/bin/sconsign
 ifeq ($(PYTHON),python)
 	cp build/scripts/scons debian/scons/usr/bin/scons
+	cp build/scripts/sconsign debian/scons/usr/bin/sconsign
 else
 	sed '1s|.*|#!/usr/bin/python2.1|' build/scripts/scons > debian/scons/usr/bin/scons
+	sed '1s|.*|#!/usr/bin/python2.1|' build/scripts/sconsign > debian/scons/usr/bin/sconsign
 endif
 	chmod +x debian/scons/usr/bin/scons
+	chmod +x debian/scons/usr/bin/sconsign
 
 	mkdir -p debian/scons/usr/share/man/man1/
 	rm -f debian/scons/usr/share/man/man1/scons.1
+	rm -f debian/scons/usr/share/man/man1/sconsign.1
 	cp scons.1 debian/scons/usr/share/man/man1/
+	cp sconsign.1 debian/scons/usr/share/man/man1/
 
 	mkdir -p debian/scons/usr/share/doc/scons
 	rm -f debian/scons/usr/share/doc/scons/changelog

File doc/SConscript

 
         if docs[doc].get('text') and jade and lynx:
             env.Command(text, html, "lynx -dump ${SOURCE.abspath} > $TARGET")
+            Local(text)
 
             env.Ignore(text, "version.sgml")
 
 #
 # Man page(s), in good ol' troff format.
 #
-scons_1 = os.path.join('man', 'scons.1')
+man_page_list = ['scons', 'sconsign']
 
-if groff:
-    ps = os.path.join('PS', 'scons-man.ps')
-    text = os.path.join('TEXT', 'scons-man.txt')
+for man in man_page_list:
+    man_1 = os.path.join('man', '%s.1' % man)
 
-    env.Command(ps, scons_1, "groff -man -Tps $SOURCES > $TARGET")
+    if groff:
+        ps = os.path.join('PS', '%s-man.ps' % man)
+        text = os.path.join('TEXT', '%s-man.txt' % man)
 
-    env.Command(text, scons_1, "groff -man -Tascii $SOURCES > $TARGET")
+        env.Command(ps, man_1, "groff -man -Tps $SOURCES > $TARGET")
+        Local(ps)
 
-    tar_deps.extend([ps, text])
-    tar_list = string.join([tar_list, ps, text], " ")
+        env.Command(text, man_1, "groff -man -Tascii $SOURCES > $TARGET")
+        Local(text)
 
-if man2html:
-    html = os.path.join('HTML' , 'scons-man.html')
+        tar_deps.extend([ps, text])
+        tar_list = string.join([tar_list, ps, text], " ")
 
-    cmds = [ "man2html $SOURCES > $TARGET" ]
-    if tidy:
-        cmds.append("tidy -m -q $TARGET || true")
-    env.Command(html, scons_1, cmds)
+    if man2html:
+        html = os.path.join('HTML' , '%s-man.html' % man)
 
-    tar_deps.append(html)
-    tar_list = tar_list + " " + html
+        cmds = [ "man2html $SOURCES > $TARGET" ]
+        if tidy:
+            cmds.append("tidy -m -q $TARGET || true")
+        env.Command(html, man_1, cmds)
+        Local(html)
+
+        tar_deps.append(html)
+        tar_list = tar_list + " " + html
 
 #
 # Now actually create the tar file of the documentation,
 if tar_deps:
     env.Command(doc_tar_gz, tar_deps,
                 "tar cf${TAR_HFLAG} - -C build/doc %s | gzip > $TARGET" % tar_list)
+    Local(doc_tar_gz)

File doc/man/sconsign.1

+.\" Copyright (c) 2001, 2002, 2003 Steven Knight
+.\"
+.\" Permission is hereby granted, free of charge, to any person obtaining
+.\" a copy of this software and associated documentation files (the
+.\" "Software"), to deal in the Software without restriction, including
+.\" without limitation the rights to use, copy, modify, merge, publish,
+.\" distribute, sublicense, and/or sell copies of the Software, and to
+.\" permit persons to whom the Software is furnished to do so, subject to
+.\" the following conditions:
+.\"
+.\" The above copyright notice and this permission notice shall be included
+.\" in all copies or substantial portions of the Software.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+.\" KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+.\" WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+.\" NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+.\" LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+.\" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+.\" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+.\"
+.\" doc/man/scons.1 0.90.D006 2003/07/02 09:02:19 knight
+.\"
+.\" ES - Example Start - indents and turns off line fill
+.de ES
+.RS
+.nf
+..
+.\" EE - Example End - ends indent and turns line fill back on
+.de EE
+.RE
+.fi
+..
+.TH SCONSIGN 1 "July 2003"
+.SH NAME
+sconsign \- print SCons .sconsign file information
+.SH SYNOPSIS
+.B sconsign
+[
+.IR options ...
+]
+.IR file
+[ ... ]
+.SH DESCRIPTION
+
+The 
+.B sconsign
+command
+displays the contents of one or more
+.B .sconsign
+files specified by the user.
+
+By default,
+.B sconsign
+dumps the entire contents of the
+specified file(s).
+Each entry is printed in the following format:
+
+    file: timestamp bsig csig
+            implicit_dependency_1
+            implicit_dependency_2
+
+If the entry has no timestamp, bsig, or csig, a dash
+(\fR\-\fP) is printed instead.
+If the entry has no implicit dependencies,
+the lines are simply omitted.
+
+.SH OPTIONS
+
+Various options control what information is printed
+and the format:
+
+.TP
+-b
+Ignored for compatibility with non-GNU versions of
+.BR make.
+
+.TP
+-b, --bsig
+Prints the build signature (bsig) information
+for all entries or the specified entries.
+
+.TP
+-c, --csig
+Prints the content signature (csig) information
+for all entries or the specified entries.
+
+.TP
+-e entry, --entry=entry
+Prints information about only the specified entry.
+Multiple -e options may be used,
+in which case information about each
+entry is printed in the order in which the
+options are specified on the command line.
+
+.TP
+-h, --help
+Prints a help message and exits.
+
+.TP
+-i, --implicit
+Prints the list of cached implicit dependencies
+for all entries or the the specified entries.
+
+.TP
+-t, --timestamp
+Prints the timestamp information
+for all entries or the specified entries.
+
+.TP
+-v, --verbose
+Prints labels identifying each field being printed.
+
+.SH ENVIRONMENT
+
+.IP SCONS_LIB_DIR
+Specifies the directory that contains the SCons Python module directory
+(e.g. /home/aroach/scons-src-0.01/src/engine).
+on the command line.
+
+.SH "SEE ALSO"
+.BR scons ,
+.B scons
+User Manual,
+.B scons
+Design Document,
+.B scons
+source code.
+
+.SH AUTHORS
+Steven Knight <knight at baldmt dot com>

File gentoo/scons.ebuild.in

         python setup.py install --root=${D}
         dodoc *.txt PKG-INFO MANIFEST
         doman scons.1
+        doman sconsign.1
 }

File rpm/scons.spec.in

 python setup.py install --root=$RPM_BUILD_ROOT --record=INSTALLED_FILES
 mkdir -p $RPM_BUILD_ROOT/usr/man/man1
 gzip -c scons.1 > $RPM_BUILD_ROOT/usr/man/man1/scons.1.gz
+gzip -c sconsign.1 > $RPM_BUILD_ROOT/usr/man/man1/sconsign.1.gz
 
 %clean
 rm -rf $RPM_BUILD_ROOT
 %defattr(-,root,root)
 __RPM_FILES__
 %doc /usr/man/man1/scons.1.gz
+%doc /usr/man/man1/sconsign.1.gz

File src/CHANGES.txt

     from CVS with a specified module name.  This avoids zero-length
     files when there is a checkout error.
 
+  - Add an "sconsign" script to print the contents of .sconsign files.
+
   From Gary Oberbrunner:
 
   - Report the target being built in error messages when building
   - Provide helpful error messages when the arguments to env.Install()
     are incorrect.
 
+  From Christoph Wiedemann
+
+  - Have the g++ Tool actually use g++ in preference to c++.
+
+  - Have the gcc Tool actually use gcc in preference to cc.
+
+  - Add a gnutools.py test of the GNU tool chain.
+
+  - Be smarter about linking: use $CC by default and $CXX only if we're
+    linking with any C++ objects.
+
 
 
 RELEASE 0.90 - Wed, 25 Jun 2003 14:24:52 -0500

File src/engine/SCons/SConfTests.py

         #    - cygwin on win32 (using cmd.exe, not bash)
         #    - posix
         #    - msvc on win32 (hopefully)
-        if self.scons_env.subst('$CXX') == 'c++':
-            # better use g++ (which is normally no symbolic link
-            # --> the c++ call fails on cygwin
-            self.scons_env['CXX'] = 'g++'
-        if self.scons_env.subst('$LINK') == 'c++':
-            self.scons_env['LINK'] = 'g++'
         if (not self.scons_env.Detect( self.scons_env.subst('$CXX') ) or
             not self.scons_env.Detect( self.scons_env.subst('$CC') ) or
             not self.scons_env.Detect( self.scons_env.subst('$LINK') )):
             raise Exception, "This test needs an installed compiler!"
-        if self.scons_env['LINK'] == 'g++':
+        if self.scons_env['CXX'] == 'g++':
             global existing_lib
             existing_lib = 'm'
         

File src/engine/SCons/Sig/SigTests.py

         assert e.bsig == None
         assert e.implicit == None
 
+class _SConsignTestCase(unittest.TestCase):
+
+    def runTest(self):
+        class DummyModule:
+            def to_string(self, sig):
+                return str(sig)
+
+            def from_string(self, sig):
+                return int(sig)
+            
+        class DummyNode:
+            path = 'not_a_valid_path'
+
+        f = SCons.Sig._SConsign()
+        f.set_bsig('foo', 1)
+        assert f.get('foo') == (None, 1, None)
+        f.set_csig('foo', 2)
+        assert f.get('foo') == (None, 1, 2)
+        f.set_timestamp('foo', 3)
+        assert f.get('foo') == (3, 1, 2)
+        f.set_implicit('foo', ['bar'])
+        assert f.get('foo') == (3, 1, 2)
+        assert f.get_implicit('foo') == ['bar']
+
+        f = SCons.Sig._SConsign(None, DummyModule())
+        f.set_bsig('foo', 1)
+        assert f.get('foo') == (None, 1, None)
+        f.set_csig('foo', 2)
+        assert f.get('foo') == (None, 1, 2)
+        f.set_timestamp('foo', 3)
+        assert f.get('foo') == (3, 1, 2)
+        f.set_implicit('foo', ['bar'])
+        assert f.get('foo') == (3, 1, 2)
+        assert f.get_implicit('foo') == ['bar']
+
 class SConsignFileTestCase(unittest.TestCase):
 
     def runTest(self):
     suite.addTest(TimeStampTestCase())
     suite.addTest(CalcTestCase())
     suite.addTest(SConsignEntryTestCase())
+    suite.addTest(_SConsignTestCase())
     suite.addTest(SConsignFileTestCase())
     return suite
 

File src/engine/SCons/Sig/__init__.py

     csig = None
     implicit = None
 
-class SConsignFile:
-    """
-    Encapsulates reading and writing a .sconsign file.
-    """
+class _SConsign:
 
-    def __init__(self, dir, module=None):
+    def __init__(self, fp=None, module=None):
         """
-        dir - the directory for the file
+        fp - file pointer to read entries from
         module - the signature module being used
         """
 
-        self.dir = dir
-
         if module is None:
             self.module = default_calc.module
         else:
             self.module = module
-        self.sconsign = os.path.join(dir.path, '.sconsign')
         self.entries = {}
         self.dirty = 0
 
-        try:
-            file = open(self.sconsign, 'rb')
-        except:
-            pass
-        else:
+        if fp:
             try:
-                self.entries = cPickle.load(file)
+                self.entries = cPickle.load(fp)
                 if type(self.entries) is not type({}):
                     self.entries = {}
                     raise TypeError
         entry.implicit = implicit
         self.set_entry(filename, entry)
 
+class SConsignFile(_SConsign):
+    """
+    Encapsulates reading and writing a .sconsign file.
+    """
+
+    def __init__(self, dir, module=None):
+        """
+        dir - the directory for the file
+        module - the signature module being used
+        """
+
+        self.dir = dir
+        self.sconsign = os.path.join(dir.path, '.sconsign')
+
+        try:
+            fp = open(self.sconsign, 'rb')
+        except:
+            fp = None
+
+        _SConsign.__init__(self, fp, module)
+
     def write(self):
         """
         Write the .sconsign file to disk.

File src/engine/SCons/Tool/g++.py

 
 import os.path
 
+import cc
 import SCons.Defaults
 import SCons.Tool
 import SCons.Util
 
-compilers = ['c++', 'g++']
-
-CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++']
-if os.path.normcase('.c') != os.path.normcase('.C'):
-    CXXSuffixes.append('.C')
+compilers = ['g++', 'c++']
 
 def generate(env):
     """Add Builders and construction variables for g++ to an Environment."""
     static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
 
-    for suffix in CXXSuffixes:
+    for suffix in cc.CXXSuffixes:
         static_obj.add_action(suffix, SCons.Defaults.CXXAction)
         shared_obj.add_action(suffix, SCons.Defaults.ShCXXAction)
 
-    env['CXX']        = env.Detect(compilers) or 'c++'
+    env['CXX']        = env.Detect(compilers) or 'g++'
     env['CXXFLAGS']   = '$CCFLAGS'
     env['CXXCOM']     = '$CXX $CXXFLAGS $CPPFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES'
     env['SHCXX']      = '$CXX'

File src/engine/SCons/Tool/gcc.py

 
 import cc
 
-compilers = ['cc', 'gcc']
+compilers = ['gcc', 'cc']
 
 def generate(env):
     """Add Builders and construction variables for gcc to an Environment."""
     cc.generate(env)
 
-    env['CC'] = env.Detect(compilers) or 'cc'
+    env['CC'] = env.Detect(compilers) or 'gcc'
     if env['PLATFORM'] == 'cygwin':
         env['SHCCFLAGS'] = '$CCFLAGS'
     else:

File src/engine/SCons/Tool/gnulink.py

 
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
+import cc
 import link
+import os.path
+import SCons.Action
+import SCons.Builder
+import SCons.Errors
+import SCons.Util
 
-linkers = ['c++', 'cc', 'g++', 'gcc']
+linkers = ['g++', 'gcc', 'c++', 'cc']
 
+def cxxSource(sources):
+    for s in sources:
+        if os.path.splitext(str(s))[1] in cc.CXXSuffixes:
+            return 1
+        if cxxSource(s.sources):
+            return 1
+    return 0
+
+def smart_link(source, target, env, for_signature):
+    cppSource = 0
+    if source is None:
+        # may occur, when env.subst('$LINK') is called
+        return '$CXX'
+    if not SCons.Util.is_List(source):
+        source = [source]
+        
+    if cxxSource(source):
+        return '$CXX'
+    else:
+        return '$CC'
+        
 def generate(env):
     """Add Builders and construction variables for gnulink to an Environment."""
     link.generate(env)
-
+    env['LINK'] = smart_link
+    
 def exists(env):
     return env.Detect(linkers)

File src/script/MANIFEST.in

 scons
+sconsign

File src/script/scons.py

 
 __developer__ = "__DEVELOPER__"
 
+import os
+import os.path
 import sys
-import os.path
-import os
+
+##############################################################################
+# BEGIN STANDARD SCons SCRIPT HEADER
+#
+# This is the cut-and-paste logic so that a self-contained script can
+# interoperate correctly with different SCons versions and installation
+# locations for the engine.  If you modify anything in this section, you
+# should also change other scripts that use this same header.
+##############################################################################
 
 # Strip the script directory from sys.path() so on case-insensitive
 # (WIN32) systems Python doesn't think that the "scons" script is the
 
 sys.path = libs + sys.path
 
+##############################################################################
+# END STANDARD SCons SCRIPT HEADER
+##############################################################################
+
 import SCons.Script
 SCons.Script.main()

File src/script/sconsign.py

+#! /usr/bin/env python
+#
+# SCons - a Software Constructor
+#
+# Copyright (c) 2001, 2002, 2003 Steven Knight
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__REVISION__"
+
+__version__ = "__VERSION__"
+
+__build__ = "__BUILD__"
+
+__buildsys__ = "__BUILDSYS__"
+
+__date__ = "__DATE__"
+
+__developer__ = "__DEVELOPER__"
+
+import os
+import os.path
+import sys
+
+##############################################################################
+# BEGIN STANDARD SCons SCRIPT HEADER
+#
+# This is the cut-and-paste logic so that a self-contained script can
+# interoperate correctly with different SCons versions and installation
+# locations for the engine.  If you modify anything in this section, you
+# should also change other scripts that use this same header.
+##############################################################################
+
+# Strip the script directory from sys.path() so on case-insensitive
+# (WIN32) systems Python doesn't think that the "scons" script is the
+# "SCons" package.  Replace it with our own library directories
+# (version-specific first, in case they installed by hand there,
+# followed by generic) so we pick up the right version of the build
+# engine modules if they're in either directory.
+
+script_dir = sys.path[0]
+
+if script_dir in sys.path:
+    sys.path.remove(script_dir)
+
+libs = []
+
+if os.environ.has_key("SCONS_LIB_DIR"):
+    libs.append(os.environ["SCONS_LIB_DIR"])
+
+local = 'scons-local-' + __version__
+if script_dir:
+    local = os.path.join(script_dir, local)
+libs.append(local)
+
+scons_version = 'scons-%s' % __version__
+
+prefs = []
+
+if sys.platform == 'win32':
+    # sys.prefix is (likely) C:\Python*;
+    # check only C:\Python*.
+    prefs.append(sys.prefix)
+else:
+    # On other (POSIX) platforms, things are more complicated due to
+    # the variety of path names and library locations.  Try to be smart
+    # about it.
+    if script_dir == 'bin':
+        # script_dir is `pwd`/bin;
+        # check `pwd`/lib/scons*.
+        prefs.append(os.getcwd())
+    else:
+        if script_dir == '.' or script_dir == '':
+            script_dir = os.getcwd()
+        head, tail = os.path.split(script_dir)
+        if tail == "bin":
+            # script_dir is /foo/bin;
+            # check /foo/lib/scons*.
+            prefs.append(head)
+
+    head, tail = os.path.split(sys.prefix)
+    if tail == "usr":
+        # sys.prefix is /foo/usr;
+        # check /foo/usr/lib/scons* first,
+        # then /foo/usr/local/lib/scons*.
+        prefs.append(sys.prefix)
+        prefs.append(os.path.join(sys.prefix, "local"))
+    elif tail == "local":
+        h, t = os.path.split(head)
+        if t == "usr":
+            # sys.prefix is /foo/usr/local;
+            # check /foo/usr/local/lib/scons* first,
+            # then /foo/usr/lib/scons*.
+            prefs.append(sys.prefix)
+            prefs.append(head)
+        else:
+            # sys.prefix is /foo/local;
+            # check only /foo/local/lib/scons*.
+            prefs.append(sys.prefix)
+    else:
+        # sys.prefix is /foo (ends in neither /usr or /local);
+        # check only /foo/lib/scons*.
+        prefs.append(sys.prefix)
+
+    prefs = map(lambda x: os.path.join(x, 'lib'), prefs)
+
+# Look first for 'scons-__version__' in all of our preference libs,
+# then for 'scons'.
+libs.extend(map(lambda x: os.path.join(x, scons_version), prefs))
+libs.extend(map(lambda x: os.path.join(x, 'scons'), prefs))
+
+sys.path = libs + sys.path
+
+##############################################################################
+# END STANDARD SCons SCRIPT HEADER
+##############################################################################
+
+import getopt
+
+helpstr = """\
+Usage: sconsign [OPTIONS] FILE [...]
+Options:
+  -b, --bsig                  Print build signature information.
+  -c, --csig                  Print content signature information.
+  -e, --entry ENTRY           Print only info about ENTRY.
+  -h, --help                  Print this message and exit.
+  -i, --implicit              Print implicit dependency information.
+  -t, --timestamp             Print timestamp information.
+  -v, --verbose               Verbose, describe each field.
+"""
+
+opts, args = getopt.getopt(sys.argv[1:], "bce:hitv",
+                            ['bsig', 'csig', 'entry=', 'help', 'implicit',
+                             'timestamp', 'verbose'])
+
+pf_bsig      = 0x1
+pf_csig      = 0x2
+pf_timestamp = 0x4
+pf_implicit  = 0x8
+pf_all       = pf_bsig | pf_csig | pf_timestamp | pf_implicit
+
+entries = []
+printflags = 0
+verbose = 0
+
+for o, a in opts:
+    if o in ('-b', '--bsig'):
+        printflags = printflags | pf_bsig
+    elif o in ('-c', '--csig'):
+        printflags = printflags | pf_csig
+    elif o in ('-e', '--entry'):
+        entries.append(a)
+    elif o in ('-h', o == '--help'):
+        print helpstr
+        sys.exit(0)
+    elif o in ('-i', '--implicit'):
+        printflags = printflags | pf_implicit
+    elif o in ('-t', '--timestamp'):
+        printflags = printflags | pf_timestamp
+    elif o in ('-v', '--verbose'):
+        verbose = 1
+
+if printflags == 0:
+    printflags = pf_all
+
+def field(name, pf, val):
+    if printflags & pf:
+        if verbose:
+            sep = "\n    " + name + ": "
+        else:
+            sep = " "
+        return sep + (val or '-')
+    else:
+        return ""
+
+def printfield(name, entry):
+    timestamp = field("timestamp", pf_timestamp, entry.timestamp)
+    bsig = field("bsig", pf_bsig, entry.bsig)
+    csig = field("csig", pf_csig, entry.csig)
+    print name + ":" + timestamp + bsig + csig
+    if printflags & pf_implicit and entry.implicit:
+        if verbose:
+            print "    implicit:"
+        for i in entry.implicit:
+            print "        %s" % i
+
+import SCons.Sig
+
+def do_sconsign(fp):
+    sconsign = SCons.Sig._SConsign(fp)
+    if entries:
+        for name in entries:
+            try:
+                entry = sconsign.entries[name]
+            except KeyError:
+                sys.stderr.write("sconsign: no entry `%s' in `%s'\n" % (name, args[0]))
+            else:
+                printfield(name, entry)
+    else:
+        for name, e in sconsign.entries.items():
+            printfield(name, e)
+    
+    
+for a in args:
+    do_sconsign(open(a, 'rb'))

File src/setup.py

                           "SCons.Sig",
                           "SCons.Tool"],
     'package_dir'      : {'' : 'engine'},
-    'scripts'          : ["script/scons"],
+    'scripts'          : ['script/scons', 'script/sconsign'],
     'cmdclass'         : {'install'         : install,
                           'install_lib'     : install_lib,
                           'install_scripts' : install_scripts}

File test/LINK.py

 
 test.write('SConstruct', """
 foo = Environment()
-link = foo.Dictionary('LINK')
+link = foo.subst("$LINK")
 bar = Environment(LINK = r'%s wrapper.py ' + link)
 foo.Program(target = 'foo', source = 'foo.c')
 bar.Program(target = 'bar', source = 'bar.c')

File test/LINKFLAGS.py

 
 test.write('SConstruct', """
 foo = Environment()
-link = foo.Dictionary('LINK')
+link = foo.subst("$LINK")
 bar = Environment(LINK = '', LINKFLAGS = r'%s wrapper.py ' + link)
 foo.Program(target = 'foo', source = 'foo.c')
 bar.Program(target = 'bar', source = 'bar.c')

File test/sconsign-script.py

+#!/usr/bin/env python
+#
+# Copyright (c) 2001, 2002, 2003 Steven Knight
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "/home/scons/scons/branch.0/baseline/test/sconsign.py 0.90.D001 2003/06/25 15:32:24 knight"
+
+import os.path
+import string
+
+import TestCmd
+import TestSCons
+
+test = TestSCons.TestSCons(match = TestCmd.match_re)
+
+test.subdir('sub1', 'sub2')
+
+test.write('SConstruct', """
+env1 = Environment(PROGSUFFIX = '.exe', OBJSUFFIX = '.obj')
+env1.Program('sub1/hello.c')
+env2 = env1.Copy(CPPPATH = ['sub2'])
+env2.Program('sub2/hello.c')
+""")
+
+test.write(['sub1', 'hello.c'], r"""\
+int
+main(int argc, char *argv[])
+{
+	argv[argc++] = "--";
+	printf("sub1/hello.c\n");
+	exit (0);
+}
+""")
+
+test.write(['sub2', 'hello.c'], r"""\
+#include <inc1.h>
+#include <inc2.h>
+int
+main(int argc, char *argv[])
+{
+	argv[argc++] = "--";
+	printf("sub2/goodbye.c\n");
+	exit (0);
+}
+""")
+
+test.write(['sub2', 'inc1.h'], r"""\
+#define STRING1 "inc1.h"
+""")
+
+test.write(['sub2', 'inc2.h'], r"""\
+#define STRING2 "inc2.h"
+""")
+
+test.run(arguments = '--implicit-cache .')
+
+test.run(interpreter = TestSCons.python,
+         program = "sconsign",
+         arguments = "sub1/.sconsign",
+         stdout = """\
+hello.exe: - \S+ -
+hello.obj: - \S+ -
+""")
+
+test.run(interpreter = TestSCons.python,
+         program = "sconsign",
+         arguments = "-v sub1/.sconsign",
+         stdout = """\
+hello.exe:
+    timestamp: -
+    bsig: \S+
+    csig: -
+hello.obj:
+    timestamp: -
+    bsig: \S+
+    csig: -
+""")
+
+test.run(interpreter = TestSCons.python,
+         program = "sconsign",
+         arguments = "-b -v sub1/.sconsign",
+         stdout = """\
+hello.exe:
+    bsig: \S+
+hello.obj:
+    bsig: \S+
+""")
+
+test.run(interpreter = TestSCons.python,
+         program = "sconsign",
+         arguments = "-c -v sub1/.sconsign",
+         stdout = """\
+hello.exe:
+    csig: -
+hello.obj:
+    csig: -
+""")
+
+test.run(interpreter = TestSCons.python,
+         program = "sconsign",
+         arguments = "-e hello.obj sub1/.sconsign",
+         stdout = """\
+hello.obj: - \S+ -
+""")
+
+test.run(interpreter = TestSCons.python,
+         program = "sconsign",
+         arguments = "-e hello.obj -e hello.exe -e hello.obj sub1/.sconsign",
+         stdout = """\
+hello.obj: - \S+ -
+hello.exe: - \S+ -
+hello.obj: - \S+ -
+""")
+
+test.run(interpreter = TestSCons.python,
+         program = "sconsign",
+         arguments = "sub2/.sconsign",
+         stdout = """\
+hello.exe: - \S+ -
+hello.obj: - \S+ -
+        %s
+        %s
+""" % (string.replace(os.path.join('sub2', 'inc1.h'), '\\', '\\\\'),
+       string.replace(os.path.join('sub2', 'inc2.h'), '\\', '\\\\')))
+
+test.run(interpreter = TestSCons.python,
+         program = "sconsign",
+         arguments = "-i -v sub2/.sconsign",
+         stdout = """\
+hello.exe:
+hello.obj:
+    implicit:
+        %s
+        %s
+""" % (string.replace(os.path.join('sub2', 'inc1.h'), '\\', '\\\\'),
+       string.replace(os.path.join('sub2', 'inc2.h'), '\\', '\\\\')))
+
+test.pass_test()
+
+test.run(interpreter = TestSCons.python,
+         program = "sconsign",
+         arguments = "-e hello.obj sub2/.sconsign sub1/.sconsign",
+         stdout = """\
+hello.obj: - \S+ -
+        %s
+        %s
+hello.obj: - \S+ -
+""" % (string.replace(os.path.join('sub2', 'inc1.h'), '\\', '\\\\'),
+       string.replace(os.path.join('sub2', 'inc2.h'), '\\', '\\\\')))
+
+test.pass_test()