Commits

Anonymous committed 5c1c2fd

Make more Environment methods from global functions.

  • Participants
  • Parent commits e747971

Comments (0)

Files changed (12)

File doc/man/scons.1

 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 .TP
 .RI BuildDir( build_dir ", " src_dir ", [" duplicate ])
+.TP
+.RI env.BuildDir( build_dir ", " src_dir ", [" duplicate ])
 This specifies a build directory
 .I build_dir
 in which to build all derived files
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 .TP 
 .RI CacheDir( cache_dir )
+.TP 
+.RI env.CacheDir( cache_dir )
 Specifies that
 .B scons
 will maintain a cache of derived files in
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 .TP
 .RI Dir( name ", [" directory ])
+.TP
+.RI env.Dir( name ", [" directory ])
 This returns an object that represents a given directory 
 .IR name . 
 .I name
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 .TP 
 .RI File( name ", [" directory ])
+.TP 
+.RI env.File( name ", [" directory ])
 This returns an object that represents a given file 
 .IR name . 
 .I name
 .EE
 
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
-.\".TP
-.\".RI GetBuildPath( XXX )
-.\"XXX
+.TP
+.RI GetBuildPath( XXX )
+.TP
+.RI env.GetBuildPath( XXX )
+XXX
 .\"
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 .\".TP
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 .TP
 .RI Repository( directory )
+.TP
+.RI env.Repository( directory )
 Specifies that
 .I directory
 is a repository to be searched for files.
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 .TP
 .RI SConsignFile([ file ])
+.TP
+.RI env.SConsignFile([ file ])
 This tells
 .B scons
 to store all file signatures

File src/CHANGES.txt

   - Support arbitrary expansion of construction variables within
     file and directory arguments to Builder calls and Environment methods.
 
-  - Add Environment-method versions of the following global function:
-    AddPreAction(), AddPostAction(), Clean(), Default(), FindFile(),
-    Local(), SourceSignatures(), TargetSignatures().
+  - Add Environment-method versions of the following global functions:
+    AddPreAction(), AddPostAction(), BuildDir(), CacheDir(), Clean(),
+    Default(), FindFile(), GetBuildPath(), Local(), Repository(),
+    SConsignFile(), SourceSignatures(), TargetSignatures().
 
   - Add the following global functions that correspond to the same-named
     Environment methods:  AlwaysBuild(), Command(), Depends(), Ignore(),

File src/engine/SCons/Environment.py

             tlist = tlist[0]
         return tlist
 
+    def BuildDir(self, build_dir, src_dir, duplicate=1):
+        build_dir = self.arg2nodes(build_dir, self.fs.Dir)[0]
+        src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0]
+        self.fs.BuildDir(build_dir, src_dir, duplicate)
+
+    def CacheDir(self, path):
+        self.fs.CacheDir(self.subst(path))
+
     def Clean(self, target, files):
         global CleanTargets
 
             tlist = tlist[0]
         return tlist
 
+    def Dir(self, name, *args, **kw):
+        """
+        """
+        return apply(self.fs.Dir, (self.subst(name),) + args, kw)
+
+    def File(self, name, *args, **kw):
+        """
+        """
+        return apply(self.fs.File, (self.subst(name),) + args, kw)
+
     def FindFile(self, file, dirs):
         file = self.subst(file)
         nodes = self.arg2nodes(dirs, self.fs.Dir)
         return SCons.Node.FS.find_file(file, nodes, self.fs.File)
 
+    def GetBuildPath(self, files):
+        ret = map(str, self.arg2nodes(files, self.fs.Entry))
+        if len(ret) == 1:
+            return ret[0]
+        return ret
+
     def Ignore(self, target, dependency):
         """Ignore a dependency."""
         tlist = self.arg2nodes(target, self.fs.File)
             tlist = tlist[0]
         return tlist
 
+    def Repository(self, *dirs, **kw):
+        dirs = self.arg2nodes(list(dirs), self.fs.Dir)
+        apply(self.fs.Repository, dirs, kw)
+
+    def SConsignFile(self, name=".sconsign.dbm"):
+        name = self.subst(name)
+        if not os.path.isabs(name):
+            name = os.path.join(str(self.fs.SConstruct_dir), name)
+        SCons.Sig.SConsignFile(name)
+
     def SideEffect(self, side_effect, target):
         """Tell scons that side_effects are built as side 
         effects of building targets."""

File src/engine/SCons/EnvironmentTests.py

         assert t[4].path == 'bbb'
         assert t[4].always_build
 
+    def test_BuildDir(self):
+        """Test the BuildDir() method"""
+        class MyFS:
+             def Dir(self, name):
+                 return name
+             def BuildDir(self, build_dir, src_dir, duplicate):
+                 self.build_dir = build_dir
+                 self.src_dir = src_dir
+                 self.duplicate = duplicate
+
+        env = Environment(FOO = 'fff', BAR = 'bbb')
+        env.fs = MyFS()
+
+        env.BuildDir('build', 'src')
+        assert env.fs.build_dir == 'build', env.fs.build_dir
+        assert env.fs.src_dir == 'src', env.fs.src_dir
+        assert env.fs.duplicate == 1, env.fs.duplicate
+
+        env.BuildDir('build${FOO}', '${BAR}src', 0)
+        assert env.fs.build_dir == 'buildfff', env.fs.build_dir
+        assert env.fs.src_dir == 'bbbsrc', env.fs.src_dir
+        assert env.fs.duplicate == 0, env.fs.duplicate
+
+    def test_CacheDir(self):
+        """Test the CacheDir() method"""
+        class MyFS:
+            def CacheDir(self, path):
+                self.CD = path
+
+        env = Environment(CD = 'CacheDir')
+        env.fs = MyFS()
+
+        env.CacheDir('foo')
+        assert env.fs.CD == 'foo', env.fs.CD
+
+        env.CacheDir('$CD')
+        assert env.fs.CD == 'CacheDir', env.fs.CD
+
     def test_Clean(self):
         """Test the Clean() method"""
         env = Environment(FOO = 'fff', BAR = 'bbb')
 	assert d.__class__.__name__ == 'File'
 	assert d.path == 'yyy.py'
 
+    def test_Dir(self):
+        """Test the Dir() method"""
+        class MyFS:
+            def Dir(self, name):
+                return 'Dir(%s)' % name
+
+        env = Environment(FOO = 'foodir', BAR = 'bardir')
+        env.fs = MyFS()
+
+        d = env.Dir('d')
+        assert d == 'Dir(d)', d
+
+        d = env.Dir('$FOO')
+        assert d == 'Dir(foodir)', d
+
+        d = env.Dir('${BAR}_$BAR')
+        assert d == 'Dir(bardir_bardir)', d
+
+    def test_File(self):
+        """Test the File() method"""
+        class MyFS:
+            def File(self, name):
+                return 'File(%s)' % name
+
+        env = Environment(FOO = 'foofile', BAR = 'barfile')
+        env.fs = MyFS()
+
+        f = env.File('f')
+        assert f == 'File(f)', f
+
+        f = env.File('$FOO')
+        assert f == 'File(foofile)', f
+
+        f = env.File('${BAR}_$BAR')
+        assert f == 'File(barfile_barfile)', f
+
     def test_FindFile(self):
         """Test the FindFile() method"""
         env = Environment(FOO = 'fff', BAR = 'bbb')
 
         # XXX
 
+    def test_GetBuildPath(self):
+        """Test the GetBuildPath() method."""
+        env = Environment(MAGIC = 'xyzzy')
+
+        p = env.GetBuildPath('foo')
+        assert p == 'foo', p
+
+        p = env.GetBuildPath('$MAGIC')
+        assert p == 'xyzzy', p
+
     def test_Ignore(self):
         """Test the explicit Ignore method."""
         env = Environment(FOO='yyy', BAR='zzz')
         assert t[4].path == 'ggg'
         assert t[4].precious
 
+    def test_Repository(self):
+        """Test the Repository() method."""
+        class MyFS:
+            def __init__(self):
+                self.list = []
+            def Repository(self, *dirs):
+                self.list.extend(dirs)
+            def Dir(self, name):
+                return name
+        env = Environment(FOO='rrr', BAR='sss')
+        env.fs = MyFS()
+        env.Repository('/tmp/foo')
+        env.Repository('/tmp/$FOO', '/tmp/$BAR/foo')
+        expect = ['/tmp/foo', '/tmp/rrr', '/tmp/sss/foo']
+        assert env.fs.list == expect, env.fs.list
+
+    def test_SConsignFile(self):
+        """Test the SConsignFile() method"""
+        import SCons.Sig
+
+        class MyFS:
+            SConstruct_dir = '/dir'
+
+        env = Environment(FOO = 'SConsign',
+                          BAR = os.path.join(os.sep, 'File'))
+        env.fs = MyFS()
+
+        try:
+            save = []
+            def capture(name, save=save):
+                save.append(name)
+
+            save_Sig_SConsignFile = SCons.Sig.SConsignFile
+            SCons.Sig.SConsignFile = capture
+
+            env.SConsignFile('foo')
+            assert save[0] == os.path.join(os.sep, 'dir', 'foo'), save
+
+            env.SConsignFile('$FOO')
+            assert save[1] == os.path.join(os.sep, 'dir', 'SConsign'), save
+
+            env.SConsignFile('/$FOO')
+            assert save[2] == '/SConsign', save
+
+            env.SConsignFile('$BAR')
+            assert save[3] == os.path.join(os.sep, 'File'), save
+
+            env.SConsignFile('__$BAR')
+            assert save[4] == os.path.join(os.sep, 'dir', '__', 'File'), save
+        finally:
+            SCons.Sig.SConsignFile = save_Sig_SConsignFile
+
     def test_SideEffect(self):
         """Test the SideEffect() method"""
         env = Environment(LIB='lll', FOO='fff', BAR='bbb')

File src/engine/SCons/Script/SConscript.py

 
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
+import SCons
 import SCons.Action
 import SCons.Builder
 import SCons.Defaults
 import SCons.Environment
 import SCons.Errors
 import SCons.Node
+import SCons.Node.Alias
 import SCons.Node.FS
 import SCons.Node.Python
 import SCons.Platform
 import SCons.Script
 import SCons.Util
 import SCons.Options
-import SCons
-import SCons.Node.Alias
 
 import os
 import os.path
 import string
 import sys
 import traceback
+import types
 
 def do_nothing(text): pass
 HelpFunction = do_nothing
             else:
                 # Fast way to only get the terminal path component of a Node.
                 fname = fn.get_path(fn.dir)
-        BuildDir(build_dir, src_dir, duplicate)
+        SCons.Node.FS.default_fs.BuildDir(build_dir, src_dir, duplicate)
         files = [os.path.join(str(build_dir), fname)]
 
     return (files, exports)
 def Help(text):
     HelpFunction(text)
 
-def BuildDir(build_dir, src_dir, duplicate=1):
-    SCons.Node.FS.default_fs.BuildDir(build_dir, src_dir, duplicate)
-
-def GetBuildPath(files):
-    nodes = SCons.Node.arg2nodes(files, SCons.Node.FS.default_fs.Entry)
-    ret = map(str, nodes)
-    if len(ret) == 1:
-        return ret[0]
-    return ret
-
 def Export(*vars):
     for var in vars:
         global_exports.update(compute_exports(var))
 def GetOption(name):
     return SCons.Script.ssoptions.get(name)
 
-def SConsignFile(name=".sconsign.dbm"):
-    import SCons.Sig
-    if not os.path.isabs(name):
-        sd = str(SCons.Node.FS.default_fs.SConstruct_dir)
-        name = os.path.join(sd, name)
-    SCons.Sig.SConsignFile(name)
+#
+_DefaultEnvironmentProxy = None
+
+def get_DefaultEnvironmentProxy():
+    global _DefaultEnvironmentProxy
+    if not _DefaultEnvironmentProxy:
+        class EnvironmentProxy(SCons.Environment.Environment):
+            """A proxy subclass for an environment instance that overrides
+            the subst() and subst_list() methods so they don't actually
+            actually perform construction variable substitution.  This is
+            specifically intended to be the shim layer in between global
+            function calls (which don't want want construction variable
+            substitution) and the DefaultEnvironment() (which would
+            substitute variables if left to its own devices)."""
+            def __init__(self, subject):
+                self.__dict__['__subject'] = subject
+            def __getattr__(self, name):
+                return getattr(self.__dict__['__subject'], name)
+            def __setattr__(self, name, value):
+                return setattr(self.__dict__['__subject'], name, value)
+            def subst(self, string, raw=0, target=None, source=None):
+                return string
+            def subst_list(self, string, raw=0, target=None, source=None):
+                return string
+        default_env = SCons.Defaults.DefaultEnvironment()
+        _DefaultEnvironmentProxy = EnvironmentProxy(default_env)
+    return _DefaultEnvironmentProxy
 
 def BuildDefaultGlobals():
     """
     globals['Action']            = SCons.Action.Action
     globals['Alias']             = Alias
     globals['ARGUMENTS']         = arguments
-    globals['BuildDir']          = BuildDir
     globals['Builder']           = SCons.Builder.Builder
-    globals['CacheDir']          = SCons.Node.FS.default_fs.CacheDir
     globals['Configure']         = SCons.SConf.SConf
     globals['CScan']             = SCons.Defaults.CScan
     globals['DefaultEnvironment'] = SCons.Defaults.DefaultEnvironment
-    globals['Dir']               = SCons.Node.FS.default_fs.Dir
     globals['EnsurePythonVersion'] = EnsurePythonVersion
     globals['EnsureSConsVersion'] = EnsureSConsVersion
     globals['Environment']       = SCons.Environment.Environment
     globals['Exit']              = Exit
     globals['Export']            = Export
-    globals['File']              = SCons.Node.FS.default_fs.File
-    globals['GetBuildPath']      = GetBuildPath
     globals['GetCommandHandler'] = SCons.Action.GetCommandHandler
     globals['GetJobs']           = GetJobs
     globals['GetLaunchDir']      = GetLaunchDir
     globals['Options']           = Options
     globals['ParseConfig']       = SCons.Util.ParseConfig
     globals['Platform']          = SCons.Platform.Platform
-    globals['Repository']        = SCons.Node.FS.default_fs.Repository
     globals['Return']            = Return
     globals['SConscript']        = SConscript
     globals['SConscriptChdir']   = SConscriptChdir
-    globals['SConsignFile']      = SConsignFile
     globals['Scanner']           = SCons.Scanner.Base
     globals['SetBuildSignatureType'] = SetBuildSignatureType
     globals['SetCommandHandler'] = SCons.Action.SetCommandHandler
     globals['WhereIs']           = SCons.Util.WhereIs
 
     class DefaultEnvironmentCall:
-        """ """
+        """A class that implements "global function" calls of
+        Environment methods by fetching the specified method from the
+        DefaultEnvironment's class.  Note that this uses an intermediate
+        proxy class instead of calling the DefaultEnvironment method
+        directly so that the proxy can override the subst() method and
+        thereby prevent expansion of construction variables (since from
+        the user's point of view this was called as a global function,
+        with no associated construction environment)."""
         def __init__(self, method_name):
             self.method_name = method_name
         def __call__(self, *args, **kw):
-            method = getattr(SCons.Defaults.DefaultEnvironment(),
-                             self.method_name)
-            return apply(method, args, kw)
+            proxy = get_DefaultEnvironmentProxy()
+            method = getattr(proxy.__class__, self.method_name)
+            return apply(method, (proxy,) + args, kw)
 
     EnvironmentMethods = [
         'AddPostAction',
         'AddPreAction',
         'AlwaysBuild',
+        'BuildDir',
+        'CacheDir',
         'Clean',
         'Command',
         'Default',
         'Depends',
+        'Dir',
+        'File',
         'FindFile',
+        'GetBuildPath',
         'Ignore',
         'Install',
         'InstallAs',
         'Local',
         'Precious',
+        'Repository',
+        'SConsignFile',
         'SideEffect',
         'SourceCode',
         'SourceSignatures',

File test/BuildDir.py

 var5 = Dir('../build/var5')
 var6 = Dir('../build/var6')
 
+env = Environment(BUILD = 'build', SRC = 'src')
 
 BuildDir('build/var1', src)
 BuildDir(var2, src)
 BuildDir(var3, src, duplicate=0)
-BuildDir(var4, src, duplicate=0)
+env.BuildDir("$BUILD/var4", "$SRC", duplicate=0)
 BuildDir(var5, src, duplicate=0)
 BuildDir(var6, src)
 

File test/CacheDir.py

 
 #
 test.write('SConstruct', """\
-CacheDir(r'%s')
+env = Environment(TWO = '2')
+env.CacheDir(r'%s')
 BuildDir('build', 'src', duplicate=0)
 SConscript('build/SConscript')
-""" % test.workpath('cache2'))
+""" % test.workpath('cache${TWO}'))
 
 # Verify that a normal build works correctly, and clean up.
 # This should populate the cache with our derived files.
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# 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__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Verify that the Dir() global function and environment method work
+correctly, and that the former does not try to expand construction
+variables.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """
+env = Environment(FOO = 'fff', BAR = 'bbb')
+print Dir('ddd')
+print Dir('$FOO')
+print Dir('${BAR}_$BAR')
+print env.Dir('eee')
+print env.Dir('$FOO')
+print env.Dir('${BAR}_$BAR')
+""")
+
+test.run(stdout = test.wrap_stdout(read_str = """\
+ddd
+$FOO
+${BAR}_$BAR
+eee
+fff
+bbb_bbb
+""", build_str = """\
+scons: `.' is up to date.
+"""))
+
+test.pass_test()

File test/File.py

+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# 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__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Verify that the File() global function and environment method work
+correctly, and that the former does not try to expand construction
+variables.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """
+env = Environment(FOO = 'fff', BAR = 'bbb')
+print File('ddd')
+print File('$FOO')
+print File('${BAR}_$BAR')
+print env.File('eee')
+print env.File('$FOO')
+print env.File('${BAR}_$BAR')
+""")
+
+test.run(stdout = test.wrap_stdout(read_str = """\
+ddd
+$FOO
+${BAR}_$BAR
+eee
+fff
+bbb_bbb
+""", build_str = """\
+scons: `.' is up to date.
+"""))
+
+test.pass_test()

File test/Repository/SConscript.py

 
 #
 test.write(['rep2', 'build', 'SConstruct'], """
-Repository(r'%s')
+env = Environment(REPOSITORY = r'%s')
+env.Repository('$REPOSITORY')
 SConscript('src/SConscript')
 """ % workpath_rep2)
 

File test/SConsignFile.py

 
 #
 test.write(['work2', 'SConstruct'], """
-SConsignFile('my_sconsign')
+e = Environment(XXX = 'scons')
+e.SConsignFile('my_${XXX}ign')
 B = Builder(action = "%s ../build.py $TARGETS $SOURCES")
 env = Environment(BUILDERS = { 'B' : B })
 env.B(target = 'f5.out', source = 'f5.in')

File test/option--C.py

 wpath = test.workpath()
 wpath_sub = test.workpath('sub')
 wpath_sub_dir = test.workpath('sub', 'dir')
+wpath_sub_foo_bar = test.workpath('sub', 'foo', 'bar')
 
 test.subdir('sub', ['sub', 'dir'])
 
 
 test.write(['sub', 'dir', 'SConstruct'], """
 import os
-print GetBuildPath('..')
+env = Environment(FOO='foo', BAR='bar')
+print env.GetBuildPath('../$FOO/$BAR')
 """)
 
 test.run(arguments = '-C sub .',
 	                           build_str = "scons: `.' is up to date.\n"))
 
 test.run(arguments = '-C sub -C dir .',
-	 stdout = test.wrap_stdout(read_str = '%s\n' % wpath_sub,
+	 stdout = test.wrap_stdout(read_str = '%s\n' % wpath_sub_foo_bar,
 	                           build_str = "scons: `.' is up to date.\n"))
 
 test.run(arguments = ".",
 	                           build_str = "scons: `.' is up to date.\n"))
 
 test.run(arguments = '--directory=sub/dir .',
-	 stdout = test.wrap_stdout(read_str = '%s\n' % wpath_sub,
+	 stdout = test.wrap_stdout(read_str = '%s\n' % wpath_sub_foo_bar,
 	                           build_str = "scons: `.' is up to date.\n"))
 
 test.run(arguments = '-C %s -C %s .' % (wpath_sub_dir, wpath_sub),
 	                           build_str = "scons: `.' is up to date.\n"))
 
 test.pass_test()
-